11 Commits
0.8.3 ... 0.9.0

Author SHA1 Message Date
Paul Miller
0ffa38db6b Release 0.9.0. 2023-03-24 11:12:02 +01:00
Paul Miller
c4c580edc0 Bump devdeps 2023-03-24 11:06:48 +01:00
Paul Miller
abe8adac7b README 2023-03-24 10:25:03 +01:00
Paul Miller
4fd2ae82b6 readme 2023-03-21 07:27:45 +01:00
Paul Miller
e2411f7dfd modular: add comment 2023-03-21 07:25:09 +01:00
Paul Miller
cb61e4f292 readme 2023-03-21 07:25:01 +01:00
Paul Miller
bb875791bd docs 2023-03-21 07:11:17 +01:00
Paul Miller
3df2553ced Docs 2023-03-21 07:02:07 +01:00
Paul Miller
8fabc7ff06 All files: rename Fp to Field 2023-03-21 06:51:18 +01:00
Paul Miller
f3c21eb347 weierstrass: make weierstrassPoints fromBytes / toBytes optional 2023-03-21 05:51:10 +01:00
Paul Miller
a8b8192714 Add CURVE.p param 2023-03-21 03:06:06 +01:00
26 changed files with 225 additions and 183 deletions

6
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"files.exclude": {
"*.{js,d.ts,js.map,d.ts.map}": true,
"esm/*.{js,d.ts,js.map,d.ts.map}": true
}
}

141
README.md
View File

@@ -1,31 +1,17 @@
# noble-curves # noble-curves
[Audited](#security) & minimal JS implementation of elliptic curve cryptography. Audited & minimal JS implementation of elliptic curve cryptography.
- **noble** family, zero dependencies
- Short Weierstrass, Edwards, Montgomery curves - Short Weierstrass, Edwards, Montgomery curves
- ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement - ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
- 🔒 [**Audited**](#security) by an independent security firm
- #⃣ [hash to curve](#abstracthash-to-curve-hashing-strings-to-curve-points) - #⃣ [hash to curve](#abstracthash-to-curve-hashing-strings-to-curve-points)
for encoding or hashing an arbitrary string to an elliptic curve point for encoding or hashing an arbitrary string to an elliptic curve point
- 🧜‍♂️ [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash - 🧜‍♂️ [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines - 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness with Wycheproof vectors and - 🔍 Unique tests ensure correctness with Wycheproof vectors and
[cryptofuzz](https://github.com/guidovranken/cryptofuzz) differential fuzzing [cryptofuzz](https://github.com/guidovranken/cryptofuzz) differential fuzzing
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app - 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included
Package consists of two parts:
1. [Abstract](#abstract-api), zero-dependency EC algorithms
2. [Implementations](#implementations), utilizing one dependency `@noble/hashes`,
providing ready-to-use:
- NIST curves secp256r1/P256, secp384r1/P384, secp521r1/P521
- SECG curve secp256k1
- ed25519/curve25519/x25519/ristretto255, edwards448/curve448/x448
implementing
[RFC7748](https://www.rfc-editor.org/rfc/rfc7748) /
[RFC8032](https://www.rfc-editor.org/rfc/rfc8032) /
[ZIP215](https://zips.z.cash/zip-0215) standards
- pairing-friendly curves bls12-381, bn254
Check out [Upgrading](#upgrading) if you've previously used single-feature noble Check out [Upgrading](#upgrading) if you've previously used single-feature noble
packages ([secp256k1](https://github.com/paulmillr/noble-secp256k1), packages ([secp256k1](https://github.com/paulmillr/noble-secp256k1),
@@ -37,12 +23,12 @@ See [Resources](#resources) for articles and real-world software that uses curve
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools. > **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, protection against supply chain attacks - No dependencies, protection against supply chain attacks
- Easily auditable TypeScript/JS code - Auditable TypeScript / JS code
- Supported in all major browsers and stable node.js versions - Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys - All releases are signed with PGP keys
- Check out [homepage](https://paulmillr.com/noble/) & all libraries: - Check out [homepage](https://paulmillr.com/noble/) & all libraries:
[curves](https://github.com/paulmillr/noble-curves) [curves](https://github.com/paulmillr/noble-curves)
([secp256k1](https://github.com/paulmillr/noble-secp256k1), (4kb versions [secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519)), [ed25519](https://github.com/paulmillr/noble-ed25519)),
[hashes](https://github.com/paulmillr/noble-hashes) [hashes](https://github.com/paulmillr/noble-hashes)
@@ -61,6 +47,20 @@ The library is tree-shaking-friendly and does not expose root entry point as
`import * from '@noble/curves'`. Instead, you need to import specific primitives. `import * from '@noble/curves'`. Instead, you need to import specific primitives.
This is done to ensure small size of your apps. This is done to ensure small size of your apps.
Package consists of two parts:
1. [Implementations](#implementations), utilizing one dependency `@noble/hashes`,
providing ready-to-use:
- NIST curves secp256r1/P256, secp384r1/P384, secp521r1/P521
- SECG curve secp256k1
- ed25519/curve25519/x25519/ristretto255, edwards448/curve448/x448
implementing
[RFC7748](https://www.rfc-editor.org/rfc/rfc7748) /
[RFC8032](https://www.rfc-editor.org/rfc/rfc8032) /
[ZIP215](https://zips.z.cash/zip-0215) standards
- pairing-friendly curves bls12-381, bn254
2. [Abstract](#abstract-api), zero-dependency EC algorithms
### Implementations ### Implementations
Each curve can be used in the following way: Each curve can be used in the following way:
@@ -152,6 +152,13 @@ import { hashToCurve, encodeToCurve } from '@noble/curves/ed448';
ed448.getPublicKey(ed448.utils.randomPrivateKey()); ed448.getPublicKey(ed448.utils.randomPrivateKey());
``` ```
Every curve has params:
```ts
import { secp256k1 } from '@noble/curves/secp256k1'; // ESM and Common.js
console.log(secp256k1.CURVE.p, secp256k1.CURVE.n, secp256k1.CURVE.a, secp256k1.CURVE.b);
```
BLS12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to BLS12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to
construct [zk-SNARKs](https://z.cash/technology/zksnarks/) at the 128-bit security construct [zk-SNARKs](https://z.cash/technology/zksnarks/) at the 128-bit security
and use aggregated, batch-verifiable and use aggregated, batch-verifiable
@@ -216,7 +223,7 @@ There are following zero-dependency algorithms:
```ts ```ts
import { weierstrass } from '@noble/curves/abstract/weierstrass'; import { weierstrass } from '@noble/curves/abstract/weierstrass';
import { Fp } from '@noble/curves/abstract/modular'; // finite field for mod arithmetics import { Field } from '@noble/curves/abstract/modular'; // finite field for mod arithmetics
import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash
import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256() import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256()
import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities
@@ -226,7 +233,7 @@ const secq256k1 = weierstrass({
// https://zcash.github.io/halo2/background/curves.html#cycles-of-curves // https://zcash.github.io/halo2/background/curves.html#cycles-of-curves
a: 0n, a: 0n,
b: 7n, b: 7n,
Fp: Fp(2n ** 256n - 432420386565659656852420866394968145599n), Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n, n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n,
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n, Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n, Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
@@ -234,6 +241,8 @@ const secq256k1 = weierstrass({
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)), hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
randomBytes, randomBytes,
}); });
// weierstrassPoints can also be used if you don't need ECDSA, hash, hmac, randomBytes
``` ```
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass` Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
@@ -265,6 +274,7 @@ type CHash = {
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates 6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
```ts ```ts
// `weierstrassPoints()` returns `CURVE` and `ProjectivePoint`
// `weierstrass()` returns `CurveFn` // `weierstrass()` returns `CurveFn`
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array }; type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
type CurveFn = { type CurveFn = {
@@ -359,6 +369,7 @@ point.assertValidity(); // Checks for being on-curve
point.toAffine(); // Converts to 2d affine xy coordinates point.toAffine(); // Converts to 2d affine xy coordinates
secq256k1.CURVE.n; secq256k1.CURVE.n;
secq256k1.CURVE.p;
secq256k1.CURVE.Fp.mod(); secq256k1.CURVE.Fp.mod();
secq256k1.CURVE.hash(); secq256k1.CURVE.hash();
@@ -371,15 +382,15 @@ fast.multiply(privKey); // much faster ECDH now
```ts ```ts
import { twistedEdwards } from '@noble/curves/abstract/edwards'; import { twistedEdwards } from '@noble/curves/abstract/edwards';
import { Fp } from '@noble/curves/abstract/modular'; import { Field } from '@noble/curves/abstract/modular';
import { sha512 } from '@noble/hashes/sha512'; import { sha512 } from '@noble/hashes/sha512';
import { randomBytes } from '@noble/hashes/utils'; import { randomBytes } from '@noble/hashes/utils';
const fp = Fp(2n ** 255n - 19n); const Fp = Field(2n ** 255n - 19n);
const ed25519 = twistedEdwards({ const ed25519 = twistedEdwards({
a: -1n, a: -1n,
d: fp.div(-121665n, 121666n), // -121665n/121666n mod p d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
Fp: fp, Fp: Fp,
n: 2n ** 252n + 27742317777372353535851937790883648493n, n: 2n ** 252n + 27742317777372353535851937790883648493n,
h: 8n, h: 8n,
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n, Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
@@ -457,12 +468,12 @@ interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
```typescript ```typescript
import { montgomery } from '@noble/curves/abstract/montgomery'; import { montgomery } from '@noble/curves/abstract/montgomery';
import { Fp } from '@noble/curves/abstract/modular'; import { Field } from '@noble/curves/abstract/modular';
const x25519 = montgomery({ const x25519 = montgomery({
Fp: Fp(2n ** 255n - 19n),
a: 486662n, a: 486662n,
Gu: 9n, Gu: 9n,
Fp: Field(2n ** 255n - 19n),
montgomeryBits: 255, montgomeryBits: 255,
nByteLength: 32, nByteLength: 32,
// Optional param // Optional param
@@ -735,6 +746,13 @@ hashToCurve
└─ed448 x 1,045 ops/sec @ 956μs/op └─ed448 x 1,045 ops/sec @ 956μs/op
``` ```
## Contributing & testing
1. Clone the repository
2. `npm install` to install build dependencies like TypeScript
3. `npm run build` to compile TypeScript code
4. `npm run test` will execute all main tests
## Resources ## Resources
Article about some of library's features: [Learning fast elliptic-curve cryptography](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/) Article about some of library's features: [Learning fast elliptic-curve cryptography](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/)
@@ -750,45 +768,52 @@ Projects using the library:
- Threshold sigs demo [genthresh.com](https://genthresh.com) - Threshold sigs demo [genthresh.com](https://genthresh.com)
- BBS signatures [github.com/Wind4Greg/BBS-Draft-Checks](https://github.com/Wind4Greg/BBS-Draft-Checks) following [draft-irtf-cfrg-bbs-signatures-latest](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html) - BBS signatures [github.com/Wind4Greg/BBS-Draft-Checks](https://github.com/Wind4Greg/BBS-Draft-Checks) following [draft-irtf-cfrg-bbs-signatures-latest](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html)
- Others - Others
- All curves demo: Elliptic curve calculator [paulmillr.com/ecc](https://paulmillr.com/ecc) - All curves demo: Elliptic curve calculator [paulmillr.com/noble](https://paulmillr.com/noble)
- [micro-starknet](https://github.com/paulmillr/micro-starknet) for stark-friendly elliptic curve. - [micro-starknet](https://github.com/paulmillr/micro-starknet) for stark-friendly elliptic curve.
## Upgrading ## Upgrading
If you're coming from single-feature noble packages, the following changes need to be kept in mind: Previously, the library was split into single-feature packages
noble-secp256k1 and noble-ed25519. curves can be thought as a continuation of their
original work. The libraries now changed their direction towards providing
minimal 4kb implementations of cryptography and are not as feature-complete.
- 2d affine (x, y) points have been removed to reduce complexity and improve speed Upgrading from [@noble/secp256k1](https://github.com/paulmillr/noble-secp256k1) 1.7:
- Removed `number` support as a type for private keys, `bigint` is still supported
- `mod`, `invert` are no longer present in `utils`: use `@noble/curves/abstract/modular`
Upgrading from @noble/secp256k1 1.7: - `getPublicKey`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getPublicKey(priv, false)`
- `sign`
- is now sync; use `signAsync` for async version
- now returns `Signature` instance with `{ r, s, recovery }` properties
- `canonical` option was renamed to `lowS`
- `recovered` option has been removed because recovery bit is always returned now
- `der` option has been removed. There are 2 options:
1. Use compact encoding: `fromCompact`, `toCompactRawBytes`, `toCompactHex`.
Compact encoding is simply a concatenation of 32-byte r and 32-byte s.
2. If you must use DER encoding, switch to noble-curves (see above).
- `verify`
- `strict` option was renamed to `lowS`
- `getSharedSecret`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getSharedSecret(a, b, false)`
- `recoverPublicKey(msg, sig, rec)` was changed to `sig.recoverPublicKey(msg)`
- `number` type for private keys have been removed: use `bigint` instead
- `Point` (2d xy) has been changed to `ProjectivePoint` (3d xyz)
- `utils` were split into `utils` (same api as in noble-curves) and
`etc` (`hmacSha256Sync` and others)
- Compressed (33-byte) public keys are now returned by default, instead of uncompressed Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519) 1.7:
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
- `sign()`
- `der`, `recovered` options were removed
- `canonical` was renamed to `lowS`
- Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature`
- `verify()`
- `strict` was renamed to `lowS`
- `recoverPublicKey()`: moved to sig instance `Signature#recoverPublicKey(msgHash)`
- `Point` was removed: use `ProjectivePoint` in xyz coordinates
- `utils`: Many methods were removed, others were moved to `schnorr` namespace
Upgrading from @noble/ed25519 1.7: - Methods are now sync by default
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
- ed25519ph, ed25519ctx
- `Point` was removed: use `ExtendedPoint` in xyzt coordinates
- `Signature` was removed
- `getSharedSecret` was removed: use separate x25519 sub-module
- `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs - `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs
- `Point` (2d xy) has been changed to `ExtendedPoint` (xyzt)
## Contributing & testing - `Signature` was removed: just use raw bytes or hex now
- `utils` were split into `utils` (same api as in noble-curves) and
1. Clone the repository `etc` (`sha512Sync` and others)
2. `npm install` to install build dependencies like TypeScript - `getSharedSecret` was moved to `x25519` module
3. `npm run build` to compile TypeScript code
4. `npm run test` will execute all main tests
## License ## License

View File

@@ -1,6 +1,6 @@
import { run, mark } from 'micro-bmark'; import { run, mark } from 'micro-bmark';
import { secp256k1 } from '../secp256k1.js'; import { secp256k1 } from '../secp256k1.js';
import { Fp } from '../abstract/modular.js'; import { Field as Fp } from '../abstract/modular.js';
run(async () => { run(async () => {
console.log(`\x1b[36mmodular, secp256k1 field\x1b[0m`); console.log(`\x1b[36mmodular, secp256k1 field\x1b[0m`);

69
package-lock.json generated
View File

@@ -18,8 +18,8 @@
"@noble/hashes": "1.3.0" "@noble/hashes": "1.3.0"
}, },
"devDependencies": { "devDependencies": {
"@scure/bip32": "~1.1.5", "@scure/bip32": "~1.2.0",
"@scure/bip39": "~1.1.1", "@scure/bip39": "~1.2.0",
"@types/node": "18.11.18", "@types/node": "18.11.18",
"fast-check": "3.0.0", "fast-check": "3.0.0",
"micro-bmark": "0.3.1", "micro-bmark": "0.3.1",
@@ -28,22 +28,25 @@
"typescript": "5.0.2" "typescript": "5.0.2"
} }
}, },
"node_modules/@noble/hashes": { "node_modules/@noble/curves": {
"version": "1.3.0", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz",
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", "integrity": "sha512-OqaOf4RWDaCRuBKJLDURrgVxjLmneGsiCXGuzYB5y95YithZMA6w4uk34DHSm0rKMrrYiaeZj48/81EvaAScLQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
"url": "https://paulmillr.com/funding/" "url": "https://paulmillr.com/funding/"
} }
] ],
"dependencies": {
"@noble/hashes": "1.3.0"
}
}, },
"node_modules/@noble/secp256k1": { "node_modules/@noble/hashes": {
"version": "1.7.1", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@@ -64,9 +67,9 @@
] ]
}, },
"node_modules/@scure/bip32": { "node_modules/@scure/bip32": {
"version": "1.1.5", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.2.0.tgz",
"integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", "integrity": "sha512-O+vT/hBVk+ag2i6j2CDemwd1E1MtGt+7O1KzrPNsaNvSsiEK55MyPIxJIMI2PS8Ijj464B2VbQlpRoQXxw1uHg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -75,27 +78,15 @@
} }
], ],
"dependencies": { "dependencies": {
"@noble/hashes": "~1.2.0", "@noble/curves": "~0.8.3",
"@noble/secp256k1": "~1.7.0", "@noble/hashes": "~1.3.0",
"@scure/base": "~1.1.0" "@scure/base": "~1.1.0"
} }
}, },
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz",
"integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"node_modules/@scure/bip39": { "node_modules/@scure/bip39": {
"version": "1.1.1", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz",
"integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", "integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -104,22 +95,10 @@
} }
], ],
"dependencies": { "dependencies": {
"@noble/hashes": "~1.2.0", "@noble/hashes": "~1.3.0",
"@scure/base": "~1.1.0" "@scure/base": "~1.1.0"
} }
}, },
"node_modules/@scure/bip39/node_modules/@noble/hashes": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz",
"integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.11.18", "version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",

View File

@@ -1,7 +1,7 @@
{ {
"name": "@noble/curves", "name": "@noble/curves",
"version": "0.8.3", "version": "0.9.0",
"description": "Minimal, auditable JS implementation of elliptic curve cryptography", "description": "Audited & minimal JS implementation of elliptic curve cryptography",
"files": [ "files": [
"abstract", "abstract",
"esm", "esm",
@@ -31,8 +31,8 @@
"@noble/hashes": "1.3.0" "@noble/hashes": "1.3.0"
}, },
"devDependencies": { "devDependencies": {
"@scure/bip32": "~1.1.5", "@scure/bip32": "~1.2.0",
"@scure/bip39": "~1.1.1", "@scure/bip39": "~1.2.0",
"@types/node": "18.11.18", "@types/node": "18.11.18",
"fast-check": "3.0.0", "fast-check": "3.0.0",
"micro-bmark": "0.3.1", "micro-bmark": "0.3.1",
@@ -171,7 +171,7 @@
"bn254", "bn254",
"pasta", "pasta",
"bls", "bls",
"nist", "noble",
"ecc", "ecc",
"ecdsa", "ecdsa",
"eddsa", "eddsa",

View File

@@ -12,7 +12,7 @@
* Some projects may prefer to swap this relation, it is not supported for now. * Some projects may prefer to swap this relation, it is not supported for now.
*/ */
import { AffinePoint } from './curve.js'; import { AffinePoint } from './curve.js';
import { Field, hashToPrivateScalar } from './modular.js'; import { IField, hashToPrivateScalar } from './modular.js';
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js'; import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
import * as htf from './hash-to-curve.js'; import * as htf from './hash-to-curve.js';
import { import {
@@ -41,15 +41,15 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
htfDefaults: htf.Opts; htfDefaults: htf.Opts;
}; };
x: bigint; x: bigint;
Fp: Field<Fp>; Fp: IField<Fp>;
Fr: Field<bigint>; Fr: IField<bigint>;
Fp2: Field<Fp2> & { Fp2: IField<Fp2> & {
reim: (num: Fp2) => { re: bigint; im: bigint }; reim: (num: Fp2) => { re: bigint; im: bigint };
multiplyByB: (num: Fp2) => Fp2; multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2; frobeniusMap(num: Fp2, power: number): Fp2;
}; };
Fp6: Field<Fp6>; Fp6: IField<Fp6>;
Fp12: Field<Fp12> & { Fp12: IField<Fp12> & {
frobeniusMap(num: Fp12, power: number): Fp12; frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12; multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12; conjugate(num: Fp12): Fp12;
@@ -62,11 +62,11 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
export type CurveFn<Fp, Fp2, Fp6, Fp12> = { export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>; CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
Fr: Field<bigint>; Fr: IField<bigint>;
Fp: Field<Fp>; Fp: IField<Fp>;
Fp2: Field<Fp2>; Fp2: IField<Fp2>;
Fp6: Field<Fp6>; Fp6: IField<Fp6>;
Fp12: Field<Fp12>; Fp12: IField<Fp12>;
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>; G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>; G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
Signature: SignatureCoder<Fp2>; Signature: SignatureCoder<Fp2>;

View File

@@ -1,6 +1,6 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Abelian group utilities // Abelian group utilities
import { Field, validateField, nLength } from './modular.js'; import { IField, validateField, nLength } from './modular.js';
import { validateObject } from './utils.js'; import { validateObject } from './utils.js';
const _0n = BigInt(0); const _0n = BigInt(0);
const _1n = BigInt(1); const _1n = BigInt(1);
@@ -168,7 +168,7 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok. // Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
// Though generator can be different (Fp2 / Fp6 for BLS). // Though generator can be different (Fp2 / Fp6 for BLS).
export type BasicCurve<T> = { export type BasicCurve<T> = {
Fp: Field<T>; // Field over which we'll do calculations (Fp) Fp: IField<T>; // Field over which we'll do calculations (Fp)
n: bigint; // Curve order, total count of valid points in the field n: bigint; // Curve order, total count of valid points in the field
nBitLength?: number; // bit length of curve order nBitLength?: number; // bit length of curve order
nByteLength?: number; // byte length of curve order nByteLength?: number; // byte length of curve order
@@ -195,5 +195,9 @@ export function validateBasic<FP, T>(curve: BasicCurve<FP> & T) {
} }
); );
// Set defaults // Set defaults
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const); return Object.freeze({
...nLength(curve.n, curve.nBitLength),
...curve,
...{ p: curve.Fp.ORDER },
} as const);
} }

View File

@@ -1,6 +1,6 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import type { Group, GroupConstructor, AffinePoint } from './curve.js'; import type { Group, GroupConstructor, AffinePoint } from './curve.js';
import { mod, Field } from './modular.js'; import { mod, IField } from './modular.js';
import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js'; import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
/** /**
@@ -163,7 +163,7 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
return u; return u;
} }
export function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]) { export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]) {
// Make same order as in spec // Make same order as in spec
const COEFF = map.map((i) => Array.from(i).reverse()); const COEFF = map.map((i) => Array.from(i).reverse());
return (x: T, y: T) => { return (x: T, y: T) => {

View File

@@ -97,7 +97,7 @@ export function tonelliShanks(P: bigint) {
// Fast-path // Fast-path
if (S === 1) { if (S === 1) {
const p1div4 = (P + _1n) / _4n; const p1div4 = (P + _1n) / _4n;
return function tonelliFast<T>(Fp: Field<T>, n: T) { return function tonelliFast<T>(Fp: IField<T>, n: T) {
const root = Fp.pow(n, p1div4); const root = Fp.pow(n, p1div4);
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root'); if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
return root; return root;
@@ -106,7 +106,7 @@ export function tonelliShanks(P: bigint) {
// Slow-path // Slow-path
const Q1div2 = (Q + _1n) / _2n; const Q1div2 = (Q + _1n) / _2n;
return function tonelliSlow<T>(Fp: Field<T>, n: T): T { return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1 // Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root'); if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
let r = S; let r = S;
@@ -146,7 +146,7 @@ export function FpSqrt(P: bigint) {
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn; // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
// const NUM = 72057594037927816n; // const NUM = 72057594037927816n;
const p1div4 = (P + _1n) / _4n; const p1div4 = (P + _1n) / _4n;
return function sqrt3mod4<T>(Fp: Field<T>, n: T) { return function sqrt3mod4<T>(Fp: IField<T>, n: T) {
const root = Fp.pow(n, p1div4); const root = Fp.pow(n, p1div4);
// Throw if root**2 != n // Throw if root**2 != n
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root'); if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
@@ -157,7 +157,7 @@ export function FpSqrt(P: bigint) {
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10) // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
if (P % _8n === _5n) { if (P % _8n === _5n) {
const c1 = (P - _5n) / _8n; const c1 = (P - _5n) / _8n;
return function sqrt5mod8<T>(Fp: Field<T>, n: T) { return function sqrt5mod8<T>(Fp: IField<T>, n: T) {
const n2 = Fp.mul(n, _2n); const n2 = Fp.mul(n, _2n);
const v = Fp.pow(n2, c1); const v = Fp.pow(n2, c1);
const nv = Fp.mul(n, v); const nv = Fp.mul(n, v);
@@ -203,7 +203,7 @@ export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) &
// - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract // - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract
// Field is not always over prime, Fp2 for example has ORDER(q)=p^m // Field is not always over prime, Fp2 for example has ORDER(q)=p^m
export interface Field<T> { export interface IField<T> {
ORDER: bigint; ORDER: bigint;
BYTES: number; BYTES: number;
BITS: number; BITS: number;
@@ -249,7 +249,7 @@ const FIELD_FIELDS = [
'eql', 'add', 'sub', 'mul', 'pow', 'div', 'eql', 'add', 'sub', 'mul', 'pow', 'div',
'addN', 'subN', 'mulN', 'sqrN' 'addN', 'subN', 'mulN', 'sqrN'
] as const; ] as const;
export function validateField<T>(field: Field<T>) { export function validateField<T>(field: IField<T>) {
const initial = { const initial = {
ORDER: 'bigint', ORDER: 'bigint',
MASK: 'bigint', MASK: 'bigint',
@@ -264,7 +264,7 @@ export function validateField<T>(field: Field<T>) {
} }
// Generic field functions // Generic field functions
export function FpPow<T>(f: Field<T>, num: T, power: bigint): T { export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
// Should have same speed as pow for bigints // Should have same speed as pow for bigints
// TODO: benchmark! // TODO: benchmark!
if (power < _0n) throw new Error('Expected power > 0'); if (power < _0n) throw new Error('Expected power > 0');
@@ -280,7 +280,8 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
return p; return p;
} }
export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] { // 0 is non-invertible: non-batched version will throw on 0
export function FpInvertBatch<T>(f: IField<T>, nums: T[]): T[] {
const tmp = new Array(nums.length); const tmp = new Array(nums.length);
// Walk from first to last, multiply them by each other MOD p // Walk from first to last, multiply them by each other MOD p
const lastMultiplied = nums.reduce((acc, num, i) => { const lastMultiplied = nums.reduce((acc, num, i) => {
@@ -299,12 +300,12 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
return tmp; return tmp;
} }
export function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T { export function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T {
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs)); return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
} }
// This function returns True whenever the value x is a square in the field F. // This function returns True whenever the value x is a square in the field F.
export function FpIsSquare<T>(f: Field<T>) { export function FpIsSquare<T>(f: IField<T>) {
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
return (x: T): boolean => { return (x: T): boolean => {
const p = f.pow(x, legendreConst); const p = f.pow(x, legendreConst);
@@ -320,16 +321,24 @@ export function nLength(n: bigint, nBitLength?: number) {
return { nBitLength: _nBitLength, nByteLength }; return { nBitLength: _nBitLength, nByteLength };
} }
// NOTE: very fragile, always bench. Major performance points: type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
// - NonNormalized ops /**
// - Object.freeze * Initializes a galois field over prime. Non-primes are not supported for now.
// - same shape of object (don't add/remove keys) * Do not init in loop: slow. Very fragile: always run a benchmark on change.
type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>; * Major performance gains:
export function Fp( * a) non-normalized operations like mulN instead of mul
* b) `Object.freeze`
* c) Same object shape: never add or remove keys
* @param ORDER prime positive bigint
* @param bitLen how many bits the field consumes
* @param isLE (def: false) if encoding / decoding should be in little-endian
* @param redef optional faster redefinitions of sqrt and other methods
*/
export function Field(
ORDER: bigint, ORDER: bigint,
bitLen?: number, bitLen?: number,
isLE = false, isLE = false,
redef: Partial<Field<bigint>> = {} redef: Partial<IField<bigint>> = {}
): Readonly<FpField> { ): Readonly<FpField> {
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`); if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen); const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
@@ -382,13 +391,13 @@ export function Fp(
return Object.freeze(f); return Object.freeze(f);
} }
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) { export function FpSqrtOdd<T>(Fp: IField<T>, elm: T) {
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`); if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm); const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? root : Fp.neg(root); return Fp.isOdd(root) ? root : Fp.neg(root);
} }
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) { export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`); if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm); const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? Fp.neg(root) : root; return Fp.isOdd(root) ? Fp.neg(root) : root;

View File

@@ -1,10 +1,10 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info // Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
import { Field, FpPow, validateField } from './modular.js'; import { IField, FpPow, validateField } from './modular.js';
// We don't provide any constants, since different implementations use different constants. // We don't provide any constants, since different implementations use different constants.
// For reference constants see './test/poseidon.test.js'. // For reference constants see './test/poseidon.test.js'.
export type PoseidonOpts = { export type PoseidonOpts = {
Fp: Field<bigint>; Fp: IField<bigint>;
t: number; t: number;
roundsFull: number; roundsFull: number;
roundsPartial: number; roundsPartial: number;

View File

@@ -82,8 +82,8 @@ export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
export type CurvePointsType<T> = BasicWCurve<T> & { export type CurvePointsType<T> = BasicWCurve<T> & {
// Bytes // Bytes
fromBytes: (bytes: Uint8Array) => AffinePoint<T>; fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, compressed: boolean) => Uint8Array; toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
}; };
function validatePointOpts<T>(curve: CurvePointsType<T>) { function validatePointOpts<T>(curve: CurvePointsType<T>) {
@@ -93,8 +93,6 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
{ {
a: 'field', a: 'field',
b: 'field', b: 'field',
fromBytes: 'function',
toBytes: 'function',
}, },
{ {
allowedPrivateKeyLengths: 'array', allowedPrivateKeyLengths: 'array',
@@ -102,6 +100,8 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
isTorsionFree: 'function', isTorsionFree: 'function',
clearCofactor: 'function', clearCofactor: 'function',
allowInfinityPoint: 'boolean', allowInfinityPoint: 'boolean',
fromBytes: 'function',
toBytes: 'function',
} }
); );
const { endo, Fp, a } = opts; const { endo, Fp, a } = opts;
@@ -184,6 +184,23 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
const CURVE = validatePointOpts(opts); const CURVE = validatePointOpts(opts);
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
const toBytes =
CURVE.toBytes ||
((c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => {
const a = point.toAffine();
return ut.concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
});
const fromBytes =
CURVE.fromBytes ||
((bytes: Uint8Array) => {
// const head = bytes[0];
const tail = bytes.subarray(1);
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
return { x, y };
});
/** /**
* y² = x³ + ax + b: Short weierstrass curve formula * y² = x³ + ax + b: Short weierstrass curve formula
* @returns y² * @returns y²
@@ -280,7 +297,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
* @param hex short/long ECDSA hex * @param hex short/long ECDSA hex
*/ */
static fromHex(hex: Hex): Point { static fromHex(hex: Hex): Point {
const P = Point.fromAffine(CURVE.fromBytes(ensureBytes('pointHex', hex))); const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
P.assertValidity(); P.assertValidity();
return P; return P;
} }
@@ -563,7 +580,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
toRawBytes(isCompressed = true): Uint8Array { toRawBytes(isCompressed = true): Uint8Array {
this.assertValidity(); this.assertValidity();
return CURVE.toBytes(Point, this, isCompressed); return toBytes(Point, this, isCompressed);
} }
toHex(isCompressed = true): string { toHex(isCompressed = true): string {
@@ -574,6 +591,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits); const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
return { return {
CURVE,
ProjectivePoint: Point as ProjConstructor<T>, ProjectivePoint: Point as ProjConstructor<T>,
normPrivateKeyToScalar, normPrivateKeyToScalar,
weierstrassEquation, weierstrassEquation,
@@ -652,8 +670,7 @@ export type CurveFn = {
export function weierstrass(curveDef: CurveType): CurveFn { export function weierstrass(curveDef: CurveType): CurveFn {
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>; const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
const CURVE_ORDER = CURVE.n; const { Fp, n: CURVE_ORDER } = CURVE;
const Fp = CURVE.Fp;
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32 const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32 const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
@@ -1055,11 +1072,10 @@ export function weierstrass(curveDef: CurveType): CurveFn {
} }
// Implementation of the Shallue and van de Woestijne method for any Weierstrass curve // Implementation of the Shallue and van de Woestijne method for any Weierstrass curve
// TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular? // TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular?
// b = True and y = sqrt(u / v) if (u / v) is square in F, and // b = True and y = sqrt(u / v) if (u / v) is square in F, and
// b = False and y = sqrt(Z * (u / v)) otherwise. // b = False and y = sqrt(Z * (u / v)) otherwise.
export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) { export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
// Generic implementation // Generic implementation
const q = Fp.ORDER; const q = Fp.ORDER;
let l = 0n; let l = 0n;
@@ -1124,7 +1140,7 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
} }
// From draft-irtf-cfrg-hash-to-curve-16 // From draft-irtf-cfrg-hash-to-curve-16
export function mapToCurveSimpleSWU<T>( export function mapToCurveSimpleSWU<T>(
Fp: mod.Field<T>, Fp: mod.IField<T>,
opts: { opts: {
A: T; A: T;
B: T; B: T;

View File

@@ -72,13 +72,13 @@ import { isogenyMap } from './abstract/hash-to-curve.js';
// CURVE FIELDS // CURVE FIELDS
// Finite field over p. // Finite field over p.
const Fp = const Fp =
mod.Fp( mod.Field(
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn
); );
type Fp = bigint; type Fp = bigint;
// Finite field over r. // Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful. // This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n); const Fr = mod.Field(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
// Fp₂ over complex plane // Fp₂ over complex plane
type BigintTuple = [bigint, bigint]; type BigintTuple = [bigint, bigint];
@@ -124,7 +124,7 @@ const FP2_ORDER =
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn ** 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn **
2n; 2n;
const Fp2: mod.Field<Fp2> & Fp2Utils = { const Fp2: mod.IField<Fp2> & Fp2Utils = {
ORDER: FP2_ORDER, ORDER: FP2_ORDER,
BITS: bitLen(FP2_ORDER), BITS: bitLen(FP2_ORDER),
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8), BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
@@ -333,7 +333,7 @@ type Fp6Utils = {
multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6; multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6;
}; };
const Fp6: mod.Field<Fp6> & Fp6Utils = { const Fp6: mod.IField<Fp6> & Fp6Utils = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 3 * Fp2.BITS, BITS: 3 * Fp2.BITS,
BYTES: 3 * Fp2.BYTES, BYTES: 3 * Fp2.BYTES,
@@ -545,7 +545,7 @@ type Fp12Utils = {
_cyclotomicExp(num: Fp12, n: bigint): Fp12; _cyclotomicExp(num: Fp12, n: bigint): Fp12;
}; };
const Fp12: mod.Field<Fp12> & Fp12Utils = { const Fp12: mod.IField<Fp12> & Fp12Utils = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 2 * Fp2.BITS, BITS: 2 * Fp2.BITS,
BYTES: 2 * Fp2.BYTES, BYTES: 2 * Fp2.BYTES,

View File

@@ -2,7 +2,7 @@
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { weierstrass } from './abstract/weierstrass.js'; import { weierstrass } from './abstract/weierstrass.js';
import { getHash } from './_shortw_utils.js'; import { getHash } from './_shortw_utils.js';
import { Fp } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
/** /**
* bn254 pairing-friendly curve. * bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security. * Previously known as alt_bn_128, when it had 128-bit security.
@@ -12,7 +12,7 @@ import { Fp } from './abstract/modular.js';
export const bn254 = weierstrass({ export const bn254 = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(3), b: BigInt(3),
Fp: Fp(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')), Fp: Field(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'), n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
Gx: BigInt(1), Gx: BigInt(1),
Gy: BigInt(2), Gy: BigInt(2),

View File

@@ -3,7 +3,7 @@ import { sha512 } from '@noble/hashes/sha512';
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
import { twistedEdwards, ExtPointType } from './abstract/edwards.js'; import { twistedEdwards, ExtPointType } from './abstract/edwards.js';
import { montgomery } from './abstract/montgomery.js'; import { montgomery } from './abstract/montgomery.js';
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js'; import { mod, pow2, isNegativeLE, Field, FpSqrtEven } from './abstract/modular.js';
import { import {
equalBytes, equalBytes,
bytesToHex, bytesToHex,

View File

@@ -2,7 +2,7 @@
import { shake256 } from '@noble/hashes/sha3'; import { shake256 } from '@noble/hashes/sha3';
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
import { twistedEdwards } from './abstract/edwards.js'; import { twistedEdwards } from './abstract/edwards.js';
import { mod, pow2, Fp as Field } from './abstract/modular.js'; import { mod, pow2, Field } from './abstract/modular.js';
import { montgomery } from './abstract/montgomery.js'; import { montgomery } from './abstract/montgomery.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

View File

@@ -3,7 +3,7 @@ import { sha512 } from '@noble/hashes/sha512';
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
import { twistedEdwards } from './abstract/edwards.js'; import { twistedEdwards } from './abstract/edwards.js';
import { blake2s } from '@noble/hashes/blake2s'; import { blake2s } from '@noble/hashes/blake2s';
import { Fp } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
/** /**
* jubjub Twisted Edwards curve. * jubjub Twisted Edwards curve.
@@ -17,7 +17,7 @@ export const jubjub = twistedEdwards({
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'), d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
// Finite field 𝔽p over which we'll do calculations // Finite field 𝔽p over which we'll do calculations
// Same value as bls12-381 Fr (not Fp) // Same value as bls12-381 Fr (not Fp)
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')), Fp: Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
// Subgroup order: how many points curve has // Subgroup order: how many points curve has
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'), n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
// Cofactor // Cofactor

View File

@@ -1,7 +1,7 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js'; import { createCurve } from './_shortw_utils.js';
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { Fp as Field } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

View File

@@ -1,7 +1,7 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js'; import { createCurve } from './_shortw_utils.js';
import { sha384 } from '@noble/hashes/sha512'; import { sha384 } from '@noble/hashes/sha512';
import { Fp as Field } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

View File

@@ -1,7 +1,7 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js'; import { createCurve } from './_shortw_utils.js';
import { sha512 } from '@noble/hashes/sha512'; import { sha512 } from '@noble/hashes/sha512';
import { Fp as Field } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

View File

@@ -11,7 +11,7 @@ export const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46e
export const pallas = weierstrass({ export const pallas = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(5), b: BigInt(5),
Fp: mod.Fp(p), Fp: mod.Field(p),
n: q, n: q,
Gx: mod.mod(BigInt(-1), p), Gx: mod.mod(BigInt(-1), p),
Gy: BigInt(2), Gy: BigInt(2),
@@ -22,7 +22,7 @@ export const pallas = weierstrass({
export const vesta = weierstrass({ export const vesta = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(5), b: BigInt(5),
Fp: mod.Fp(q), Fp: mod.Field(q),
n: p, n: p,
Gx: mod.mod(BigInt(-1), q), Gx: mod.mod(BigInt(-1), q),
Gy: BigInt(2), Gy: BigInt(2),

View File

@@ -1,7 +1,7 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { randomBytes } from '@noble/hashes/utils'; import { randomBytes } from '@noble/hashes/utils';
import { Fp as Field, mod, pow2 } from './abstract/modular.js'; import { Field, mod, pow2 } from './abstract/modular.js';
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import type { Hex, PrivKey } from './abstract/utils.js'; import type { Hex, PrivKey } from './abstract/utils.js';
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js'; import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
@@ -43,7 +43,6 @@ function sqrtMod(y: bigint): bigint {
} }
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod }); const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
type Fp = bigint;
export const secp256k1 = createCurve( export const secp256k1 = createCurve(
{ {
@@ -245,7 +244,7 @@ const isoMap = htf.isogenyMap(
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f', '0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1 '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
], ],
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]] ].map((i) => i.map((j) => BigInt(j))) as [bigint[], bigint[], bigint[], bigint[]]
); );
const mapSWU = mapToCurveSimpleSWU(Fp, { const mapSWU = mapToCurveSimpleSWU(Fp, {
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'), A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),

View File

@@ -1,7 +1,7 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from '../esm/_shortw_utils.js'; import { createCurve } from '../esm/_shortw_utils.js';
import { sha224, sha256 } from '@noble/hashes/sha256'; import { sha224, sha256 } from '@noble/hashes/sha256';
import { Fp } from '../esm/abstract/modular.js'; import { Field as Fp } from '../esm/abstract/modular.js';
// NIST secp192r1 aka P192 // NIST secp192r1 aka P192
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1 // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1

View File

@@ -1,7 +1,7 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { utf8ToBytes } from '@noble/hashes/utils'; import { utf8ToBytes } from '@noble/hashes/utils';
import { Fp, validateField } from '../esm/abstract/modular.js'; import { Field as Fp, validateField } from '../esm/abstract/modular.js';
import { poseidon } from '../esm/abstract/poseidon.js'; import { poseidon } from '../esm/abstract/poseidon.js';
import * as u from '../esm/abstract/utils.js'; import * as u from '../esm/abstract/utils.js';

View File

@@ -553,6 +553,7 @@ for (const name in CURVES) {
}); });
} }
describe(name, () => { describe(name, () => {
if (['bn254', 'pallas', 'vesta'].includes(name)) return;
// Generic complex things (getPublicKey/sign/verify/getSharedSecret) // Generic complex things (getPublicKey/sign/verify/getSharedSecret)
should('.getPublicKey() type check', () => { should('.getPublicKey() type check', () => {
throws(() => C.getPublicKey(0), '0'); throws(() => C.getPublicKey(0), '0');

View File

@@ -8,7 +8,8 @@ import './ed25519.test.js';
import './secp256k1.test.js'; import './secp256k1.test.js';
import './secp256k1-schnorr.test.js'; import './secp256k1-schnorr.test.js';
import './jubjub.test.js'; import './jubjub.test.js';
import './bls12-381.test.js';
import './hash-to-curve.test.js'; import './hash-to-curve.test.js';
import './poseidon.test.js';
import './bls12-381.test.js';
should.run(); should.run();

View File

@@ -132,7 +132,9 @@ describe('Stark', () => {
// Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt // Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
should('poseidonperm_x5_255_3', () => { should('poseidonperm_x5_255_3', () => {
const Fp = mod.Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')); const Fp = mod.Field(
BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')
);
const mds = [ const mds = [
[ [
@@ -179,7 +181,7 @@ should('poseidonperm_x5_255_3', () => {
}); });
should('poseidonperm_x5_255_5', () => { should('poseidonperm_x5_255_5', () => {
const Fp = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n); const Fp = mod.Field(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
const t = 5; const t = 5;
const mds = [ const mds = [
@@ -250,7 +252,7 @@ should('poseidonperm_x5_255_5', () => {
}); });
should('poseidonperm_x5_254_3', () => { should('poseidonperm_x5_254_3', () => {
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n); const Fp = mod.Field(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
const t = 3; const t = 3;
const mds = [ const mds = [
@@ -297,7 +299,7 @@ should('poseidonperm_x5_254_3', () => {
}); });
should('poseidonperm_x5_254_5', () => { should('poseidonperm_x5_254_5', () => {
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n); const Fp = mod.Field(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
const t = 5; const t = 5;
const mds = [ const mds = [