Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19f04a4c1c | ||
|
|
d0c3bee4de | ||
|
|
4244f97d38 | ||
|
|
618508d32c | ||
|
|
3936449e7b | ||
|
|
0ffa38db6b | ||
|
|
c4c580edc0 | ||
|
|
abe8adac7b | ||
|
|
4fd2ae82b6 | ||
|
|
e2411f7dfd | ||
|
|
cb61e4f292 | ||
|
|
bb875791bd | ||
|
|
3df2553ced | ||
|
|
8fabc7ff06 | ||
|
|
f3c21eb347 | ||
|
|
a8b8192714 | ||
|
|
1c6aa07ff7 | ||
|
|
e110237298 | ||
|
|
45393db807 | ||
|
|
acc3a9dc4d | ||
|
|
9295b0dbae | ||
|
|
5784ef23f6 | ||
|
|
ef55efe842 |
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
410
README.md
410
README.md
@@ -1,28 +1,21 @@
|
|||||||
# 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 [cryptofuzz](https://github.com/guidovranken/cryptofuzz) differential fuzzing
|
- 🔍 Unique tests ensure correctness with Wycheproof vectors and
|
||||||
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
[cryptofuzz](https://github.com/guidovranken/cryptofuzz) differential fuzzing
|
||||||
|
- 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included
|
||||||
|
|
||||||
Package consists of two parts:
|
Check out [Upgrading](#upgrading) if you've previously used single-feature noble
|
||||||
|
packages ([secp256k1](https://github.com/paulmillr/noble-secp256k1),
|
||||||
1. [Abstract](#abstract-api), zero-dependency EC algorithms
|
[ed25519](https://github.com/paulmillr/noble-ed25519)).
|
||||||
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 [RFC7748](https://www.rfc-editor.org/rfc/rfc7748) / [RFC8032](https://www.rfc-editor.org/rfc/rfc8032) / [ZIP215](https://zips.z.cash/zip-0215) stuff
|
|
||||||
- pairing-friendly curves bls12-381, bn254
|
|
||||||
|
|
||||||
Check out [Upgrading](#upgrading) if you've previously used single-feature noble packages
|
|
||||||
([secp256k1](https://github.com/paulmillr/noble-secp256k1), [ed25519](https://github.com/paulmillr/noble-ed25519)).
|
|
||||||
See [Resources](#resources) for articles and real-world software that uses curves.
|
See [Resources](#resources) for articles and real-world software that uses curves.
|
||||||
|
|
||||||
### This library belongs to _noble_ crypto
|
### This library belongs to _noble_ crypto
|
||||||
@@ -30,33 +23,50 @@ 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)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Use NPM for browser / node.js:
|
Browser, deno and node.js are supported:
|
||||||
|
|
||||||
> npm install @noble/curves
|
> npm install @noble/curves
|
||||||
|
|
||||||
For [Deno](https://deno.land), use it with [npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers). In browser, you could also include the single file from
|
For [Deno](https://deno.land), use it with
|
||||||
|
[npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers).
|
||||||
|
In browser, you could also include the single file from
|
||||||
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases).
|
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases).
|
||||||
|
|
||||||
The library is tree-shaking-friendly and does not expose root entry point as `import * from '@noble/curves'`.
|
The library is tree-shaking-friendly and does not expose root entry point as
|
||||||
Instead, you need to import specific primitives. This is done to ensure small size of your apps.
|
`import * from '@noble/curves'`. Instead, you need to import specific primitives.
|
||||||
|
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:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { secp256k1 } from '@noble/curves/secp256k1'; // ECMAScript Modules (ESM) and Common.js
|
import { secp256k1 } from '@noble/curves/secp256k1'; // ESM and Common.js
|
||||||
// import { secp256k1 } from 'npm:@noble/curves@1.2.0/secp256k1'; // Deno
|
// import { secp256k1 } from 'npm:@noble/curves@1.2.0/secp256k1'; // Deno
|
||||||
const priv = secp256k1.utils.randomPrivateKey();
|
const priv = secp256k1.utils.randomPrivateKey();
|
||||||
const pub = secp256k1.getPublicKey(priv);
|
const pub = secp256k1.getPublicKey(priv);
|
||||||
@@ -64,8 +74,9 @@ const msg = new Uint8Array(32).fill(1);
|
|||||||
const sig = secp256k1.sign(msg, priv);
|
const sig = secp256k1.sign(msg, priv);
|
||||||
secp256k1.verify(sig, msg, pub) === true;
|
secp256k1.verify(sig, msg, pub) === true;
|
||||||
|
|
||||||
|
// hex strings are also supported besides Uint8Arrays:
|
||||||
const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236';
|
const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236';
|
||||||
const pub2 = secp256k1.getPublicKey(privHex); // keys & other inputs can be Uint8Array-s or hex strings
|
const pub2 = secp256k1.getPublicKey(privHex);
|
||||||
```
|
```
|
||||||
|
|
||||||
All curves:
|
All curves:
|
||||||
@@ -90,7 +101,7 @@ Weierstrass curves feature recovering public keys from signatures and ECDH key a
|
|||||||
const sigImprovedSecurity = secp256k1.sign(msg, priv, { extraEntropy: true });
|
const sigImprovedSecurity = secp256k1.sign(msg, priv, { extraEntropy: true });
|
||||||
sig.recoverPublicKey(msg) === pub; // public key recovery
|
sig.recoverPublicKey(msg) === pub; // public key recovery
|
||||||
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
||||||
const shared = secp256k1.getSharedSecret(priv, someonesPub); // ECDH (elliptic curve diffie-hellman)
|
const shared = secp256k1.getSharedSecret(priv, someonesPub); // ECDH
|
||||||
```
|
```
|
||||||
|
|
||||||
secp256k1 has schnorr signature implementation which follows
|
secp256k1 has schnorr signature implementation which follows
|
||||||
@@ -103,7 +114,6 @@ const pub = schnorr.getPublicKey(priv);
|
|||||||
const msg = new TextEncoder().encode('hello');
|
const msg = new TextEncoder().encode('hello');
|
||||||
const sig = schnorr.sign(msg, priv);
|
const sig = schnorr.sign(msg, priv);
|
||||||
const isValid = schnorr.verify(sig, msg, pub);
|
const isValid = schnorr.verify(sig, msg, pub);
|
||||||
console.log(isValid);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
ed25519 module has ed25519ctx / ed25519ph variants,
|
ed25519 module has ed25519ctx / ed25519ph variants,
|
||||||
@@ -134,18 +144,27 @@ RistrettoPoint.hashToCurve('Ristretto is traditionally a short shot of espresso
|
|||||||
// also has add(), equals(), multiply(), toRawBytes() methods
|
// also has add(), equals(), multiply(), toRawBytes() methods
|
||||||
```
|
```
|
||||||
|
|
||||||
ed448 module is basically the same:
|
ed448 is similar:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
|
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
|
||||||
import { hashToCurve, encodeToCurve } from '@noble/curves/ed448';
|
import { hashToCurve, encodeToCurve } from '@noble/curves/ed448';
|
||||||
|
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
|
||||||
[threshold signatures](https://medium.com/snigirev.stepan/bls-signatures-better-than-schnorr-5a7fe30ea716),
|
[threshold signatures](https://medium.com/snigirev.stepan/bls-signatures-better-than-schnorr-5a7fe30ea716),
|
||||||
using Boneh-Lynn-Shacham signature scheme.
|
using Boneh-Lynn-Shacham signature scheme. Compatible with ETH and others,
|
||||||
|
just make sure to provide correct DST (domain separation tag argument).
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
||||||
@@ -182,10 +201,13 @@ console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
|
|||||||
|
|
||||||
## Abstract API
|
## Abstract API
|
||||||
|
|
||||||
Abstract API allows to define custom curves. All arithmetics is done with JS bigints over finite fields,
|
Abstract API allows to define custom curves. All arithmetics is done with JS
|
||||||
which is defined from `modular` sub-module. For scalar multiplication, we use [precomputed tables with w-ary non-adjacent form (wNAF)](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
|
bigints over finite fields, which is defined from `modular` sub-module. For
|
||||||
Precomputes are enabled for weierstrass and edwards BASE points of a curve. You could precompute any
|
scalar multiplication, we use
|
||||||
other point (e.g. for ECDH) using `utils.precompute()` method: check out examples.
|
[precomputed tables with w-ary non-adjacent form (wNAF)](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
|
||||||
|
Precomputes are enabled for weierstrass and edwards BASE points of a curve. You
|
||||||
|
could precompute any other point (e.g. for ECDH) using `utils.precompute()`
|
||||||
|
method: check out examples.
|
||||||
|
|
||||||
There are following zero-dependency algorithms:
|
There are following zero-dependency algorithms:
|
||||||
|
|
||||||
@@ -201,14 +223,36 @@ There are following zero-dependency algorithms:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
||||||
|
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 { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256()
|
||||||
|
import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities
|
||||||
|
const secq256k1 = weierstrass({
|
||||||
|
// secq256k1: cycle of secp256k1 with Fp/N flipped.
|
||||||
|
// https://personaelabs.org/posts/spartan-ecdsa
|
||||||
|
// https://zcash.github.io/halo2/background/curves.html#cycles-of-curves
|
||||||
|
a: 0n,
|
||||||
|
b: 7n,
|
||||||
|
Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
|
||||||
|
n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n,
|
||||||
|
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
||||||
|
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
||||||
|
hash: sha256,
|
||||||
|
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
|
||||||
|
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` expects arguments `a`, `b`, field `Fp`, curve order `n`, cofactor `h`
|
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
|
||||||
|
expects arguments `a`, `b`, field `Fp`, curve order `n`, cofactor `h`
|
||||||
and coordinates `Gx`, `Gy` of generator point.
|
and coordinates `Gx`, `Gy` of generator point.
|
||||||
|
|
||||||
**`k` generation** is done deterministically, following [RFC6979](https://www.rfc-editor.org/rfc/rfc6979).
|
**`k` generation** is done deterministically, following
|
||||||
For this you will need `hmac` & `hash`, which in our implementations is provided by noble-hashes.
|
[RFC6979](https://www.rfc-editor.org/rfc/rfc6979). For this you will need
|
||||||
If you're using different hashing library, make sure to wrap it in the following interface:
|
`hmac` & `hash`, which in our implementations is provided by noble-hashes. If
|
||||||
|
you're using different hashing library, make sure to wrap it in the following interface:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
type CHash = {
|
type CHash = {
|
||||||
@@ -224,11 +268,36 @@ type CHash = {
|
|||||||
1. Exported as `ProjectivePoint`
|
1. Exported as `ProjectivePoint`
|
||||||
2. Represented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
2. Represented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
||||||
3. Use complete exception-free formulas for addition and doubling
|
3. Use complete exception-free formulas for addition and doubling
|
||||||
4. Can be decoded/encoded from/to Uint8Array / hex strings using `ProjectivePoint.fromHex` and `ProjectivePoint#toRawBytes()`
|
4. Can be decoded/encoded from/to Uint8Array / hex strings using
|
||||||
|
`ProjectivePoint.fromHex` and `ProjectivePoint#toRawBytes()`
|
||||||
5. Have `assertValidity()` which checks for being on-curve
|
5. Have `assertValidity()` which checks for being on-curve
|
||||||
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`
|
||||||
|
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
|
||||||
|
type CurveFn = {
|
||||||
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||||
|
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||||
|
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
||||||
|
verify: (
|
||||||
|
signature: Hex | SignatureType,
|
||||||
|
msgHash: Hex,
|
||||||
|
publicKey: Hex,
|
||||||
|
opts?: { lowS?: boolean; prehash?: boolean }
|
||||||
|
) => boolean;
|
||||||
|
ProjectivePoint: ProjectivePointConstructor;
|
||||||
|
Signature: SignatureConstructor;
|
||||||
|
utils: {
|
||||||
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
|
isValidPrivateKey(key: PrivKey): boolean;
|
||||||
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// T is usually bigint, but can be something else like complex numbers in BLS curves
|
// T is usually bigint, but can be something else like complex numbers in BLS curves
|
||||||
interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
||||||
readonly px: T;
|
readonly px: T;
|
||||||
@@ -254,7 +323,8 @@ interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**ECDSA signatures** are represented by `Signature` instances and can be described by the interface:
|
**ECDSA signatures** are represented by `Signature` instances and can be
|
||||||
|
described by the interface:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
interface SignatureType {
|
interface SignatureType {
|
||||||
@@ -279,28 +349,9 @@ type SignatureConstructor = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Example implementing [secq256k1](https://personaelabs.org/posts/spartan-ecdsa) (NOT secp256k1)
|
More examples:
|
||||||
[cycle](https://zcash.github.io/halo2/background/curves.html#cycles-of-curves) of secp256k1 with Fp/N flipped.
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
|
||||||
import { Field } from '@noble/curves/abstract/modular'; // finite field, mod arithmetics done over it
|
|
||||||
import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash, with blockLen/outputLen
|
|
||||||
import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256()
|
|
||||||
import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities
|
|
||||||
const secq256k1 = weierstrass({
|
|
||||||
// secq256k1: cycle of secp256k1 with Fp/N flipped.
|
|
||||||
a: 0n,
|
|
||||||
b: 7n,
|
|
||||||
Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
|
|
||||||
n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n,
|
|
||||||
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
|
||||||
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
|
||||||
hash: sha256,
|
|
||||||
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
|
|
||||||
randomBytes,
|
|
||||||
});
|
|
||||||
|
|
||||||
// All curves expose same generic interface.
|
// All curves expose same generic interface.
|
||||||
const priv = secq256k1.utils.randomPrivateKey();
|
const priv = secq256k1.utils.randomPrivateKey();
|
||||||
secq256k1.getPublicKey(priv); // Convert private key to public.
|
secq256k1.getPublicKey(priv); // Convert private key to public.
|
||||||
@@ -318,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();
|
||||||
|
|
||||||
@@ -326,33 +378,34 @@ const fast = secq256k1.utils.precompute(8, Point.fromHex(someonesPubKey));
|
|||||||
fast.multiply(privKey); // much faster ECDH now
|
fast.multiply(privKey); // much faster ECDH now
|
||||||
```
|
```
|
||||||
|
|
||||||
`weierstrass()` returns `CurveFn`:
|
### abstract/edwards: Twisted Edwards curve
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
|
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
||||||
type CurveFn = {
|
import { Field } from '@noble/curves/abstract/modular';
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
||||||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
|
||||||
verify: (
|
|
||||||
signature: Hex | SignatureType,
|
|
||||||
msgHash: Hex,
|
|
||||||
publicKey: Hex,
|
|
||||||
opts?: { lowS?: boolean; prehash?: boolean }
|
|
||||||
) => boolean;
|
|
||||||
ProjectivePoint: ProjectivePointConstructor;
|
|
||||||
Signature: SignatureConstructor;
|
|
||||||
utils: {
|
|
||||||
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
||||||
isValidPrivateKey(key: PrivKey): boolean;
|
|
||||||
randomPrivateKey: () => Uint8Array;
|
|
||||||
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### abstract/edwards: Twisted Edwards curve
|
const Fp = Field(2n ** 255n - 19n);
|
||||||
|
const ed25519 = twistedEdwards({
|
||||||
|
a: -1n,
|
||||||
|
d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
|
||||||
|
Fp: Fp,
|
||||||
|
n: 2n ** 252n + 27742317777372353535851937790883648493n,
|
||||||
|
h: 8n,
|
||||||
|
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
||||||
|
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n,
|
||||||
|
hash: sha512,
|
||||||
|
randomBytes,
|
||||||
|
adjustScalarBytes(bytes) {
|
||||||
|
// optional; but mandatory in ed25519
|
||||||
|
bytes[0] &= 248;
|
||||||
|
bytes[31] &= 127;
|
||||||
|
bytes[31] |= 64;
|
||||||
|
return bytes;
|
||||||
|
},
|
||||||
|
} as const);
|
||||||
|
```
|
||||||
|
|
||||||
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`. You must specify `a`, `d`, field `Fp`, order `n`, cofactor `h`
|
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`. You must specify `a`, `d`, field `Fp`, order `n`, cofactor `h`
|
||||||
and coordinates `Gx`, `Gy` of generator point.
|
and coordinates `Gx`, `Gy` of generator point.
|
||||||
@@ -370,6 +423,25 @@ For EdDSA signatures, `hash` param required. `adjustScalarBytes` which instructs
|
|||||||
7. Have `isTorsionFree()`, `clearCofactor()` and `isSmallOrder()` utilities to handle torsions
|
7. Have `isTorsionFree()`, `clearCofactor()` and `isSmallOrder()` utilities to handle torsions
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
// `twistedEdwards()` returns `CurveFn` of following type:
|
||||||
|
type CurveFn = {
|
||||||
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
|
sign: (message: Hex, privateKey: Hex, context?: Hex) => Uint8Array;
|
||||||
|
verify: (sig: SigType, message: Hex, publicKey: Hex, context?: Hex) => boolean;
|
||||||
|
ExtendedPoint: ExtPointConstructor;
|
||||||
|
utils: {
|
||||||
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
getExtendedPublicKey: (key: PrivKey) => {
|
||||||
|
head: Uint8Array;
|
||||||
|
prefix: Uint8Array;
|
||||||
|
scalar: bigint;
|
||||||
|
point: PointType;
|
||||||
|
pointBytes: Uint8Array;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
interface ExtPointType extends Group<ExtPointType> {
|
interface ExtPointType extends Group<ExtPointType> {
|
||||||
readonly ex: bigint;
|
readonly ex: bigint;
|
||||||
readonly ey: bigint;
|
readonly ey: bigint;
|
||||||
@@ -392,69 +464,16 @@ interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example implementing edwards25519:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
|
||||||
import { Field, div } from '@noble/curves/abstract/modular';
|
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
|
||||||
|
|
||||||
const Fp = Field(2n ** 255n - 19n);
|
|
||||||
const ed25519 = twistedEdwards({
|
|
||||||
a: -1n,
|
|
||||||
d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
|
|
||||||
Fp,
|
|
||||||
n: 2n ** 252n + 27742317777372353535851937790883648493n,
|
|
||||||
h: 8n,
|
|
||||||
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
|
||||||
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n,
|
|
||||||
hash: sha512,
|
|
||||||
randomBytes,
|
|
||||||
adjustScalarBytes(bytes) {
|
|
||||||
// optional; but mandatory in ed25519
|
|
||||||
bytes[0] &= 248;
|
|
||||||
bytes[31] &= 127;
|
|
||||||
bytes[31] |= 64;
|
|
||||||
return bytes;
|
|
||||||
},
|
|
||||||
} as const);
|
|
||||||
```
|
|
||||||
|
|
||||||
`twistedEdwards()` returns `CurveFn` of following type:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type CurveFn = {
|
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
|
||||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
|
||||||
sign: (message: Hex, privateKey: Hex, context?: Hex) => Uint8Array;
|
|
||||||
verify: (sig: SigType, message: Hex, publicKey: Hex, context?: Hex) => boolean;
|
|
||||||
ExtendedPoint: ExtPointConstructor;
|
|
||||||
utils: {
|
|
||||||
randomPrivateKey: () => Uint8Array;
|
|
||||||
getExtendedPublicKey: (key: PrivKey) => {
|
|
||||||
head: Uint8Array;
|
|
||||||
prefix: Uint8Array;
|
|
||||||
scalar: bigint;
|
|
||||||
point: PointType;
|
|
||||||
pointBytes: Uint8Array;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### abstract/montgomery: Montgomery curve
|
### abstract/montgomery: Montgomery curve
|
||||||
|
|
||||||
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748. Proper Elliptic Curve Points are not implemented yet.
|
|
||||||
|
|
||||||
You must specify curve params `Fp`, `a`, `Gu` coordinate of u, `montgomeryBits` and `nByteLength`.
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { montgomery } from '@noble/curves/abstract/montgomery';
|
import { montgomery } from '@noble/curves/abstract/montgomery';
|
||||||
|
import { Field } from '@noble/curves/abstract/modular';
|
||||||
|
|
||||||
const x25519 = montgomery({
|
const x25519 = montgomery({
|
||||||
Fp: Field(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
|
||||||
@@ -467,6 +486,11 @@ const x25519 = montgomery({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748.
|
||||||
|
Proper Elliptic Curve Points are not implemented yet.
|
||||||
|
|
||||||
|
You must specify curve params `Fp`, `a`, `Gu` coordinate of u, `montgomeryBits` and `nByteLength`.
|
||||||
|
|
||||||
### abstract/hash-to-curve: Hashing strings to curve points
|
### abstract/hash-to-curve: Hashing strings to curve points
|
||||||
|
|
||||||
The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16).
|
The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16).
|
||||||
@@ -507,19 +531,37 @@ function expand_message_xof(
|
|||||||
): Uint8Array;
|
): Uint8Array;
|
||||||
```
|
```
|
||||||
|
|
||||||
`hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
|
`hash_to_field(msg, count, options)`
|
||||||
|
[(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
|
||||||
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
||||||
|
|
||||||
- `msg` a byte string containing the message to hash
|
|
||||||
- `count` the number of elements of F to output
|
|
||||||
- `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`.
|
|
||||||
- `p` is field prime, m=field extension (1 for prime fields)
|
|
||||||
- `k` is security target in bits (e.g. 128).
|
|
||||||
- `expand` should be `xmd` for SHA2, SHA3, BLAKE; `xof` for SHAKE, BLAKE-XOF
|
|
||||||
- `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
|
||||||
- Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
/**
|
||||||
|
* * `DST` is a domain separation tag, defined in section 2.2.5
|
||||||
|
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
||||||
|
* * `m` is extension degree (1 for prime fields)
|
||||||
|
* * `k` is the target security target in bits (e.g. 128), from section 5.1
|
||||||
|
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
||||||
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
||||||
|
*/
|
||||||
|
type UnicodeOrBytes = string | Uint8Array;
|
||||||
|
type Opts = {
|
||||||
|
DST: UnicodeOrBytes;
|
||||||
|
p: bigint;
|
||||||
|
m: number;
|
||||||
|
k: number;
|
||||||
|
expand?: 'xmd' | 'xof';
|
||||||
|
hash: CHash;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
||||||
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||||
|
* @param msg a byte string containing the message to hash
|
||||||
|
* @param count the number of elements of F to output
|
||||||
|
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
||||||
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||||
|
*/
|
||||||
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
|
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -704,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/)
|
||||||
@@ -719,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
|
||||||
|
|
||||||
|
|||||||
@@ -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`);
|
||||||
|
|||||||
77
package-lock.json
generated
77
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.8.0",
|
"version": "0.9.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.8.0",
|
"version": "0.9.1",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -15,35 +15,38 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "1.2.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.3",
|
"@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",
|
||||||
"micro-should": "0.4.0",
|
"micro-should": "0.4.0",
|
||||||
"prettier": "2.8.3",
|
"prettier": "2.8.4",
|
||||||
"typescript": "4.7.3"
|
"typescript": "5.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@noble/hashes": {
|
"node_modules/@noble/curves": {
|
||||||
"version": "1.2.0",
|
"version": "0.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz",
|
||||||
"integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==",
|
"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,15 +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/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": [
|
||||||
{
|
{
|
||||||
@@ -92,14 +95,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "~1.2.0",
|
"@noble/hashes": "~1.3.0",
|
||||||
"@scure/base": "~1.1.0"
|
"@scure/base": "~1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.11.3",
|
"version": "18.11.18",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||||
"integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==",
|
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fast-check": {
|
"node_modules/fast-check": {
|
||||||
@@ -131,9 +134,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "2.8.3",
|
"version": "2.8.4",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||||
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin-prettier.js"
|
||||||
@@ -162,16 +165,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.7.3",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz",
|
||||||
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
|
"integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.2.0"
|
"node": ">=12.20"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
package.json
18
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.8.1",
|
"version": "0.9.1",
|
||||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
"description": "Audited & minimal JS implementation of elliptic curve cryptography",
|
||||||
"files": [
|
"files": [
|
||||||
"abstract",
|
"abstract",
|
||||||
"esm",
|
"esm",
|
||||||
@@ -28,17 +28,17 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "1.2.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.3",
|
"@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",
|
||||||
"micro-should": "0.4.0",
|
"micro-should": "0.4.0",
|
||||||
"prettier": "2.8.3",
|
"prettier": "2.8.4",
|
||||||
"typescript": "4.7.3"
|
"typescript": "5.0.2"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
"bn254",
|
"bn254",
|
||||||
"pasta",
|
"pasta",
|
||||||
"bls",
|
"bls",
|
||||||
"nist",
|
"noble",
|
||||||
"ecc",
|
"ecc",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"eddsa",
|
"eddsa",
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ import * as ut from './utils.js';
|
|||||||
import { ensureBytes, FHash, Hex } from './utils.js';
|
import { ensureBytes, FHash, Hex } from './utils.js';
|
||||||
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
||||||
|
|
||||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
const _0n = BigInt(0);
|
// prettier-ignore
|
||||||
const _1n = BigInt(1);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
|
||||||
const _2n = BigInt(2);
|
|
||||||
const _8n = BigInt(8);
|
|
||||||
|
|
||||||
// Edwards curves must declare params a & d.
|
// Edwards curves must declare params a & d.
|
||||||
export type CurveType = BasicCurve<bigint> & {
|
export type CurveType = BasicCurve<bigint> & {
|
||||||
@@ -51,6 +49,8 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|||||||
readonly ey: bigint;
|
readonly ey: bigint;
|
||||||
readonly ez: bigint;
|
readonly ez: bigint;
|
||||||
readonly et: bigint;
|
readonly et: bigint;
|
||||||
|
get x(): bigint;
|
||||||
|
get y(): bigint;
|
||||||
assertValidity(): void;
|
assertValidity(): void;
|
||||||
multiply(scalar: bigint): ExtPointType;
|
multiply(scalar: bigint): ExtPointType;
|
||||||
multiplyUnsafe(scalar: bigint): ExtPointType;
|
multiplyUnsafe(scalar: bigint): ExtPointType;
|
||||||
@@ -58,6 +58,8 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|||||||
isTorsionFree(): boolean;
|
isTorsionFree(): boolean;
|
||||||
clearCofactor(): ExtPointType;
|
clearCofactor(): ExtPointType;
|
||||||
toAffine(iz?: bigint): AffinePoint<bigint>;
|
toAffine(iz?: bigint): AffinePoint<bigint>;
|
||||||
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
|
toHex(isCompressed?: boolean): string;
|
||||||
}
|
}
|
||||||
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
||||||
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||||
@@ -109,7 +111,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
||||||
return data;
|
return data;
|
||||||
}); // NOOP
|
}); // NOOP
|
||||||
const inBig = (n: bigint) => typeof n === 'bigint' && 0n < n; // n in [1..]
|
const inBig = (n: bigint) => typeof n === 'bigint' && _0n < n; // n in [1..]
|
||||||
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
||||||
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
||||||
function assertInRange(n: bigint, max: bigint) {
|
function assertInRange(n: bigint, max: bigint) {
|
||||||
@@ -297,8 +299,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||||
// It's faster, but should only be used when you don't care about
|
// It's faster, but should only be used when you don't care about
|
||||||
// an exposed private key e.g. sig verification.
|
// an exposed private key e.g. sig verification.
|
||||||
|
// Does NOT allow scalars higher than CURVE.n.
|
||||||
multiplyUnsafe(scalar: bigint): Point {
|
multiplyUnsafe(scalar: bigint): Point {
|
||||||
let n = assertGE0(scalar);
|
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
|
||||||
if (n === _0n) return I;
|
if (n === _0n) return I;
|
||||||
if (this.equals(I) || n === _1n) return this;
|
if (this.equals(I) || n === _1n) return this;
|
||||||
if (this.equals(G)) return this.wNAF(n).p;
|
if (this.equals(G)) return this.wNAF(n).p;
|
||||||
@@ -440,8 +443,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
||||||
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
||||||
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
||||||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
|
||||||
const SB = G.multiplyUnsafe(s);
|
const SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
|
||||||
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
||||||
const RkA = R.add(A.multiplyUnsafe(k));
|
const RkA = R.add(A.multiplyUnsafe(k));
|
||||||
// [8][S]B = [8]R + [8][k]A'
|
// [8][S]B = [8]R + [8][k]A'
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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');
|
||||||
@@ -275,12 +275,13 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
|||||||
while (power > _0n) {
|
while (power > _0n) {
|
||||||
if (power & _1n) p = f.mul(p, d);
|
if (power & _1n) p = f.mul(p, d);
|
||||||
d = f.sqr(d);
|
d = f.sqr(d);
|
||||||
power >>= 1n;
|
power >>= _1n;
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
|||||||
readonly px: T;
|
readonly px: T;
|
||||||
readonly py: T;
|
readonly py: T;
|
||||||
readonly pz: T;
|
readonly pz: T;
|
||||||
|
get x(): T;
|
||||||
|
get y(): T;
|
||||||
multiply(scalar: bigint): ProjPointType<T>;
|
multiply(scalar: bigint): ProjPointType<T>;
|
||||||
toAffine(iz?: T): AffinePoint<T>;
|
toAffine(iz?: T): AffinePoint<T>;
|
||||||
isTorsionFree(): boolean;
|
isTorsionFree(): boolean;
|
||||||
@@ -82,8 +84,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 +95,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 +102,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;
|
||||||
@@ -176,14 +178,31 @@ const DER = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
const _0n = BigInt(0);
|
// prettier-ignore
|
||||||
const _1n = BigInt(1);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
||||||
|
|
||||||
export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
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 +299,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;
|
||||||
}
|
}
|
||||||
@@ -348,7 +367,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
|
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
|
||||||
double() {
|
double() {
|
||||||
const { a, b } = CURVE;
|
const { a, b } = CURVE;
|
||||||
const b3 = Fp.mul(b, 3n);
|
const b3 = Fp.mul(b, _3n);
|
||||||
const { px: X1, py: Y1, pz: Z1 } = this;
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
||||||
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
||||||
let t0 = Fp.mul(X1, X1); // step 1
|
let t0 = Fp.mul(X1, X1); // step 1
|
||||||
@@ -395,7 +414,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
const { px: X2, py: Y2, pz: Z2 } = other;
|
const { px: X2, py: Y2, pz: Z2 } = other;
|
||||||
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
||||||
const a = CURVE.a;
|
const a = CURVE.a;
|
||||||
const b3 = Fp.mul(CURVE.b, 3n);
|
const b3 = Fp.mul(CURVE.b, _3n);
|
||||||
let t0 = Fp.mul(X1, X2); // step 1
|
let t0 = Fp.mul(X1, X2); // step 1
|
||||||
let t1 = Fp.mul(Y1, Y2);
|
let t1 = Fp.mul(Y1, Y2);
|
||||||
let t2 = Fp.mul(Z1, Z2);
|
let t2 = Fp.mul(Z1, Z2);
|
||||||
@@ -563,7 +582,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 +593,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 +672,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,22 +1074,21 @@ 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;
|
||||||
for (let o = q - 1n; o % 2n === 0n; o /= 2n) l += 1n;
|
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
||||||
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
||||||
const c2 = (q - 1n) / 2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
||||||
const c3 = (c2 - 1n) / 2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
||||||
const c4 = 2n ** c1 - 1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
const c4 = _2n ** c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
||||||
const c5 = 2n ** (c1 - 1n); // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
const c5 = _2n ** (c1 - _1n); // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
||||||
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
||||||
const c7 = Fp.pow(Z, (c2 + 1n) / 2n); // 7. c7 = Z^((c2 + 1) / 2)
|
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
||||||
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
||||||
let tv1 = c6; // 1. tv1 = c6
|
let tv1 = c6; // 1. tv1 = c6
|
||||||
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
||||||
@@ -1090,7 +1108,7 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
|
|||||||
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
||||||
// 17. for i in (c1, c1 - 1, ..., 2):
|
// 17. for i in (c1, c1 - 1, ..., 2):
|
||||||
for (let i = c1; i > 1; i--) {
|
for (let i = c1; i > 1; i--) {
|
||||||
let tv5 = 2n ** (i - 2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
|
let tv5 = _2n ** (i - _2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
|
||||||
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
||||||
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
||||||
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
||||||
@@ -1101,9 +1119,9 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
|
|||||||
}
|
}
|
||||||
return { isValid: isQR, value: tv3 };
|
return { isValid: isQR, value: tv3 };
|
||||||
};
|
};
|
||||||
if (Fp.ORDER % 4n === 3n) {
|
if (Fp.ORDER % _4n === _3n) {
|
||||||
// sqrt_ratio_3mod4(u, v)
|
// sqrt_ratio_3mod4(u, v)
|
||||||
const c1 = (Fp.ORDER - 3n) / 4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||||
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
||||||
sqrtRatio = (u: T, v: T) => {
|
sqrtRatio = (u: T, v: T) => {
|
||||||
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
||||||
@@ -1119,12 +1137,12 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// No curves uses that
|
// No curves uses that
|
||||||
// if (Fp.ORDER % 8n === 5n) // sqrt_ratio_5mod8
|
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
||||||
return sqrtRatio;
|
return sqrtRatio;
|
||||||
}
|
}
|
||||||
// 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;
|
||||||
|
|||||||
313
src/bls12-381.ts
313
src/bls12-381.ts
@@ -69,16 +69,23 @@ import {
|
|||||||
} from './abstract/weierstrass.js';
|
} from './abstract/weierstrass.js';
|
||||||
import { isogenyMap } from './abstract/hash-to-curve.js';
|
import { isogenyMap } from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
||||||
|
const _8n = BigInt(8),
|
||||||
|
_16n = BigInt(16);
|
||||||
|
|
||||||
// CURVE FIELDS
|
// CURVE FIELDS
|
||||||
// Finite field over p.
|
// Finite field over p.
|
||||||
const Fp =
|
const Fp = mod.Field(
|
||||||
mod.Fp(
|
BigInt(
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
|
||||||
);
|
)
|
||||||
|
);
|
||||||
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(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
|
||||||
|
|
||||||
// Fp₂ over complex plane
|
// Fp₂ over complex plane
|
||||||
type BigintTuple = [bigint, bigint];
|
type BigintTuple = [bigint, bigint];
|
||||||
@@ -121,10 +128,11 @@ type Fp2Utils = {
|
|||||||
// h2q
|
// h2q
|
||||||
// NOTE: ORDER was wrong!
|
// NOTE: ORDER was wrong!
|
||||||
const FP2_ORDER =
|
const FP2_ORDER =
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn **
|
BigInt(
|
||||||
2n;
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
|
||||||
|
) ** _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),
|
||||||
@@ -175,7 +183,7 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
|
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
|
||||||
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
|
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
|
||||||
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
|
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
|
||||||
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + 8n) / 16n);
|
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + _8n) / _16n);
|
||||||
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
|
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
|
||||||
const R = FP2_ROOTS_OF_UNITY;
|
const R = FP2_ROOTS_OF_UNITY;
|
||||||
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
|
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
|
||||||
@@ -193,10 +201,10 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// Same as sgn0_fp2 in draft-irtf-cfrg-hash-to-curve-16
|
// Same as sgn0_fp2 in draft-irtf-cfrg-hash-to-curve-16
|
||||||
isOdd: (x: Fp2) => {
|
isOdd: (x: Fp2) => {
|
||||||
const { re: x0, im: x1 } = Fp2.reim(x);
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||||
const sign_0 = x0 % 2n;
|
const sign_0 = x0 % _2n;
|
||||||
const zero_0 = x0 === 0n;
|
const zero_0 = x0 === _0n;
|
||||||
const sign_1 = x1 % 2n;
|
const sign_1 = x1 % _2n;
|
||||||
return BigInt(sign_0 || (zero_0 && sign_1)) == 1n;
|
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
|
||||||
},
|
},
|
||||||
// Bytes util
|
// Bytes util
|
||||||
fromBytes(b: Uint8Array): Fp2 {
|
fromBytes(b: Uint8Array): Fp2 {
|
||||||
@@ -216,8 +224,8 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// multiply by u + 1
|
// multiply by u + 1
|
||||||
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
|
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
|
||||||
multiplyByB: ({ c0, c1 }) => {
|
multiplyByB: ({ c0, c1 }) => {
|
||||||
let t0 = Fp.mul(c0, 4n); // 4 * c0
|
let t0 = Fp.mul(c0, _4n); // 4 * c0
|
||||||
let t1 = Fp.mul(c1, 4n); // 4 * c1
|
let t1 = Fp.mul(c1, _4n); // 4 * c1
|
||||||
// (T0-T1) + (T0+T1)*i
|
// (T0-T1) + (T0+T1)*i
|
||||||
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
|
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
|
||||||
},
|
},
|
||||||
@@ -234,33 +242,36 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// Finite extension field over irreducible polynominal.
|
// Finite extension field over irreducible polynominal.
|
||||||
// Fp(u) / (u² - β) where β = -1
|
// Fp(u) / (u² - β) where β = -1
|
||||||
const FP2_FROBENIUS_COEFFICIENTS = [
|
const FP2_FROBENIUS_COEFFICIENTS = [
|
||||||
0x1n,
|
BigInt('0x1'),
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
|
BigInt(
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
|
||||||
|
),
|
||||||
].map((item) => Fp.create(item));
|
].map((item) => Fp.create(item));
|
||||||
|
|
||||||
// For Fp2 roots of unity.
|
// For Fp2 roots of unity.
|
||||||
const rv1 =
|
const rv1 = BigInt(
|
||||||
0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n;
|
'0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
|
||||||
|
);
|
||||||
// const ev1 =
|
// const ev1 =
|
||||||
// 0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90n;
|
// BigInt('0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90');
|
||||||
// const ev2 =
|
// const ev2 =
|
||||||
// 0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5n;
|
// BigInt('0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5');
|
||||||
// const ev3 =
|
// const ev3 =
|
||||||
// 0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17n;
|
// BigInt('0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17');
|
||||||
// const ev4 =
|
// const ev4 =
|
||||||
// 0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1n;
|
// BigInt('0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1');
|
||||||
|
|
||||||
// Eighth roots of unity, used for computing square roots in Fp2.
|
// Eighth roots of unity, used for computing square roots in Fp2.
|
||||||
// To verify or re-calculate:
|
// To verify or re-calculate:
|
||||||
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
|
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
|
||||||
const FP2_ROOTS_OF_UNITY = [
|
const FP2_ROOTS_OF_UNITY = [
|
||||||
[1n, 0n],
|
[_1n, _0n],
|
||||||
[rv1, -rv1],
|
[rv1, -rv1],
|
||||||
[0n, 1n],
|
[_0n, _1n],
|
||||||
[rv1, rv1],
|
[rv1, rv1],
|
||||||
[-1n, 0n],
|
[-_1n, _0n],
|
||||||
[-rv1, rv1],
|
[-rv1, rv1],
|
||||||
[0n, -1n],
|
[_0n, -_1n],
|
||||||
[-rv1, -rv1],
|
[-rv1, -rv1],
|
||||||
].map((pair) => Fp2.fromBigTuple(pair));
|
].map((pair) => Fp2.fromBigTuple(pair));
|
||||||
// eta values, used for computing sqrt(g(X1(t)))
|
// eta values, used for computing sqrt(g(X1(t)))
|
||||||
@@ -314,8 +325,8 @@ const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
|
|||||||
};
|
};
|
||||||
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
||||||
let t0 = Fp2.sqr(c0); // c0²
|
let t0 = Fp2.sqr(c0); // c0²
|
||||||
let t1 = Fp2.mul(Fp2.mul(c0, c1), 2n); // 2 * c0 * c1
|
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
|
||||||
let t3 = Fp2.mul(Fp2.mul(c1, c2), 2n); // 2 * c1 * c2
|
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
|
||||||
let t4 = Fp2.sqr(c2); // c2²
|
let t4 = Fp2.sqr(c2); // c2²
|
||||||
return {
|
return {
|
||||||
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
||||||
@@ -333,7 +344,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,
|
||||||
@@ -440,46 +451,64 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FP6_FROBENIUS_COEFFICIENTS_1 = [
|
const FP6_FROBENIUS_COEFFICIENTS_1 = [
|
||||||
[0x1n, 0x0n],
|
[BigInt('0x1'), BigInt('0x0')],
|
||||||
[
|
[
|
||||||
0x0n,
|
BigInt('0x0'),
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[0x0n, 0x1n],
|
[BigInt('0x0'), BigInt('0x1')],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x0n,
|
BigInt('0x0'),
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
].map((pair) => Fp2.fromBigTuple(pair));
|
].map((pair) => Fp2.fromBigTuple(pair));
|
||||||
const FP6_FROBENIUS_COEFFICIENTS_2 = [
|
const FP6_FROBENIUS_COEFFICIENTS_2 = [
|
||||||
[0x1n, 0x0n],
|
[BigInt('0x1'), BigInt('0x0')],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaadn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffffn,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
].map((pair) => Fp2.fromBigTuple(pair));
|
].map((pair) => Fp2.fromBigTuple(pair));
|
||||||
|
|
||||||
@@ -488,7 +517,7 @@ const FP6_FROBENIUS_COEFFICIENTS_2 = [
|
|||||||
// Fp₆(w) / (w² - γ) where γ = v
|
// Fp₆(w) / (w² - γ) where γ = v
|
||||||
type Fp12 = { c0: Fp6; c1: Fp6 };
|
type Fp12 = { c0: Fp6; c1: Fp6 };
|
||||||
// The BLS parameter x for BLS12-381
|
// The BLS parameter x for BLS12-381
|
||||||
const BLS_X = 0xd201000000010000n;
|
const BLS_X = BigInt('0xd201000000010000');
|
||||||
const BLS_X_LEN = bitLen(BLS_X);
|
const BLS_X_LEN = bitLen(BLS_X);
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -545,7 +574,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,
|
||||||
@@ -646,14 +675,14 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
|||||||
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
||||||
return {
|
return {
|
||||||
c0: Fp6.create({
|
c0: Fp6.create({
|
||||||
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), 2n), t3), // 2 * (T3 - c0c0) + T3
|
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
|
||||||
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), 2n), t5), // 2 * (T5 - c0c1) + T5
|
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
|
||||||
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), 2n), t7),
|
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
|
||||||
}), // 2 * (T7 - c0c2) + T7
|
}), // 2 * (T7 - c0c2) + T7
|
||||||
c1: Fp6.create({
|
c1: Fp6.create({
|
||||||
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), 2n), t9), // 2 * (T9 + c1c0) + T9
|
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
|
||||||
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), 2n), t4), // 2 * (T4 + c1c1) + T4
|
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
|
||||||
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), 2n), t6),
|
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
|
||||||
}),
|
}),
|
||||||
}; // 2 * (T6 + c1c2) + T6
|
}; // 2 * (T6 + c1c2) + T6
|
||||||
},
|
},
|
||||||
@@ -688,50 +717,84 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
const FP12_FROBENIUS_COEFFICIENTS = [
|
const FP12_FROBENIUS_COEFFICIENTS = [
|
||||||
[0x1n, 0x0n],
|
[BigInt('0x1'), BigInt('0x0')],
|
||||||
[
|
[
|
||||||
0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8n,
|
BigInt(
|
||||||
0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3n,
|
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffffn,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2n,
|
BigInt(
|
||||||
0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n,
|
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995n,
|
BigInt(
|
||||||
0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116n,
|
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3n,
|
BigInt(
|
||||||
0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8n,
|
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n,
|
BigInt(
|
||||||
0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2n,
|
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaadn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116n,
|
BigInt(
|
||||||
0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995n,
|
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
].map((n) => Fp2.fromBigTuple(n));
|
].map((n) => Fp2.fromBigTuple(n));
|
||||||
// END OF CURVE FIELDS
|
// END OF CURVE FIELDS
|
||||||
@@ -887,17 +950,21 @@ const isogenyMapG1 = isogenyMap(
|
|||||||
|
|
||||||
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
|
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
|
||||||
const G2_SWU = mapToCurveSimpleSWU(Fp2, {
|
const G2_SWU = mapToCurveSimpleSWU(Fp2, {
|
||||||
A: Fp2.create({ c0: Fp.create(0n), c1: Fp.create(240n) }), // A' = 240 * I
|
A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(240n) }), // A' = 240 * I
|
||||||
B: Fp2.create({ c0: Fp.create(1012n), c1: Fp.create(1012n) }), // B' = 1012 * (1 + I)
|
B: Fp2.create({ c0: Fp.create(1012n), c1: Fp.create(1012n) }), // B' = 1012 * (1 + I)
|
||||||
Z: Fp2.create({ c0: Fp.create(-2n), c1: Fp.create(-1n) }), // Z: -(2 + I)
|
Z: Fp2.create({ c0: Fp.create(-2n), c1: Fp.create(-1n) }), // Z: -(2 + I)
|
||||||
});
|
});
|
||||||
// Optimized SWU Map - Fp to G1
|
// Optimized SWU Map - Fp to G1
|
||||||
const G1_SWU = mapToCurveSimpleSWU(Fp, {
|
const G1_SWU = mapToCurveSimpleSWU(Fp, {
|
||||||
A: Fp.create(
|
A: Fp.create(
|
||||||
0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1dn
|
BigInt(
|
||||||
|
'0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d'
|
||||||
|
)
|
||||||
),
|
),
|
||||||
B: Fp.create(
|
B: Fp.create(
|
||||||
0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0n
|
BigInt(
|
||||||
|
'0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0'
|
||||||
|
)
|
||||||
),
|
),
|
||||||
Z: Fp.create(11n),
|
Z: Fp.create(11n),
|
||||||
});
|
});
|
||||||
@@ -922,8 +989,9 @@ function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
|||||||
}
|
}
|
||||||
// Ψ²(P) endomorphism
|
// Ψ²(P) endomorphism
|
||||||
// 1 / F2(2)^((p-1)/3) in GF(p²)
|
// 1 / F2(2)^((p-1)/3) in GF(p²)
|
||||||
const PSI2_C1 =
|
const PSI2_C1 = BigInt(
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn;
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
);
|
||||||
|
|
||||||
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
||||||
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
|
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
|
||||||
@@ -1000,14 +1068,18 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
G1: {
|
G1: {
|
||||||
Fp,
|
Fp,
|
||||||
// cofactor; (z - 1)²/3
|
// cofactor; (z - 1)²/3
|
||||||
h: 0x396c8c005555e1568c00aaab0000aaabn,
|
h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
|
||||||
// generator's coordinates
|
// generator's coordinates
|
||||||
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
||||||
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
||||||
Gx: 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbn,
|
Gx: BigInt(
|
||||||
Gy: 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1n,
|
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
||||||
|
),
|
||||||
|
Gy: BigInt(
|
||||||
|
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
||||||
|
),
|
||||||
a: Fp.ZERO,
|
a: Fp.ZERO,
|
||||||
b: 4n,
|
b: _4n,
|
||||||
htfDefaults: { ...htfDefaults, m: 1 },
|
htfDefaults: { ...htfDefaults, m: 1 },
|
||||||
wrapPrivateKey: true,
|
wrapPrivateKey: true,
|
||||||
allowInfinityPoint: true,
|
allowInfinityPoint: true,
|
||||||
@@ -1017,8 +1089,9 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
// https://eprint.iacr.org/2021/1130.pdf
|
// https://eprint.iacr.org/2021/1130.pdf
|
||||||
isTorsionFree: (c, point): boolean => {
|
isTorsionFree: (c, point): boolean => {
|
||||||
// φ endomorphism
|
// φ endomorphism
|
||||||
const cubicRootOfUnityModP =
|
const cubicRootOfUnityModP = BigInt(
|
||||||
0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen;
|
'0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
);
|
||||||
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
|
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
|
||||||
|
|
||||||
// todo: unroll
|
// todo: unroll
|
||||||
@@ -1028,7 +1101,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
|
|
||||||
// https://eprint.iacr.org/2019/814.pdf
|
// https://eprint.iacr.org/2019/814.pdf
|
||||||
// (z² − 1)/3
|
// (z² − 1)/3
|
||||||
// const c1 = 0x396c8c005555e1560000000055555555n;
|
// const c1 = BigInt('0x396c8c005555e1560000000055555555');
|
||||||
// const P = this;
|
// const P = this;
|
||||||
// const S = P.sigma();
|
// const S = P.sigma();
|
||||||
// const Q = S.double();
|
// const Q = S.double();
|
||||||
@@ -1054,13 +1127,13 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const compressedValue = bytesToNumberBE(bytes);
|
const compressedValue = bytesToNumberBE(bytes);
|
||||||
const bflag = bitGet(compressedValue, I_BIT_POS);
|
const bflag = bitGet(compressedValue, I_BIT_POS);
|
||||||
// Zero
|
// Zero
|
||||||
if (bflag === 1n) return { x: 0n, y: 0n };
|
if (bflag === _1n) return { x: _0n, y: _0n };
|
||||||
const x = Fp.create(compressedValue & Fp.MASK);
|
const x = Fp.create(compressedValue & Fp.MASK);
|
||||||
const right = Fp.add(Fp.pow(x, 3n), Fp.create(bls12_381.CURVE.G1.b)); // y² = x³ + b
|
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.CURVE.G1.b)); // y² = x³ + b
|
||||||
let y = Fp.sqrt(right);
|
let y = Fp.sqrt(right);
|
||||||
if (!y) throw new Error('Invalid compressed G1 point');
|
if (!y) throw new Error('Invalid compressed G1 point');
|
||||||
const aflag = bitGet(compressedValue, C_BIT_POS);
|
const aflag = bitGet(compressedValue, C_BIT_POS);
|
||||||
if ((y * 2n) / P !== aflag) y = Fp.neg(y);
|
if ((y * _2n) / P !== aflag) y = Fp.neg(y);
|
||||||
return { x: Fp.create(x), y: Fp.create(y) };
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
} else if (bytes.length === 96) {
|
} else if (bytes.length === 96) {
|
||||||
// Check if the infinity flag is set
|
// Check if the infinity flag is set
|
||||||
@@ -1079,7 +1152,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
if (isZero) return COMPRESSED_ZERO.slice();
|
if (isZero) return COMPRESSED_ZERO.slice();
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
let num;
|
let num;
|
||||||
num = bitSet(x, C_BIT_POS, Boolean((y * 2n) / P)); // set aflag
|
num = bitSet(x, C_BIT_POS, Boolean((y * _2n) / P)); // set aflag
|
||||||
num = bitSet(num, S_BIT_POS, true);
|
num = bitSet(num, S_BIT_POS, true);
|
||||||
return numberToBytesBE(num, Fp.BYTES);
|
return numberToBytesBE(num, Fp.BYTES);
|
||||||
} else {
|
} else {
|
||||||
@@ -1100,21 +1173,33 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
G2: {
|
G2: {
|
||||||
Fp: Fp2,
|
Fp: Fp2,
|
||||||
// cofactor
|
// cofactor
|
||||||
h: 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5n,
|
h: BigInt(
|
||||||
|
'0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'
|
||||||
|
),
|
||||||
Gx: Fp2.fromBigTuple([
|
Gx: Fp2.fromBigTuple([
|
||||||
0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8n,
|
BigInt(
|
||||||
0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7en,
|
'0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
// y =
|
// y =
|
||||||
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
|
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
|
||||||
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
||||||
Gy: Fp2.fromBigTuple([
|
Gy: Fp2.fromBigTuple([
|
||||||
0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801n,
|
BigInt(
|
||||||
0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79ben,
|
'0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
a: Fp2.ZERO,
|
a: Fp2.ZERO,
|
||||||
b: Fp2.fromBigTuple([4n, 4n]),
|
b: Fp2.fromBigTuple([4n, _4n]),
|
||||||
hEff: 0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551n,
|
hEff: BigInt(
|
||||||
|
'0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
|
||||||
|
),
|
||||||
htfDefaults: { ...htfDefaults },
|
htfDefaults: { ...htfDefaults },
|
||||||
wrapPrivateKey: true,
|
wrapPrivateKey: true,
|
||||||
allowInfinityPoint: true,
|
allowInfinityPoint: true,
|
||||||
@@ -1175,9 +1260,9 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const x_1 = slc(bytes, 0, L);
|
const x_1 = slc(bytes, 0, L);
|
||||||
const x_0 = slc(bytes, L, 2 * L);
|
const x_0 = slc(bytes, L, 2 * L);
|
||||||
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
||||||
const right = Fp2.add(Fp2.pow(x, 3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
||||||
let y = Fp2.sqrt(right);
|
let y = Fp2.sqrt(right);
|
||||||
const Y_bit = y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P ? 1n : 0n;
|
const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
|
||||||
y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
|
y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
|
||||||
return { x, y };
|
return { x, y };
|
||||||
} else if (bytes.length === 192 && !bitC) {
|
} else if (bytes.length === 192 && !bitC) {
|
||||||
@@ -1200,7 +1285,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||||
const flag = Boolean(y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P);
|
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
|
||||||
// set compressed & sign bits (looks like different offsets than for G1/Fp?)
|
// set compressed & sign bits (looks like different offsets than for G1/Fp?)
|
||||||
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
|
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
|
||||||
x_1 = bitSet(x_1, S_BIT_POS, true);
|
x_1 = bitSet(x_1, S_BIT_POS, true);
|
||||||
@@ -1229,12 +1314,12 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const z2 = bytesToNumberBE(hex.slice(half));
|
const z2 = bytesToNumberBE(hex.slice(half));
|
||||||
// Indicates the infinity point
|
// Indicates the infinity point
|
||||||
const bflag1 = bitGet(z1, I_BIT_POS);
|
const bflag1 = bitGet(z1, I_BIT_POS);
|
||||||
if (bflag1 === 1n) return bls12_381.G2.ProjectivePoint.ZERO;
|
if (bflag1 === _1n) return bls12_381.G2.ProjectivePoint.ZERO;
|
||||||
|
|
||||||
const x1 = Fp.create(z1 & Fp.MASK);
|
const x1 = Fp.create(z1 & Fp.MASK);
|
||||||
const x2 = Fp.create(z2);
|
const x2 = Fp.create(z2);
|
||||||
const x = Fp2.create({ c0: x2, c1: x1 });
|
const x = Fp2.create({ c0: x2, c1: x1 });
|
||||||
const y2 = Fp2.add(Fp2.pow(x, 3n), bls12_381.CURVE.G2.b); // y² = x³ + 4
|
const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.CURVE.G2.b); // y² = x³ + 4
|
||||||
// The slow part
|
// The slow part
|
||||||
let y = Fp2.sqrt(y2);
|
let y = Fp2.sqrt(y2);
|
||||||
if (!y) throw new Error('Failed to find a square root');
|
if (!y) throw new Error('Failed to find a square root');
|
||||||
@@ -1243,8 +1328,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
// If y1 happens to be zero, then use the bit of y0
|
// If y1 happens to be zero, then use the bit of y0
|
||||||
const { re: y0, im: y1 } = Fp2.reim(y);
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
const aflag1 = bitGet(z1, 381);
|
const aflag1 = bitGet(z1, 381);
|
||||||
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
|
||||||
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
|
||||||
if (isGreater || isZero) y = Fp2.neg(y);
|
if (isGreater || isZero) y = Fp2.neg(y);
|
||||||
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
@@ -1258,8 +1343,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const a = point.toAffine();
|
const a = point.toAffine();
|
||||||
const { re: x0, im: x1 } = Fp2.reim(a.x);
|
const { re: x0, im: x1 } = Fp2.reim(a.x);
|
||||||
const { re: y0, im: y1 } = Fp2.reim(a.y);
|
const { re: y0, im: y1 } = Fp2.reim(a.y);
|
||||||
const tmp = y1 > 0n ? y1 * 2n : y0 * 2n;
|
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
|
||||||
const aflag1 = Boolean((tmp / Fp.ORDER) & 1n);
|
const aflag1 = Boolean((tmp / Fp.ORDER) & _1n);
|
||||||
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
||||||
const z2 = x0;
|
const z2 = x0;
|
||||||
return concatB(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
|
return concatB(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -204,7 +204,7 @@ function map_to_curve_elligator2_curve25519(u: bigint) {
|
|||||||
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
||||||
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
|
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
|
||||||
y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
|
y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
|
||||||
return { xMn: xn, xMd: xd, yMn: y, yMd: 1n }; // 39. return (xn, xd, y, 1)
|
return { xMn: xn, xMd: xd, yMn: y, yMd: _1n }; // 39. return (xn, xd, y, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); // sgn0(c1) MUST equal 0
|
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); // sgn0(c1) MUST equal 0
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Fp = Field(ed448P, 456, true);
|
const Fp = Field(ed448P, 456, true);
|
||||||
|
const _4n = BigInt(4);
|
||||||
|
|
||||||
const ED448_DEF = {
|
const ED448_DEF = {
|
||||||
// Param: a
|
// Param: a
|
||||||
@@ -195,10 +196,10 @@ function map_to_curve_elligator2_edwards448(u: bigint) {
|
|||||||
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||||
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||||
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||||
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
xEn = Fp.mul(xEn, _4n); // 12. xEn = xEn * 4
|
||||||
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||||
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||||
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
let tv3 = Fp.mul(yn2, _4n); // 15. tv3 = 4 * yn2
|
||||||
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||||
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||||
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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(
|
||||||
{
|
{
|
||||||
@@ -132,7 +131,7 @@ function lift_x(x: bigint): PointType<bigint> {
|
|||||||
const xx = modP(x * x);
|
const xx = modP(x * x);
|
||||||
const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.
|
const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.
|
||||||
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||||
if (y % 2n !== 0n) y = modP(-y); // Return the unique point P such that x(P) = x and
|
if (y % _2n !== _0n) y = modP(-y); // Return the unique point P such that x(P) = x and
|
||||||
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
||||||
p.assertValidity();
|
p.assertValidity();
|
||||||
return p;
|
return p;
|
||||||
@@ -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'),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
"outDir": ".",
|
"outDir": ".",
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"lib": ["es2020"], // Set explicitly to remove DOM
|
"lib": ["es2020"], // Set explicitly to remove DOM
|
||||||
"module": "es6",
|
"module": "commonjs",
|
||||||
"moduleResolution": "node16",
|
"moduleResolution": "node",
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user