Compare commits

..

No commits in common. "main" and "1.3.0" have entirely different histories.
main ... 1.3.0

22 changed files with 150 additions and 658 deletions

1
.github/funding.yml vendored

@ -1 +1,2 @@
github: paulmillr
# custom: https://paulmillr.com/funding/

@ -1,23 +1,20 @@
name: Run node.js tests
on:
- push
- pull_request
name: Node CI
on: [push, pull_request]
jobs:
test:
name: v${{ matrix.node }} @ ubuntu-latest
runs-on: ubuntu-latest
strategy:
matrix:
node:
- 18
- 20
node: [18, 20]
steps:
- uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npm run build --if-present
- run: npm test
- run: npm run lint --if-present
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npm run build --if-present
- run: npm test
- run: npm run lint --if-present

@ -1,23 +1,23 @@
name: Publish package to npm
name: Publish Package to npm
on:
release:
types: [created]
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
cache: npm
- run: npm install -g npm
- run: npm ci
- run: npm run build
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
cache: npm
- run: npm install -g npm
- run: npm ci
- run: npm run build
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

@ -1,28 +0,0 @@
name: Upload standalone file to GitHub Releases
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
cache: npm
- run: npm install -g npm
- run: npm ci
- run: npm run build
- run: |
cd build
npm ci
npm run build:release
cd ..
- run: gh release upload ${{ github.event.release.tag_name }} build/`npx jsbt outfile`
env:
GH_TOKEN: ${{ github.token }}

18
.gitignore vendored

@ -1,9 +1,13 @@
node_modules
build/
node_modules/
coverage/
/*.js
/*.ts
/*.js.map
/*.d.ts.map
/esm/*.js
*.d.ts
*.d.ts.map
*.js.map
/build
/abstract
/esm/abstract
/esm/*.ts
/esm/*.js.map
/esm/*.d.ts.map
/esm/abstract
/abstract/

@ -7,18 +7,14 @@ Audited & minimal JS implementation of elliptic curve cryptography.
- 🏎 Ultra-fast, hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness: property-based, cross-library and Wycheproof vectors, fuzzing
- ➰ Short Weierstrass, Edwards, Montgomery curves
- ✍️ ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement, hashing to curves
- ✍️ ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
- 🔖 SUF-CMA, SBS (non-repudiation), ZIP215 (consensus friendliness) features for ed25519
- #⃣ hash-to-curve for encoding or hashing an arbitrary string to an elliptic curve point
- 🧜‍♂️ Poseidon ZK-friendly hash
- 🪶 178KB for everything, 25KB for single-curve build
For discussions, questions and support, visit
[GitHub Discussions](https://github.com/paulmillr/noble-curves/discussions)
section of the repository.
### This library belongs to _noble_ crypto
### This library belongs to _noble_ cryptography
> **noble cryptography** — high-security, easily auditable set of contained cryptographic libraries and tools.
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
- Zero or minimal dependencies
- Highly readable TypeScript / JS code
@ -26,10 +22,7 @@ section of the repository.
- All libraries:
[ciphers](https://github.com/paulmillr/noble-ciphers),
[curves](https://github.com/paulmillr/noble-curves),
[hashes](https://github.com/paulmillr/noble-hashes),
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
4kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
[ed25519](https://github.com/paulmillr/noble-ed25519)
[hashes](https://github.com/paulmillr/noble-hashes)
- [Check out homepage](https://paulmillr.com/noble/)
for reading resources, documentation and apps built with noble
@ -45,7 +38,7 @@ A standalone file [noble-curves.js](https://github.com/paulmillr/noble-curves/re
```js
// import * from '@noble/curves'; // Error: use sub-imports, to ensure small app size
import { secp256k1 } from '@noble/curves/secp256k1'; // ESM and Common.js
// import { secp256k1 } from 'npm:@noble/curves@1.4.0/secp256k1'; // Deno
// import { secp256k1 } from 'npm:@noble/curves@1.2.0/secp256k1'; // Deno
```
- [Implementations](#implementations)
@ -96,8 +89,6 @@ const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c12623
const pub2 = secp256k1.getPublicKey(privHex);
```
We support P256 (secp256r1), P384 (secp384r1), P521 (secp521r1).
#### ECDSA public key recovery & extra entropy
```ts
@ -246,7 +237,7 @@ Same RFC7748 / RFC8032 / IRTF draft are followed.
#### bls12-381
See [abstract/bls](#bls-barreto-lynn-scott-curves).
See [abstract/bls](#abstractbls-barreto-lynn-scott-curves).
#### All available imports
@ -555,7 +546,7 @@ import { Field } from '@noble/curves/abstract/modular';
const x25519 = montgomery({
a: 486662n,
Gu: 9n,
P: 2n ** 255n - 19n,
Fp: Field(2n ** 255n - 19n),
montgomeryBits: 255,
nByteLength: 32,
// Optional param
@ -613,12 +604,6 @@ const signature = bls.sign(message, privateKey);
const isValid = bls.verify(signature, message, publicKey);
console.log({ publicKey, signature, isValid });
// Use custom DST, e.g. for Ethereum consensus layer
const htfEthereum = {DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_'};
const signatureEth = bls.sign(message, privateKey, htfEthereum);
const isValidEth = bls.verify(signature, message, publicKey, htfEthereum);
console.log({ signatureEth, isValidEth });
// Sign 1 msg with 3 keys
const privateKeys = [
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
@ -771,7 +756,7 @@ and [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380#section-5.2).
This means, for 32-byte key, we would need 48-byte hash to get 2^-128 bias, which matches curve security level.
`hashToPrivateScalar()` that hashes to **private key** was created for this purpose.
Use [abstract/hash-to-curve](#hash-to-curve-hashing-strings-to-curve-points)
Use [abstract/hash-to-curve](#abstracthash-to-curve-hashing-strings-to-curve-points)
if you need to hash to **public key**.
```ts

@ -1,7 +1,7 @@
# build
The directory is used to build a single file which contains everything.
The directory is used to build a single file `noble-curves.js` which contains everything.
The single file uses iife wrapper and can be used in browsers as-is.
The output file uses iife wrapper and can be used in browsers as-is.
Don't use it unless you can't use NPM/ESM, which support tree shaking.

@ -1,4 +1,4 @@
import { bytesToHex, concatBytes, hexToBytes, utf8ToBytes } from '@noble/curves/abstract/utils';
import { bytesToHex, concatBytes, hexToBytes } from '@noble/curves/abstract/utils';
export { secp256k1, schnorr as secp256k1_schnorr } from '@noble/curves/secp256k1';
export {
@ -17,4 +17,4 @@ export { p384 } from '@noble/curves/p384';
export { p521 } from '@noble/curves/p521';
export { bls12_381 } from '@noble/curves/bls12-381';
export const utils = { bytesToHex, concatBytes, hexToBytes, utf8ToBytes };
export const utils = { bytesToHex, concatBytes, hexToBytes };

445
build/package-lock.json generated

@ -1,445 +0,0 @@
{
"name": "build",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "build",
"version": "1.0.0",
"devDependencies": {
"@noble/curves": "file:..",
"esbuild": "0.20.1"
}
},
"..": {
"version": "1.4.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.4.0"
},
"devDependencies": {
"@paulmillr/jsbt": "0.1.0",
"fast-check": "3.0.0",
"micro-bmark": "0.3.1",
"micro-should": "0.4.0",
"prettier": "3.1.1",
"typescript": "5.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz",
"integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz",
"integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz",
"integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz",
"integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz",
"integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz",
"integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz",
"integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz",
"integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz",
"integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz",
"integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz",
"integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz",
"integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz",
"integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz",
"integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz",
"integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz",
"integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz",
"integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz",
"integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz",
"integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz",
"integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz",
"integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz",
"integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz",
"integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@noble/curves": {
"resolved": "..",
"link": true
},
"node_modules/esbuild": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz",
"integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.20.1",
"@esbuild/android-arm": "0.20.1",
"@esbuild/android-arm64": "0.20.1",
"@esbuild/android-x64": "0.20.1",
"@esbuild/darwin-arm64": "0.20.1",
"@esbuild/darwin-x64": "0.20.1",
"@esbuild/freebsd-arm64": "0.20.1",
"@esbuild/freebsd-x64": "0.20.1",
"@esbuild/linux-arm": "0.20.1",
"@esbuild/linux-arm64": "0.20.1",
"@esbuild/linux-ia32": "0.20.1",
"@esbuild/linux-loong64": "0.20.1",
"@esbuild/linux-mips64el": "0.20.1",
"@esbuild/linux-ppc64": "0.20.1",
"@esbuild/linux-riscv64": "0.20.1",
"@esbuild/linux-s390x": "0.20.1",
"@esbuild/linux-x64": "0.20.1",
"@esbuild/netbsd-x64": "0.20.1",
"@esbuild/openbsd-x64": "0.20.1",
"@esbuild/sunos-x64": "0.20.1",
"@esbuild/win32-arm64": "0.20.1",
"@esbuild/win32-ia32": "0.20.1",
"@esbuild/win32-x64": "0.20.1"
}
}
}
}

@ -2,13 +2,17 @@
"name": "build",
"private": true,
"version": "1.0.0",
"description": "Used to build a single file",
"main": "input.js",
"keywords": [],
"type": "module",
"author": "",
"license": "MIT",
"devDependencies": {
"@noble/curves": "file:..",
"esbuild": "0.20.1"
"@noble/curves": "..",
"esbuild": "0.18.11"
},
"scripts": {
"build:release": "npx esbuild --bundle input.js --outfile=`npx jsbt outfile` --global-name=`npx jsbt global`"
"build": "npx esbuild --bundle input.js --outfile=noble-curves.js --global-name=nobleCurves"
}
}

26
package-lock.json generated

@ -1,18 +1,17 @@
{
"name": "@tornado/noble-curves",
"version": "1.4.0",
"name": "@noble/curves",
"version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@tornado/noble-curves",
"version": "1.4.0",
"name": "@noble/curves",
"version": "1.3.0",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.4.0"
"@noble/hashes": "1.3.3"
},
"devDependencies": {
"@paulmillr/jsbt": "0.1.0",
"fast-check": "3.0.0",
"micro-bmark": "0.3.1",
"micro-should": "0.4.0",
@ -24,9 +23,9 @@
}
},
"node_modules/@noble/hashes": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
"engines": {
"node": ">= 16"
},
@ -34,15 +33,6 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@paulmillr/jsbt": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@paulmillr/jsbt/-/jsbt-0.1.0.tgz",
"integrity": "sha512-TdowoHD36hkZARv6LW4jenkVTdK2vP0sy4ZM8E9MxaqAAIRdwmn3RlB+zWkEHi4hKTgLqMGkURfNkFtt0STX2Q==",
"dev": true,
"bin": {
"jsbt": "jsbt.js"
}
},
"node_modules/fast-check": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.0.0.tgz",

@ -1,6 +1,6 @@
{
"name": "@tornado/noble-curves",
"version": "1.4.0",
"name": "@noble/curves",
"version": "1.3.0",
"description": "Audited & minimal JS implementation of elliptic curve cryptography",
"files": [
"abstract",
@ -14,7 +14,7 @@
"scripts": {
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node hash-to-curve.js; node modular.js; node bls.js; node ristretto255.js; node decaf448.js",
"build": "tsc && tsc -p tsconfig.esm.json",
"build:release": "cd build && npm i && npm run build",
"build:release": "cd build; npm install && npm run build",
"build:clean": "rm *.{js,d.ts,d.ts.map,js.map} esm/*.{js,d.ts,d.ts.map,js.map} 2> /dev/null",
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
"format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
@ -24,14 +24,13 @@
"homepage": "https://paulmillr.com/noble/",
"repository": {
"type": "git",
"url": "https://git.tornado.ws/tornado-packages/noble-curvest"
"url": "https://github.com/paulmillr/noble-curves.git"
},
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.4.0"
"@noble/hashes": "1.3.3"
},
"devDependencies": {
"@paulmillr/jsbt": "0.1.0",
"fast-check": "3.0.0",
"micro-bmark": "0.3.1",
"micro-should": "0.4.0",
@ -179,4 +178,4 @@
"schnorr"
],
"funding": "https://paulmillr.com/funding/"
}
}

@ -83,12 +83,12 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
getPublicKey: (privateKey: PrivKey) => Uint8Array;
getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;
sign: {
(message: Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
(message: ProjPointType<Fp2>, privateKey: PrivKey, htfOpts?: htfBasicOpts): ProjPointType<Fp2>;
(message: Hex, privateKey: PrivKey): Uint8Array;
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
};
signShortSignature: {
(message: Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
(message: ProjPointType<Fp>, privateKey: PrivKey, htfOpts?: htfBasicOpts): ProjPointType<Fp>;
(message: Hex, privateKey: PrivKey): Uint8Array;
(message: ProjPointType<Fp>, privateKey: PrivKey): ProjPointType<Fp>;
};
verify: (
signature: Hex | ProjPointType<Fp2>,

@ -2,7 +2,7 @@
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
import { mod, IField } from './modular.js';
import type { CHash } from './utils.js';
import { bytesToNumberBE, abytes, concatBytes, utf8ToBytes, validateObject } from './utils.js';
import { bytesToNumberBE, isBytes, concatBytes, utf8ToBytes, validateObject } from './utils.js';
/**
* * `DST` is a domain separation tag, defined in section 2.2.5
@ -22,6 +22,12 @@ export type Opts = {
hash: CHash;
};
function validateDST(dst: UnicodeOrBytes): Uint8Array {
if (isBytes(dst)) return dst;
if (typeof dst === 'string') return utf8ToBytes(dst);
throw new Error('DST must be Uint8Array or string');
}
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
const os2ip = bytesToNumberBE;
@ -46,7 +52,10 @@ function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
return arr;
}
function anum(item: unknown): void {
function abytes(item: unknown): void {
if (!isBytes(item)) throw new Error('Uint8Array expected');
}
function isNum(item: unknown): void {
if (!Number.isSafeInteger(item)) throw new Error('number expected');
}
@ -60,7 +69,7 @@ export function expand_message_xmd(
): Uint8Array {
abytes(msg);
abytes(DST);
anum(lenInBytes);
isNum(lenInBytes);
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
@ -94,7 +103,7 @@ export function expand_message_xof(
): Uint8Array {
abytes(msg);
abytes(DST);
anum(lenInBytes);
isNum(lenInBytes);
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
if (DST.length > 255) {
@ -132,8 +141,8 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
});
const { p, k, m, hash, expand, DST: _DST } = options;
abytes(msg);
anum(count);
const DST = typeof _DST === 'string' ? utf8ToBytes(_DST) : _DST;
isNum(count);
const DST = validateDST(_DST);
const log2p = p.toString(2).length;
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
const len_in_bytes = count * m * L;

@ -23,10 +23,6 @@ export function isBytes(a: unknown): a is Uint8Array {
);
}
export function abytes(item: unknown): void {
if (!isBytes(item)) throw new Error('Uint8Array expected');
}
// Array where index 0xf0 (240) is mapped to string 'f0'
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
i.toString(16).padStart(2, '0')
@ -35,7 +31,7 @@ const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
*/
export function bytesToHex(bytes: Uint8Array): string {
abytes(bytes);
if (!isBytes(bytes)) throw new Error('Uint8Array expected');
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
@ -90,7 +86,7 @@ export function bytesToNumberBE(bytes: Uint8Array): bigint {
return hexToNumber(bytesToHex(bytes));
}
export function bytesToNumberLE(bytes: Uint8Array): bigint {
abytes(bytes);
if (!isBytes(bytes)) throw new Error('Uint8Array expected');
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
}
@ -142,11 +138,12 @@ export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
let sum = 0;
for (let i = 0; i < arrays.length; i++) {
const a = arrays[i];
abytes(a);
if (!isBytes(a)) throw new Error('Uint8Array expected');
sum += a.length;
}
const res = new Uint8Array(sum);
for (let i = 0, pad = 0; i < arrays.length; i++) {
let res = new Uint8Array(sum);
let pad = 0;
for (let i = 0; i < arrays.length; i++) {
const a = arrays[i];
res.set(a, pad);
pad += a.length;
@ -198,9 +195,9 @@ export function bitGet(n: bigint, pos: number) {
/**
* Sets single bit at position.
*/
export function bitSet(n: bigint, pos: number, value: boolean) {
export const bitSet = (n: bigint, pos: number, value: boolean) => {
return n | ((value ? _1n : _0n) << BigInt(pos));
}
};
/**
* Calculate mask for N bits. Not using ** operator with bigints because of old engines.

@ -27,7 +27,7 @@ export type BasicWCurve<T> = BasicCurve<T> & {
clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
};
type Entropy = Hex | boolean;
type Entropy = Hex | true;
export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
export type VerOpts = { lowS?: boolean; prehash?: boolean };
@ -158,7 +158,7 @@ export const DER = {
// parse DER signature
const { Err: E } = DER;
const data = typeof hex === 'string' ? h2b(hex) : hex;
ut.abytes(data);
if (!ut.isBytes(data)) throw new Error('ui8a expected');
let l = data.length;
if (l < 2 || data[0] != 0x30) throw new E('Invalid signature tag');
if (data[1] !== l - 2) throw new E('Invalid signature: incorrect length');
@ -733,13 +733,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
const x = ut.bytesToNumberBE(tail);
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
let y: bigint;
try {
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
} catch (sqrtError) {
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
throw new Error('Point is not on curve' + suffix);
}
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
const isYOdd = (y & _1n) === _1n;
// ECDSA
const isHeadOdd = (head & 1) === 1;
@ -977,7 +971,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
const seedArgs = [int2octets(d), int2octets(h1int)];
// extraEntropy. RFC6979 3.6: additional k' (optional).
if (ent != null && ent !== false) {
if (ent != null) {
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes

@ -1364,7 +1364,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
fromHex(hex: Hex): ProjPointType<Fp2> {
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
const P = Fp.ORDER;
const half = value.length / 2;
const half = hex.length / 2;
if (half !== 48 && half !== 96)
throw new Error('Invalid compressed signature length, must be 96 or 192');
const z1 = bytesToNumberBE(value.slice(0, half));

@ -1,3 +0,0 @@
{
"type": "module"
}

@ -2,7 +2,7 @@ import { deepStrictEqual, throws } from 'assert';
import { should, describe } from 'micro-should';
import * as fc from 'fast-check';
import * as mod from '../esm/abstract/modular.js';
import { bytesToHex, isBytes, bytesToHex as toHex } from '../esm/abstract/utils.js';
import { bytesToHex as toHex } from '../esm/abstract/utils.js';
// Generic tests for all curves in package
import { secp192r1, secp224r1 } from './_more-curves.helpers.js';
import { secp256r1 } from '../esm/p256.js';
@ -595,18 +595,6 @@ for (const name in CURVES) {
{ numRuns: NUM_RUNS }
)
);
should('.verify() should verify random signatures in hex', () =>
fc.assert(
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
const priv = toHex(C.utils.randomPrivateKey());
const pub = toHex(C.getPublicKey(priv));
const sig = C.sign(msg, priv);
let sighex = isBytes(sig) ? toHex(sig) : sig.toCompactHex();
deepStrictEqual(C.verify(sighex, msg, pub), true, `priv=${priv},pub=${pub},msg=${msg}`);
}),
{ numRuns: NUM_RUNS }
)
);
should('.verify() should verify empty signatures', () => {
const msg = new Uint8Array([]);
const priv = C.utils.randomPrivateKey();

@ -1173,8 +1173,6 @@ describe('verify()', () => {
const pub = bls.getPublicKey(priv);
const res = bls.verify(sig, msg, pub);
deepStrictEqual(res, true, `${priv}-${msg}`);
const resHex = bls.verify(bytesToHex(sig), msg, pub);
deepStrictEqual(resHex, true, `${priv}-${msg}-hex`);
}
});
should('not verify signature with wrong message', () => {
@ -1195,8 +1193,6 @@ describe('verify()', () => {
const invPub = bls.getPublicKey(invPriv);
const res = bls.verify(sig, msg, invPub);
deepStrictEqual(res, false);
const resHex = bls.verify(bytesToHex(sig), msg, invPub);
deepStrictEqual(resHex, false);
}
});
should('verify signed message (short signatures)', () => {
@ -1206,8 +1202,6 @@ describe('verify()', () => {
const pub = bls.getPublicKeyForShortSignatures(priv);
const res = bls.verifyShortSignature(sig, msg, pub);
deepStrictEqual(res, true, `${priv}-${msg}`);
const resHex = bls.verifyShortSignature(bytesToHex(sig), msg, pub);
deepStrictEqual(resHex, true, `${priv}-${msg}`);
}
});
should('not verify signature with wrong message (short signatures)', () => {
@ -1218,8 +1212,6 @@ describe('verify()', () => {
const pub = bls.getPublicKeyForShortSignatures(priv);
const res = bls.verifyShortSignature(sig, invMsg, pub);
deepStrictEqual(res, false);
const resHex = bls.verifyShortSignature(bytesToHex(sig), invMsg, pub);
deepStrictEqual(resHex, false);
}
});
should('not verify signature with wrong key', () => {
@ -1230,8 +1222,6 @@ describe('verify()', () => {
const invPub = bls.getPublicKeyForShortSignatures(invPriv);
const res = bls.verifyShortSignature(sig, msg, invPub);
deepStrictEqual(res, false);
const resHex = bls.verifyShortSignature(bytesToHex(sig), msg, invPub);
deepStrictEqual(resHex, false);
}
});
describe('batch', () => {
@ -1244,10 +1234,6 @@ describe('verify()', () => {
const signatures = messages.map((message, i) => bls.sign(message, privateKeys[i]));
const aggregatedSignature = bls.aggregateSignatures(signatures);
deepStrictEqual(bls.verifyBatch(aggregatedSignature, messages, publicKey), true);
deepStrictEqual(
bls.verifyBatch(bytesToHex(aggregatedSignature), messages, publicKey),
true
);
})
);
});
@ -1266,10 +1252,6 @@ describe('verify()', () => {
bls.verifyBatch(aggregatedSignature, wrongMessages, publicKey),
messages.every((m, i) => m === wrongMessages[i])
);
deepStrictEqual(
bls.verifyBatch(bytesToHex(aggregatedSignature), wrongMessages, publicKey),
messages.every((m, i) => m === wrongMessages[i])
);
})
);
});
@ -1292,10 +1274,6 @@ describe('verify()', () => {
bls.verifyBatch(aggregatedSignature, messages, wrongPublicKeys),
wrongPrivateKeys.every((p, i) => p === privateKeys[i])
);
deepStrictEqual(
bls.verifyBatch(bytesToHex(aggregatedSignature), messages, wrongPublicKeys),
wrongPrivateKeys.every((p, i) => p === privateKeys[i])
);
}
)
);
@ -1309,10 +1287,6 @@ describe('verify()', () => {
const aggregatedSignature = bls.aggregateSignatures(signatures);
const aggregatedPublicKey = bls.aggregatePublicKeys(publicKey);
deepStrictEqual(bls.verify(aggregatedSignature, message, aggregatedPublicKey), true);
deepStrictEqual(
bls.verify(bytesToHex(aggregatedSignature), message, aggregatedPublicKey),
true
);
})
);
});
@ -1328,10 +1302,6 @@ describe('verify()', () => {
bls.verify(aggregatedSignature, wrongMessage, aggregatedPublicKey),
message === wrongMessage
);
deepStrictEqual(
bls.verify(bytesToHex(aggregatedSignature), wrongMessage, aggregatedPublicKey),
message === wrongMessage
);
})
);
});

@ -1,9 +1,24 @@
{
"extends": "@paulmillr/jsbt/tsconfigs/esm.json",
"compilerOptions": {
"outDir": "esm",
"target": "ES2015"
"target": "es2020",
"module": "es6",
"moduleResolution": "bundler",
"baseUrl": ".",
"paths": {
"@noble/hashes/crypto": ["src/crypto"]
},
"sourceMap": true,
"strict": true,
"allowSyntheticDefaultImports": false,
"allowUnreachableCode": false,
"esModuleInterop": false,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
},
"include": ["index.ts", "src"],
"include": ["src"],
"exclude": ["node_modules", "lib"]
}

@ -1,9 +1,24 @@
{
"extends": "@paulmillr/jsbt/tsconfigs/cjs.json",
"compilerOptions": {
"outDir": ".",
"target": "ES2015"
"target": "es2020",
"lib": ["es2020"], // Set explicitly to remove DOM
"module": "commonjs",
"moduleResolution": "node",
"baseUrl": ".",
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"strict": true,
"allowSyntheticDefaultImports": false,
"allowUnreachableCode": false,
"esModuleInterop": false,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": false,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["index.ts", "src"],
"exclude": ["node_modules", "lib"]
"include": ["src"],
"exclude": ["node_modules", "*.d.ts"]
}