Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8b2e91f74 | ||
|
|
9ee694ae23 | ||
|
|
6bc4b35cf4 | ||
|
|
0163b63532 | ||
|
|
7e825520f1 | ||
|
|
d739297b2c | ||
|
|
285aa6375d | ||
|
|
8c77331ef2 | ||
|
|
669641e0a3 | ||
|
|
68dd57ed31 | ||
|
|
a9fdd6df9f | ||
|
|
d485d8b0e6 | ||
|
|
0fdd763dc7 | ||
|
|
586e2ad5fb | ||
|
|
ed81707bdc | ||
|
|
6d56b2d78e | ||
|
|
8397241a8f | ||
|
|
001d0cc24a | ||
|
|
ce9d165657 | ||
|
|
2902b0299a | ||
|
|
e1cb8549e8 | ||
|
|
26ebb5dcce | ||
|
|
8b2863aeac | ||
|
|
b1f50d9364 | ||
|
|
b81d74d3cb | ||
|
|
d5fe537159 | ||
|
|
cde1d5c488 |
220
README.md
220
README.md
@@ -5,25 +5,25 @@ Audited & minimal JS implementation of elliptic curve cryptography.
|
|||||||
- **noble** family, zero dependencies
|
- **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
|
||||||
- #️⃣ [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
|
- #️⃣ [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. Wycheproof vectors included
|
- 🔍 Unique tests ensure correctness with Wycheproof vectors and [cryptofuzz](https://github.com/guidovranken/cryptofuzz) differential fuzzing
|
||||||
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
||||||
|
|
||||||
Package consists of two parts:
|
Package consists of two parts:
|
||||||
|
|
||||||
1. [Abstract](#abstract-api), zero-dependency EC algorithms
|
1. [Abstract](#abstract-api), zero-dependency EC algorithms
|
||||||
2. [Implementations](#implementations), utilizing one dependency `@noble/hashes`, providing ready-to-use:
|
2. [Implementations](#implementations), utilizing one dependency `@noble/hashes`, providing ready-to-use:
|
||||||
- NIST curves secp192r1/P192, secp224r1/P224, secp256r1/P256, secp384r1/P384, secp521r1/P521
|
- NIST curves secp256r1/P256, secp384r1/P384, secp521r1/P521
|
||||||
- SECG curve secp256k1
|
- SECG curve secp256k1
|
||||||
- ed25519/curve25519/x25519/ristretto255, edwards448/curve448/x448 RFC7748 / RFC8032 / ZIP215 stuff
|
- 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
|
- pairing-friendly curves bls12-381, bn254
|
||||||
|
|
||||||
Check out [Upgrading](#upgrading) if you've previously used single-feature noble packages
|
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)).
|
([secp256k1](https://github.com/paulmillr/noble-secp256k1), [ed25519](https://github.com/paulmillr/noble-ed25519)).
|
||||||
See [In the wild](#in-the-wild) for 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
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ Use NPM for browser / node.js:
|
|||||||
|
|
||||||
> npm install @noble/curves
|
> npm install @noble/curves
|
||||||
|
|
||||||
For [Deno](https://deno.land), use it with npm specifier. 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 `import * from '@noble/curves'`.
|
||||||
@@ -64,12 +64,8 @@ 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;
|
||||||
|
|
||||||
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); // keys & other inputs can be Uint8Array-s or hex strings
|
||||||
|
|
||||||
// Follows hash-to-curve specification to encode arbitrary hashes to EC points
|
|
||||||
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1';
|
|
||||||
hashToCurve('0102abcd');
|
|
||||||
```
|
```
|
||||||
|
|
||||||
All curves:
|
All curves:
|
||||||
@@ -125,7 +121,7 @@ import { ed25519ctx, ed25519ph } from '@noble/curves/ed25519';
|
|||||||
import { x25519 } from '@noble/curves/ed25519';
|
import { x25519 } from '@noble/curves/ed25519';
|
||||||
const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
|
const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
|
||||||
const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
|
const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
|
||||||
x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub);
|
x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
|
||||||
x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
|
x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
|
||||||
|
|
||||||
// hash-to-curve
|
// hash-to-curve
|
||||||
@@ -180,9 +176,9 @@ const signatures3 = privateKeys.map((p, i) => bls.sign(messages[i], p));
|
|||||||
const aggSignature3 = bls.aggregateSignatures(signatures3);
|
const aggSignature3 = bls.aggregateSignatures(signatures3);
|
||||||
const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys);
|
const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys);
|
||||||
console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
|
console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
|
||||||
|
// bls.pairing(PointG1, PointG2) // pairings
|
||||||
|
|
||||||
// Pairings
|
// hash-to-curve examples can be seen below
|
||||||
// bls.pairing(PointG1, PointG2)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Abstract API
|
## Abstract API
|
||||||
@@ -190,7 +186,7 @@ console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
|
|||||||
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 bigints over finite fields,
|
||||||
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/).
|
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/).
|
||||||
Precomputes are enabled for weierstrass and edwards BASE points of a curve. You could precompute any
|
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.
|
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:
|
||||||
|
|
||||||
@@ -216,7 +212,7 @@ For this you will need `hmac` & `hash`, which in our implementations is provided
|
|||||||
If you're using different hashing library, make sure to wrap it in the following interface:
|
If you're using different hashing library, make sure to wrap it in the following interface:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export type CHash = {
|
type CHash = {
|
||||||
(message: Uint8Array): Uint8Array;
|
(message: Uint8Array): Uint8Array;
|
||||||
blockLen: number;
|
blockLen: number;
|
||||||
outputLen: number;
|
outputLen: number;
|
||||||
@@ -235,7 +231,7 @@ export type CHash = {
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// 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
|
||||||
export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
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;
|
||||||
@@ -251,7 +247,7 @@ export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
|||||||
toHex(isCompressed?: boolean): string;
|
toHex(isCompressed?: boolean): string;
|
||||||
}
|
}
|
||||||
// Static methods for 3d XYZ points
|
// Static methods for 3d XYZ points
|
||||||
export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
||||||
new (x: T, y: T, z: T): ProjPointType<T>;
|
new (x: T, y: T, z: T): ProjPointType<T>;
|
||||||
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
|
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
|
||||||
fromHex(hex: Hex): ProjPointType<T>;
|
fromHex(hex: Hex): ProjPointType<T>;
|
||||||
@@ -262,7 +258,7 @@ export 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
|
||||||
export interface SignatureType {
|
interface SignatureType {
|
||||||
readonly r: bigint;
|
readonly r: bigint;
|
||||||
readonly s: bigint;
|
readonly s: bigint;
|
||||||
readonly recovery?: number;
|
readonly recovery?: number;
|
||||||
@@ -274,9 +270,14 @@ export interface SignatureType {
|
|||||||
toCompactRawBytes(): Uint8Array;
|
toCompactRawBytes(): Uint8Array;
|
||||||
toCompactHex(): string;
|
toCompactHex(): string;
|
||||||
// DER-encoded
|
// DER-encoded
|
||||||
toDERRawBytes(isCompressed?: boolean): Uint8Array;
|
toDERRawBytes(): Uint8Array;
|
||||||
toDERHex(isCompressed?: boolean): string;
|
toDERHex(): string;
|
||||||
}
|
}
|
||||||
|
type SignatureConstructor = {
|
||||||
|
new (r: bigint, s: bigint): SignatureType;
|
||||||
|
fromCompact(hex: Hex): SignatureType;
|
||||||
|
fromDER(hex: Hex): SignatureType;
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Example implementing [secq256k1](https://personaelabs.org/posts/spartan-ecdsa) (NOT secp256k1)
|
Example implementing [secq256k1](https://personaelabs.org/posts/spartan-ecdsa) (NOT secp256k1)
|
||||||
@@ -307,24 +308,30 @@ secq256k1.getPublicKey(priv); // Convert private key to public.
|
|||||||
const sig = secq256k1.sign(msg, priv); // Sign msg with private key.
|
const sig = secq256k1.sign(msg, priv); // Sign msg with private key.
|
||||||
secq256k1.verify(sig, msg, priv); // Verify if sig is correct.
|
secq256k1.verify(sig, msg, priv); // Verify if sig is correct.
|
||||||
|
|
||||||
const point = secq256k1.Point.BASE; // Elliptic curve Point class and BASE point static var.
|
const Point = secq256k1.ProjectivePoint;
|
||||||
|
const point = Point.BASE; // Elliptic curve Point class and BASE point static var.
|
||||||
point.add(point).equals(point.double()); // add(), equals(), double() methods
|
point.add(point).equals(point.double()); // add(), equals(), double() methods
|
||||||
point.subtract(point).equals(secq256k1.Point.ZERO); // subtract() method, ZERO static var
|
point.subtract(point).equals(Point.ZERO); // subtract() method, ZERO static var
|
||||||
point.negate(); // Flips point over x/y coordinate.
|
point.negate(); // Flips point over x/y coordinate.
|
||||||
point.multiply(31415n); // Multiplication of Point by scalar.
|
point.multiply(31415n); // Multiplication of Point by scalar.
|
||||||
|
|
||||||
point.assertValidity(); // Checks for being on-curve
|
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.Fp.mod();
|
secq256k1.CURVE.Fp.mod();
|
||||||
secq256k1.CURVE.hash();
|
secq256k1.CURVE.hash();
|
||||||
|
|
||||||
|
// precomputes
|
||||||
|
const fast = secq256k1.utils.precompute(8, Point.fromHex(someonesPubKey));
|
||||||
|
fast.multiply(privKey); // much faster ECDH now
|
||||||
```
|
```
|
||||||
|
|
||||||
`weierstrass()` returns `CurveFn`:
|
`weierstrass()` returns `CurveFn`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export type CurveFn = {
|
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
|
||||||
|
type CurveFn = {
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||||
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||||
@@ -338,8 +345,10 @@ export type CurveFn = {
|
|||||||
ProjectivePoint: ProjectivePointConstructor;
|
ProjectivePoint: ProjectivePointConstructor;
|
||||||
Signature: SignatureConstructor;
|
Signature: SignatureConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
isValidPrivateKey(privateKey: PrivKey): boolean;
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
|
isValidPrivateKey(key: PrivKey): boolean;
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@@ -362,7 +371,7 @@ 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
|
||||||
export interface ExtPointType extends Group<ExtPointType> {
|
interface ExtPointType extends Group<ExtPointType> {
|
||||||
readonly ex: bigint;
|
readonly ex: bigint;
|
||||||
readonly ey: bigint;
|
readonly ey: bigint;
|
||||||
readonly ez: bigint;
|
readonly ez: bigint;
|
||||||
@@ -376,7 +385,7 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|||||||
toAffine(iz?: bigint): AffinePoint<bigint>;
|
toAffine(iz?: bigint): AffinePoint<bigint>;
|
||||||
}
|
}
|
||||||
// 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> {
|
interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
||||||
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
||||||
fromHex(hex: Hex): ExtPointType;
|
fromHex(hex: Hex): ExtPointType;
|
||||||
@@ -388,13 +397,14 @@ Example implementing edwards25519:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
||||||
import { div } from '@noble/curves/abstract/modular';
|
import { Field, div } from '@noble/curves/abstract/modular';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
|
|
||||||
|
const Fp = Field(2n ** 255n - 19n);
|
||||||
const ed25519 = twistedEdwards({
|
const ed25519 = twistedEdwards({
|
||||||
a: -1n,
|
a: -1n,
|
||||||
d: div(-121665n, 121666n, 2n ** 255n - 19n), // -121665n/121666n
|
d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
|
||||||
P: 2n ** 255n - 19n,
|
Fp,
|
||||||
n: 2n ** 252n + 27742317777372353535851937790883648493n,
|
n: 2n ** 252n + 27742317777372353535851937790883648493n,
|
||||||
h: 8n,
|
h: 8n,
|
||||||
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
||||||
@@ -414,13 +424,12 @@ const ed25519 = twistedEdwards({
|
|||||||
`twistedEdwards()` returns `CurveFn` of following type:
|
`twistedEdwards()` returns `CurveFn` of following type:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export type CurveFn = {
|
type CurveFn = {
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
sign: (message: Hex, privateKey: Hex, context?: Hex) => Uint8Array;
|
||||||
verify: (sig: SigType, message: Hex, publicKey: PubKey, context?: Hex) => boolean;
|
verify: (sig: SigType, message: Hex, publicKey: Hex, context?: Hex) => boolean;
|
||||||
ExtendedPoint: ExtendedPointConstructor;
|
ExtendedPoint: ExtPointConstructor;
|
||||||
Signature: SignatureConstructor;
|
|
||||||
utils: {
|
utils: {
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
getExtendedPublicKey: (key: PrivKey) => {
|
getExtendedPublicKey: (key: PrivKey) => {
|
||||||
@@ -438,22 +447,18 @@ export type CurveFn = {
|
|||||||
|
|
||||||
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748. Proper Elliptic Curve Points are not implemented yet.
|
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 field, `a24` special variable, `montgomeryBits`, `nByteLength`, and coordinate `u` of generator point.
|
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';
|
||||||
|
|
||||||
const x25519 = montgomery({
|
const x25519 = montgomery({
|
||||||
P: 2n ** 255n - 19n,
|
Fp: Field(2n ** 255n - 19n),
|
||||||
a24: 121665n, // TODO: change to a
|
a: 486662n,
|
||||||
|
Gu: 9n,
|
||||||
montgomeryBits: 255,
|
montgomeryBits: 255,
|
||||||
nByteLength: 32,
|
nByteLength: 32,
|
||||||
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
|
// Optional param
|
||||||
|
|
||||||
// Optional params
|
|
||||||
powPminus2: (x: bigint): bigint => {
|
|
||||||
return mod.pow(x, P - 2, P);
|
|
||||||
},
|
|
||||||
adjustScalarBytes(bytes) {
|
adjustScalarBytes(bytes) {
|
||||||
bytes[0] &= 248;
|
bytes[0] &= 248;
|
||||||
bytes[31] &= 127;
|
bytes[31] &= 127;
|
||||||
@@ -465,40 +470,58 @@ const x25519 = montgomery({
|
|||||||
|
|
||||||
### 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 v11](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11).
|
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).
|
||||||
|
|
||||||
|
Every curve has exported `hashToCurve` and `encodeToCurve` methods:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1';
|
||||||
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
|
hashToCurve('0102abcd');
|
||||||
|
console.log(hashToCurve(randomBytes()));
|
||||||
|
console.log(encodeToCurve(randomBytes()));
|
||||||
|
|
||||||
|
import { bls12_381 } from '@noble/curves/bls12-381';
|
||||||
|
bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
|
||||||
|
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need low-level methods from spec:
|
||||||
|
|
||||||
`expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
`expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
||||||
|
|
||||||
|
Hash must conform to `CHash` interface (see [weierstrass section](#abstractweierstrass-short-weierstrass-curve)).
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
function expand_message_xmd(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash): Uint8Array;
|
function expand_message_xmd(
|
||||||
function expand_message_xof(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash): Uint8Array;
|
msg: Uint8Array,
|
||||||
|
DST: Uint8Array,
|
||||||
|
lenInBytes: number,
|
||||||
|
H: CHash
|
||||||
|
): Uint8Array;
|
||||||
|
function expand_message_xof(
|
||||||
|
msg: Uint8Array,
|
||||||
|
DST: Uint8Array,
|
||||||
|
lenInBytes: number,
|
||||||
|
k: number,
|
||||||
|
H: CHash
|
||||||
|
): 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
|
- `msg` a byte string containing the message to hash
|
||||||
_ `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
- `count` the number of elements of F to output
|
||||||
_ Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
- `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
|
||||||
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
|
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
|
||||||
type htfOpts = {
|
|
||||||
DST: string; // a domain separation tag defined in section 2.2.5
|
|
||||||
// p: the characteristic of F
|
|
||||||
// where F is a finite field of characteristic p and order q = p^m
|
|
||||||
p: bigint;
|
|
||||||
// m: the extension degree of F, m >= 1
|
|
||||||
// where F is a finite field of characteristic p and order q = p^m
|
|
||||||
m: number;
|
|
||||||
k: number; // the target security level for the suite in bits defined in section 5.1
|
|
||||||
expand?: 'xmd' | 'xof'; // option to use a message that has already been processed by expand_message_xmd
|
|
||||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
|
||||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
|
||||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
|
||||||
// TODO: verify that hash is shake if expand==='xof' via types
|
|
||||||
hash: CHash;
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### abstract/poseidon: Poseidon hash
|
### abstract/poseidon: Poseidon hash
|
||||||
@@ -532,10 +555,6 @@ and others with it.
|
|||||||
|
|
||||||
### abstract/modular: Modular arithmetics utilities
|
### abstract/modular: Modular arithmetics utilities
|
||||||
|
|
||||||
The module also contains useful `hashToPrivateScalar` method which allows to create
|
|
||||||
scalars (e.g. private keys) with the modulo bias being neglible. It follows
|
|
||||||
FIPS 186 B.4.1. Requires at least 40 bytes of input for 32-byte private key.
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import * as mod from '@noble/curves/abstract/modular';
|
import * as mod from '@noble/curves/abstract/modular';
|
||||||
const fp = mod.Field(2n ** 255n - 19n); // Finite field over 2^255-19
|
const fp = mod.Field(2n ** 255n - 19n); // Finite field over 2^255-19
|
||||||
@@ -548,7 +567,29 @@ fp.sqrt(21n); // square root
|
|||||||
mod.mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
mod.mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
||||||
mod.invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
mod.invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
||||||
mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
||||||
mod.hashToPrivateScalar(sha512_of_something, secp256r1.n);
|
```
|
||||||
|
|
||||||
|
#### Creating private keys from hashes
|
||||||
|
|
||||||
|
Suppose you have `sha256(something)` (e.g. from HMAC) and you want to make a private key from it.
|
||||||
|
Even though p256 or secp256k1 may have 32-byte private keys,
|
||||||
|
and sha256 output is also 32-byte, you can't just use it and reduce it modulo `CURVE.n`.
|
||||||
|
|
||||||
|
Doing so will make the result key [biased](https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/).
|
||||||
|
|
||||||
|
To avoid the bias, we implement FIPS 186 B.4.1, which allows to take arbitrary
|
||||||
|
byte array and produce valid scalars / private keys with bias being neglible.
|
||||||
|
|
||||||
|
Use [hash-to-curve](#abstracthash-to-curve-hashing-strings-to-curve-points) if you need
|
||||||
|
hashing to **public keys**; the function in the module instead operates on **private keys**.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { p256 } from '@noble/curves/p256';
|
||||||
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { hkdf } from '@noble/hashes/hkdf';
|
||||||
|
const someKey = new Uint8Array(32).fill(2); // Needs to actually be random, not .fill(2)
|
||||||
|
const derived = hkdf(sha256, someKey, undefined, 'application', 40); // 40 bytes
|
||||||
|
const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n);
|
||||||
```
|
```
|
||||||
|
|
||||||
### abstract/utils: General utilities
|
### abstract/utils: General utilities
|
||||||
@@ -561,8 +602,8 @@ utils.hexToBytes('deadbeef');
|
|||||||
utils.hexToNumber();
|
utils.hexToNumber();
|
||||||
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
||||||
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
||||||
utils.numberToBytesBE(123n);
|
utils.numberToBytesBE(123n, 32);
|
||||||
utils.numberToBytesLE(123n);
|
utils.numberToBytesLE(123n, 64);
|
||||||
utils.numberToHexUnpadded(123n);
|
utils.numberToHexUnpadded(123n);
|
||||||
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
|
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
|
||||||
utils.nLength(255n);
|
utils.nLength(255n);
|
||||||
@@ -571,7 +612,7 @@ utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
|
|||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
The library had no prior security audit.
|
The library had no prior security audit. The library has been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz): you can run the fuzzer by yourself to check it.
|
||||||
|
|
||||||
[Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: we are using non-CT bigints. However, _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
|
[Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: we are using non-CT bigints. However, _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
|
||||||
|
|
||||||
@@ -635,6 +676,14 @@ pedersen x 884 ops/sec @ 1ms/op
|
|||||||
poseidon x 8,598 ops/sec @ 116μs/op
|
poseidon x 8,598 ops/sec @ 116μs/op
|
||||||
verify x 528 ops/sec @ 1ms/op
|
verify x 528 ops/sec @ 1ms/op
|
||||||
|
|
||||||
|
ecdh
|
||||||
|
├─x25519 x 1,337 ops/sec @ 747μs/op
|
||||||
|
├─secp256k1 x 461 ops/sec @ 2ms/op
|
||||||
|
├─P256 x 441 ops/sec @ 2ms/op
|
||||||
|
├─P384 x 179 ops/sec @ 5ms/op
|
||||||
|
├─P521 x 93 ops/sec @ 10ms/op
|
||||||
|
└─x448 x 496 ops/sec @ 2ms/op
|
||||||
|
|
||||||
bls12-381
|
bls12-381
|
||||||
init x 32 ops/sec @ 30ms/op
|
init x 32 ops/sec @ 30ms/op
|
||||||
getPublicKey 1-bit x 858 ops/sec @ 1ms/op
|
getPublicKey 1-bit x 858 ops/sec @ 1ms/op
|
||||||
@@ -648,17 +697,28 @@ aggregatePublicKeys/128 x 7 ops/sec @ 125ms/op
|
|||||||
aggregateSignatures/8 x 45 ops/sec @ 22ms/op
|
aggregateSignatures/8 x 45 ops/sec @ 22ms/op
|
||||||
aggregateSignatures/32 x 11 ops/sec @ 84ms/op
|
aggregateSignatures/32 x 11 ops/sec @ 84ms/op
|
||||||
aggregateSignatures/128 x 3 ops/sec @ 332ms/opp
|
aggregateSignatures/128 x 3 ops/sec @ 332ms/opp
|
||||||
|
|
||||||
|
hash-to-curve
|
||||||
|
hash_to_field x 850,340 ops/sec @ 1μs/op
|
||||||
|
hashToCurve
|
||||||
|
├─secp256k1 x 1,850 ops/sec @ 540μs/op
|
||||||
|
├─P256 x 3,352 ops/sec @ 298μs/op
|
||||||
|
├─P384 x 1,367 ops/sec @ 731μs/op
|
||||||
|
├─P521 x 691 ops/sec @ 1ms/op
|
||||||
|
├─ed25519 x 2,492 ops/sec @ 401μs/op
|
||||||
|
└─ed448 x 1,045 ops/sec @ 956μs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
## In the wild
|
## Resources
|
||||||
|
|
||||||
Elliptic curve calculator: [paulmillr.com/ecc](https://paulmillr.com/ecc).
|
Article about some of library's features: [Learning fast elliptic-curve cryptography](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/). Elliptic curve calculator: [paulmillr.com/ecc](https://paulmillr.com/ecc)
|
||||||
|
|
||||||
- secp256k1
|
- secp256k1
|
||||||
- [btc-signer](https://github.com/paulmillr/micro-btc-signer), [eth-signer](https://github.com/paulmillr/micro-eth-signer)
|
- [btc-signer](https://github.com/paulmillr/micro-btc-signer), [eth-signer](https://github.com/paulmillr/micro-eth-signer)
|
||||||
- ed25519
|
- ed25519
|
||||||
- [sol-signer](https://github.com/paulmillr/micro-sol-signer)
|
- [sol-signer](https://github.com/paulmillr/micro-sol-signer)
|
||||||
- BLS12-381
|
- BLS12-381
|
||||||
|
- Check out `bls12-381.ts` for articles about the curve
|
||||||
- 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)
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
| >=0.5.0 | :white_check_mark: |
|
| >=1.0.0 | :white_check_mark: |
|
||||||
| <0.5.0 | :x: |
|
| <1.0.0 | :x: |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { mark, run } from 'micro-bmark';
|
import { mark, run } from 'micro-bmark';
|
||||||
import { bls12_381 as bls } from '../lib/bls12-381.js';
|
import { bls12_381 as bls } from '../bls12-381.js';
|
||||||
|
|
||||||
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
||||||
.trim()
|
.trim()
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { run, mark, utils } from 'micro-bmark';
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
import { generateData } from './_shared.js';
|
import { generateData } from './_shared.js';
|
||||||
import { P256 } from '../lib/p256.js';
|
import { P256 } from '../p256.js';
|
||||||
import { P384 } from '../lib/p384.js';
|
import { P384 } from '../p384.js';
|
||||||
import { P521 } from '../lib/p521.js';
|
import { P521 } from '../p521.js';
|
||||||
import { ed25519 } from '../lib/ed25519.js';
|
import { ed25519 } from '../ed25519.js';
|
||||||
import { ed448 } from '../lib/ed448.js';
|
import { ed448 } from '../ed448.js';
|
||||||
|
|
||||||
run(async () => {
|
run(async () => {
|
||||||
const RAM = false
|
const RAM = false
|
||||||
|
|||||||
19
benchmark/ecdh.js
Normal file
19
benchmark/ecdh.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { run, mark, compare, utils } from 'micro-bmark';
|
||||||
|
import { generateData } from './_shared.js';
|
||||||
|
import { secp256k1 } from '../secp256k1.js';
|
||||||
|
import { P256 } from '../p256.js';
|
||||||
|
import { P384 } from '../p384.js';
|
||||||
|
import { P521 } from '../p521.js';
|
||||||
|
import { x25519 } from '../ed25519.js';
|
||||||
|
import { x448 } from '../ed448.js';
|
||||||
|
|
||||||
|
run(async () => {
|
||||||
|
const curves = { x25519, secp256k1, P256, P384, P521, x448 };
|
||||||
|
const fns = {};
|
||||||
|
for (let [k, c] of Object.entries(curves)) {
|
||||||
|
const pubB = c.getPublicKey(c.utils.randomPrivateKey());
|
||||||
|
const privA = c.utils.randomPrivateKey();
|
||||||
|
fns[k] = () => c.getSharedSecret(privA, pubB);
|
||||||
|
}
|
||||||
|
await compare('ecdh', 1000, fns);
|
||||||
|
});
|
||||||
29
benchmark/hash-to-curve.js
Normal file
29
benchmark/hash-to-curve.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
|
import { hash_to_field } from '../abstract/hash-to-curve.js';
|
||||||
|
import { hashToPrivateScalar } from '../abstract/modular.js';
|
||||||
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
// import { generateData } from './_shared.js';
|
||||||
|
import { hashToCurve as secp256k1 } from '../secp256k1.js';
|
||||||
|
import { hashToCurve as P256 } from '../p256.js';
|
||||||
|
import { hashToCurve as P384 } from '../p384.js';
|
||||||
|
import { hashToCurve as P521 } from '../p521.js';
|
||||||
|
import { hashToCurve as ed25519 } from '../ed25519.js';
|
||||||
|
import { hashToCurve as ed448 } from '../ed448.js';
|
||||||
|
import { utf8ToBytes } from '../abstract/utils.js';
|
||||||
|
|
||||||
|
const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
||||||
|
run(async () => {
|
||||||
|
const rand = randomBytes(40);
|
||||||
|
await mark('hashToPrivateScalar', 1000000, () => hashToPrivateScalar(rand, N));
|
||||||
|
// - p, the characteristic of F
|
||||||
|
// - m, the extension degree of F, m >= 1
|
||||||
|
// - L = ceil((ceil(log2(p)) + k) / 8), where k is the security of suite (e.g. 128)
|
||||||
|
await mark('hash_to_field', 1000000, () =>
|
||||||
|
hash_to_field(rand, 1, { DST: 'secp256k1', hash: sha256, p: N, m: 1, k: 128 })
|
||||||
|
);
|
||||||
|
const msg = utf8ToBytes('message');
|
||||||
|
for (let [title, fn] of Object.entries({ secp256k1, P256, P384, P521, ed25519, ed448 })) {
|
||||||
|
await mark(`hashToCurve ${title}`, 1000, () => fn(msg));
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { run, mark, utils } from 'micro-bmark';
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
import { secp256k1, schnorr } from '../lib/secp256k1.js';
|
import { secp256k1, schnorr } from '../secp256k1.js';
|
||||||
import { generateData } from './_shared.js';
|
import { generateData } from './_shared.js';
|
||||||
|
|
||||||
run(async () => {
|
run(async () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { run, mark, compare, utils } from 'micro-bmark';
|
import { run, mark, compare, utils } from 'micro-bmark';
|
||||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||||
import * as stark from '../lib/stark.js';
|
import * as stark from '../stark.js';
|
||||||
|
|
||||||
run(async () => {
|
run(async () => {
|
||||||
const RAM = false;
|
const RAM = false;
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.7.0",
|
"version": "0.7.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.7.0",
|
"version": "0.7.2",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.7.0",
|
"version": "0.7.3",
|
||||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
||||||
"files": [
|
"files": [
|
||||||
"abstract",
|
"abstract",
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"*.d.ts.map"
|
"*.d.ts.map"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bench": "cd benchmark; node secp256k1.js; node curves.js; node stark.js; node bls.js",
|
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node stark.js; node bls.js",
|
||||||
"build": "tsc && tsc -p tsconfig.esm.json",
|
"build": "tsc && tsc -p tsconfig.esm.json",
|
||||||
"build:release": "rollup -c rollup.config.js",
|
"build:release": "rollup -c rollup.config.js",
|
||||||
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
|
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
|
||||||
@@ -126,16 +126,6 @@
|
|||||||
"import": "./esm/jubjub.js",
|
"import": "./esm/jubjub.js",
|
||||||
"default": "./jubjub.js"
|
"default": "./jubjub.js"
|
||||||
},
|
},
|
||||||
"./p192": {
|
|
||||||
"types": "./p192.d.ts",
|
|
||||||
"import": "./esm/p192.js",
|
|
||||||
"default": "./p192.js"
|
|
||||||
},
|
|
||||||
"./p224": {
|
|
||||||
"types": "./p224.d.ts",
|
|
||||||
"import": "./esm/p224.js",
|
|
||||||
"default": "./p224.js"
|
|
||||||
},
|
|
||||||
"./p256": {
|
"./p256": {
|
||||||
"types": "./p256.d.ts",
|
"types": "./p256.d.ts",
|
||||||
"import": "./esm/p256.js",
|
"import": "./esm/p256.js",
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
|
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
|
||||||
const msgPoint = normP2Hash(message, htfOpts);
|
const msgPoint = normP2Hash(message, htfOpts);
|
||||||
msgPoint.assertValidity();
|
msgPoint.assertValidity();
|
||||||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
||||||
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
||||||
return Signature.encode(sigPoint);
|
return Signature.encode(sigPoint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,35 @@
|
|||||||
/*! 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, Field } from './modular.js';
|
||||||
import { CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
|
import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * `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
|
||||||
|
*/
|
||||||
export type Opts = {
|
export type Opts = {
|
||||||
DST: string; // DST: a domain separation tag, defined in section 2.2.5
|
DST: string | Uint8Array;
|
||||||
encodeDST: string;
|
p: bigint;
|
||||||
p: bigint; // characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
m: number;
|
||||||
m: number; // extension degree of F, m >= 1
|
k: number;
|
||||||
k: number; // k: the target security level for the suite in bits, defined in section 5.1
|
expand?: 'xmd' | 'xof';
|
||||||
expand?: 'xmd' | 'xof'; // use a message that has already been processed by expand_message_xmd
|
|
||||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
|
||||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
|
||||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
|
||||||
// TODO: verify that hash is shake if expand==='xof' via types
|
|
||||||
hash: CHash;
|
hash: CHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Octet Stream to Integer (bytesToNumberBE)
|
function validateDST(dst: string | Uint8Array): Uint8Array {
|
||||||
function os2ip(bytes: Uint8Array): bigint {
|
if (dst instanceof Uint8Array) return dst;
|
||||||
let result = 0n;
|
if (typeof dst === 'string') return utf8ToBytes(dst);
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
throw new Error('DST must be Uint8Array or string');
|
||||||
result <<= 8n;
|
|
||||||
result += BigInt(bytes[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Integer to Octet Stream
|
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
||||||
|
const os2ip = bytesToNumberBE;
|
||||||
|
|
||||||
|
// Integer to Octet Stream (numberToBytesBE)
|
||||||
function i2osp(value: number, length: number): Uint8Array {
|
function i2osp(value: number, length: number): Uint8Array {
|
||||||
if (value < 0 || value >= 1 << (8 * length)) {
|
if (value < 0 || value >= 1 << (8 * length)) {
|
||||||
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
||||||
@@ -68,13 +70,12 @@ export function expand_message_xmd(
|
|||||||
isNum(lenInBytes);
|
isNum(lenInBytes);
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
||||||
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
||||||
const b_in_bytes = H.outputLen;
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
||||||
const r_in_bytes = H.blockLen;
|
|
||||||
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
||||||
if (ell > 255) throw new Error('Invalid xmd length');
|
if (ell > 255) throw new Error('Invalid xmd length');
|
||||||
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
||||||
const Z_pad = i2osp(0, r_in_bytes);
|
const Z_pad = i2osp(0, r_in_bytes);
|
||||||
const l_i_b_str = i2osp(lenInBytes, 2);
|
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
|
||||||
const b = new Array<Uint8Array>(ell);
|
const b = new Array<Uint8Array>(ell);
|
||||||
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
||||||
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
||||||
@@ -120,30 +121,33 @@ export function expand_message_xof(
|
|||||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
* 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 msg a byte string containing the message to hash
|
||||||
* @param count the number of elements of F to output
|
* @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}`
|
* @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.
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||||
*/
|
*/
|
||||||
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
||||||
const { p, k, m, hash, expand, DST: _DST } = options;
|
const { p, k, m, hash, expand, DST: _DST } = options;
|
||||||
isBytes(msg);
|
isBytes(msg);
|
||||||
isNum(count);
|
isNum(count);
|
||||||
if (typeof _DST !== 'string') throw new Error('DST must be valid');
|
const DST = validateDST(_DST);
|
||||||
const log2p = p.toString(2).length;
|
const log2p = p.toString(2).length;
|
||||||
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
||||||
const len_in_bytes = count * m * L;
|
const len_in_bytes = count * m * L;
|
||||||
const DST = utf8ToBytes(_DST);
|
let prb; // pseudo_random_bytes
|
||||||
const pseudo_random_bytes =
|
if (expand === 'xmd') {
|
||||||
expand === 'xmd'
|
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
|
||||||
? expand_message_xmd(msg, DST, len_in_bytes, hash)
|
} else if (expand === 'xof') {
|
||||||
: expand === 'xof'
|
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
|
||||||
? expand_message_xof(msg, DST, len_in_bytes, k, hash)
|
} else if (expand === undefined) {
|
||||||
: msg;
|
prb = msg;
|
||||||
|
} else {
|
||||||
|
throw new Error('expand must be "xmd", "xof" or undefined');
|
||||||
|
}
|
||||||
const u = new Array(count);
|
const u = new Array(count);
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const e = new Array(m);
|
const e = new Array(m);
|
||||||
for (let j = 0; j < m; j++) {
|
for (let j = 0; j < m; j++) {
|
||||||
const elm_offset = L * (j + i * m);
|
const elm_offset = L * (j + i * m);
|
||||||
const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
|
const tv = prb.subarray(elm_offset, elm_offset + L);
|
||||||
e[j] = mod(os2ip(tv), p);
|
e[j] = mod(os2ip(tv), p);
|
||||||
}
|
}
|
||||||
u[i] = e;
|
u[i] = e;
|
||||||
@@ -184,7 +188,7 @@ export type htfBasicOpts = { DST: string };
|
|||||||
export function createHasher<T>(
|
export function createHasher<T>(
|
||||||
Point: H2CPointConstructor<T>,
|
Point: H2CPointConstructor<T>,
|
||||||
mapToCurve: MapToCurve<T>,
|
mapToCurve: MapToCurve<T>,
|
||||||
def: Opts
|
def: Opts & { encodeDST?: string }
|
||||||
) {
|
) {
|
||||||
validateObject(def, {
|
validateObject(def, {
|
||||||
DST: 'string',
|
DST: 'string',
|
||||||
@@ -193,10 +197,7 @@ export function createHasher<T>(
|
|||||||
k: 'isSafeInteger',
|
k: 'isSafeInteger',
|
||||||
hash: 'hash',
|
hash: 'hash',
|
||||||
});
|
});
|
||||||
if (def.expand !== 'xmd' && def.expand !== 'xof' && def.expand !== undefined)
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
||||||
throw new Error('Invalid htf/expand');
|
|
||||||
if (typeof mapToCurve !== 'function')
|
|
||||||
throw new Error('hashToCurve: mapToCurve() has not been defined');
|
|
||||||
return {
|
return {
|
||||||
// Encodes byte string to elliptic curve
|
// Encodes byte string to elliptic curve
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||||
|
|||||||
@@ -11,25 +11,27 @@ export type CurveType = {
|
|||||||
nByteLength: number;
|
nByteLength: number;
|
||||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||||
a24: bigint; // Related to d, but cannot be derived from it
|
a: bigint;
|
||||||
montgomeryBits: number;
|
montgomeryBits: number;
|
||||||
powPminus2?: (x: bigint) => bigint;
|
powPminus2?: (x: bigint) => bigint;
|
||||||
xyToU?: (x: bigint, y: bigint) => bigint;
|
xyToU?: (x: bigint, y: bigint) => bigint;
|
||||||
Gu: string;
|
Gu: bigint;
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
};
|
};
|
||||||
export type CurveFn = {
|
export type CurveFn = {
|
||||||
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
||||||
scalarMultBase: (scalar: Hex) => Uint8Array;
|
scalarMultBase: (scalar: Hex) => Uint8Array;
|
||||||
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
||||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
Gu: string;
|
utils: { randomPrivateKey: () => Uint8Array };
|
||||||
|
GuBytes: Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateOpts(curve: CurveType) {
|
function validateOpts(curve: CurveType) {
|
||||||
validateObject(
|
validateObject(
|
||||||
curve,
|
curve,
|
||||||
{
|
{
|
||||||
a24: 'bigint',
|
a: 'bigint',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
montgomeryBits: 'isSafeInteger',
|
montgomeryBits: 'isSafeInteger',
|
||||||
@@ -37,7 +39,7 @@ function validateOpts(curve: CurveType) {
|
|||||||
adjustScalarBytes: 'function',
|
adjustScalarBytes: 'function',
|
||||||
domain: 'function',
|
domain: 'function',
|
||||||
powPminus2: 'function',
|
powPminus2: 'function',
|
||||||
Gu: 'string',
|
Gu: 'bigint',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Set defaults
|
// Set defaults
|
||||||
@@ -49,7 +51,7 @@ function validateOpts(curve: CurveType) {
|
|||||||
export function montgomery(curveDef: CurveType): CurveFn {
|
export function montgomery(curveDef: CurveType): CurveFn {
|
||||||
const CURVE = validateOpts(curveDef);
|
const CURVE = validateOpts(curveDef);
|
||||||
const { P } = CURVE;
|
const { P } = CURVE;
|
||||||
const modP = (a: bigint) => mod(a, P);
|
const modP = (n: bigint) => mod(n, P);
|
||||||
const montgomeryBits = CURVE.montgomeryBits;
|
const montgomeryBits = CURVE.montgomeryBits;
|
||||||
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
||||||
const fieldLen = CURVE.nByteLength;
|
const fieldLen = CURVE.nByteLength;
|
||||||
@@ -73,12 +75,15 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
return [x_2, x_3];
|
return [x_2, x_3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accepts 0 as well
|
||||||
function assertFieldElement(n: bigint): bigint {
|
function assertFieldElement(n: bigint): bigint {
|
||||||
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
|
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
|
||||||
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
||||||
}
|
}
|
||||||
|
|
||||||
// x25519 from 4
|
// x25519 from 4
|
||||||
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
||||||
|
const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
||||||
@@ -90,8 +95,6 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
// Section 5: Implementations MUST accept non-canonical values and process them as
|
// Section 5: Implementations MUST accept non-canonical values and process them as
|
||||||
// if they had been reduced modulo the field prime.
|
// if they had been reduced modulo the field prime.
|
||||||
const k = assertFieldElement(scalar);
|
const k = assertFieldElement(scalar);
|
||||||
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
|
||||||
const a24 = CURVE.a24;
|
|
||||||
const x_1 = u;
|
const x_1 = u;
|
||||||
let x_2 = _1n;
|
let x_2 = _1n;
|
||||||
let z_2 = _0n;
|
let z_2 = _0n;
|
||||||
@@ -170,8 +173,9 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
return encodeUCoordinate(pu);
|
return encodeUCoordinate(pu);
|
||||||
}
|
}
|
||||||
// Computes public key from private. By doing scalar multiplication of base point.
|
// Computes public key from private. By doing scalar multiplication of base point.
|
||||||
|
const GuBytes = encodeUCoordinate(CURVE.Gu);
|
||||||
function scalarMultBase(scalar: Hex): Uint8Array {
|
function scalarMultBase(scalar: Hex): Uint8Array {
|
||||||
return scalarMult(scalar, CURVE.Gu);
|
return scalarMult(scalar, GuBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -179,6 +183,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
scalarMultBase,
|
scalarMultBase,
|
||||||
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
||||||
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
||||||
Gu: CURVE.Gu,
|
utils: { randomPrivateKey: () => CURVE.randomBytes!(CURVE.nByteLength) },
|
||||||
|
GuBytes: GuBytes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
|||||||
|
|
||||||
export type CurvePointsRes<T> = {
|
export type CurvePointsRes<T> = {
|
||||||
ProjectivePoint: ProjConstructor<T>;
|
ProjectivePoint: ProjConstructor<T>;
|
||||||
normalizePrivateKey: (key: PrivKey) => bigint;
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
weierstrassEquation: (x: T) => T;
|
weierstrassEquation: (x: T) => T;
|
||||||
isWithinCurveOrder: (num: bigint) => boolean;
|
isWithinCurveOrder: (num: bigint) => boolean;
|
||||||
};
|
};
|
||||||
@@ -203,8 +203,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
||||||
}
|
}
|
||||||
// Validates if priv key is valid and converts it to bigint.
|
// Validates if priv key is valid and converts it to bigint.
|
||||||
// Supports options CURVE.normalizePrivateKey and CURVE.wrapPrivateKey.
|
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
||||||
function normalizePrivateKey(key: PrivKey): bigint {
|
function normPrivateKeyToScalar(key: PrivKey): bigint {
|
||||||
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
|
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
|
||||||
if (lengths && typeof key !== 'bigint') {
|
if (lengths && typeof key !== 'bigint') {
|
||||||
if (key instanceof Uint8Array) key = ut.bytesToHex(key);
|
if (key instanceof Uint8Array) key = ut.bytesToHex(key);
|
||||||
@@ -287,7 +287,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
|
|
||||||
// Multiplies generator point by privateKey.
|
// Multiplies generator point by privateKey.
|
||||||
static fromPrivateKey(privateKey: PrivKey) {
|
static fromPrivateKey(privateKey: PrivKey) {
|
||||||
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We calculate precomputes for elliptic curve point multiplication
|
// We calculate precomputes for elliptic curve point multiplication
|
||||||
@@ -488,8 +488,9 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
* Constant time multiplication.
|
* Constant time multiplication.
|
||||||
* Uses wNAF method. Windowed method may be 10% faster,
|
* Uses wNAF method. Windowed method may be 10% faster,
|
||||||
* but takes 2x longer to generate and consumes 2x memory.
|
* but takes 2x longer to generate and consumes 2x memory.
|
||||||
|
* Uses precomputes when available.
|
||||||
|
* Uses endomorphism for Koblitz curves.
|
||||||
* @param scalar by which the point would be multiplied
|
* @param scalar by which the point would be multiplied
|
||||||
* @param affinePoint optional point ot save cached precompute windows on it
|
|
||||||
* @returns New point
|
* @returns New point
|
||||||
*/
|
*/
|
||||||
multiply(scalar: bigint): Point {
|
multiply(scalar: bigint): Point {
|
||||||
@@ -517,6 +518,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
|
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
|
||||||
|
* Not using Strauss-Shamir trick: precomputation tables are faster.
|
||||||
|
* The trick could be useful if both P and Q are not G (not in our case).
|
||||||
* @returns non-zero affine point
|
* @returns non-zero affine point
|
||||||
*/
|
*/
|
||||||
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
||||||
@@ -572,7 +575,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
ProjectivePoint: Point as ProjConstructor<T>,
|
ProjectivePoint: Point as ProjConstructor<T>,
|
||||||
normalizePrivateKey,
|
normPrivateKeyToScalar,
|
||||||
weierstrassEquation,
|
weierstrassEquation,
|
||||||
isWithinCurveOrder,
|
isWithinCurveOrder,
|
||||||
};
|
};
|
||||||
@@ -642,7 +645,6 @@ export type CurveFn = {
|
|||||||
utils: {
|
utils: {
|
||||||
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
isValidPrivateKey(privateKey: PrivKey): boolean;
|
isValidPrivateKey(privateKey: PrivKey): boolean;
|
||||||
hashToPrivateKey: (hash: Hex) => Uint8Array;
|
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
||||||
};
|
};
|
||||||
@@ -667,7 +669,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
ProjectivePoint: Point,
|
ProjectivePoint: Point,
|
||||||
normalizePrivateKey,
|
normPrivateKeyToScalar,
|
||||||
weierstrassEquation,
|
weierstrassEquation,
|
||||||
isWithinCurveOrder,
|
isWithinCurveOrder,
|
||||||
} = weierstrassPoints({
|
} = weierstrassPoints({
|
||||||
@@ -677,7 +679,6 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const x = Fp.toBytes(a.x);
|
const x = Fp.toBytes(a.x);
|
||||||
const cat = ut.concatBytes;
|
const cat = ut.concatBytes;
|
||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
// TODO: hasEvenY
|
|
||||||
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
||||||
} else {
|
} else {
|
||||||
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
||||||
@@ -801,37 +802,35 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const utils = {
|
const utils = {
|
||||||
isValidPrivateKey(privateKey: PrivKey) {
|
isValidPrivateKey(privateKey: PrivKey) {
|
||||||
try {
|
try {
|
||||||
normalizePrivateKey(privateKey);
|
normPrivateKeyToScalar(privateKey);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
normPrivateKeyToScalar: normalizePrivateKey,
|
normPrivateKeyToScalar: normPrivateKeyToScalar,
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
|
||||||
*/
|
|
||||||
hashToPrivateKey: (hash: Hex): Uint8Array =>
|
|
||||||
ut.numberToBytesBE(mod.hashToPrivateScalar(hash, CURVE_ORDER), CURVE.nByteLength),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
||||||
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
||||||
*/
|
*/
|
||||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(CURVE.randomBytes(Fp.BYTES + 8)),
|
randomPrivateKey: (): Uint8Array => {
|
||||||
|
const rand = CURVE.randomBytes(Fp.BYTES + 8);
|
||||||
|
const num = mod.hashToPrivateScalar(rand, CURVE_ORDER);
|
||||||
|
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. Returns cached point which you can use to pass to `getSharedSecret` or `#multiply` by it.
|
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
||||||
* 2. Precomputes point multiplication table. Is done by default on first `getPublicKey()` call.
|
* Allows to massively speed-up `point.multiply(scalar)`.
|
||||||
* If you want your first getPublicKey to take 0.16ms instead of 20ms, make sure to call
|
|
||||||
* utils.precompute() somewhere without arguments first.
|
|
||||||
* @param windowSize 2, 4, 8, 16
|
|
||||||
* @returns cached point
|
* @returns cached point
|
||||||
|
* @example
|
||||||
|
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
||||||
|
* fast.multiply(privKey); // much faster ECDH now
|
||||||
*/
|
*/
|
||||||
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
||||||
point._setWindowSize(windowSize);
|
point._setWindowSize(windowSize);
|
||||||
point.multiply(BigInt(3));
|
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
||||||
return point;
|
return point;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -862,7 +861,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
/**
|
/**
|
||||||
* ECDH (Elliptic Curve Diffie Hellman).
|
* ECDH (Elliptic Curve Diffie Hellman).
|
||||||
* Computes shared public key from private key and public key.
|
* Computes shared public key from private key and public key.
|
||||||
* Checks: 1) private key validity 2) shared key is on-curve
|
* Checks: 1) private key validity 2) shared key is on-curve.
|
||||||
|
* Does NOT hash the result.
|
||||||
* @param privateA private key
|
* @param privateA private key
|
||||||
* @param publicB different public key
|
* @param publicB different public key
|
||||||
* @param isCompressed whether to return compact (default), or full key
|
* @param isCompressed whether to return compact (default), or full key
|
||||||
@@ -872,7 +872,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
if (isProbPub(privateA)) throw new Error('first arg must be private key');
|
if (isProbPub(privateA)) throw new Error('first arg must be private key');
|
||||||
if (!isProbPub(publicB)) throw new Error('second arg must be public key');
|
if (!isProbPub(publicB)) throw new Error('second arg must be public key');
|
||||||
const b = Point.fromHex(publicB); // check for being on-curve
|
const b = Point.fromHex(publicB); // check for being on-curve
|
||||||
return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed);
|
return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
||||||
@@ -895,10 +895,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
};
|
};
|
||||||
// NOTE: pads output with zero as per spec
|
// NOTE: pads output with zero as per spec
|
||||||
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
||||||
|
/**
|
||||||
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
||||||
|
*/
|
||||||
function int2octets(num: bigint): Uint8Array {
|
function int2octets(num: bigint): Uint8Array {
|
||||||
if (typeof num !== 'bigint') throw new Error('bigint expected');
|
if (typeof num !== 'bigint') throw new Error('bigint expected');
|
||||||
if (!(_0n <= num && num < ORDER_MASK))
|
if (!(_0n <= num && num < ORDER_MASK))
|
||||||
// n in [0..ORDER_MASK-1]
|
|
||||||
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
|
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
|
||||||
// works with order, can have different size than numToField!
|
// works with order, can have different size than numToField!
|
||||||
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
||||||
@@ -922,7 +924,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
// with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
// with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
||||||
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
||||||
const h1int = bits2int_modN(msgHash);
|
const h1int = bits2int_modN(msgHash);
|
||||||
const d = normalizePrivateKey(privateKey); // validate private key, convert to bigint
|
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
|
||||||
const seedArgs = [int2octets(d), int2octets(h1int)];
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
||||||
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
||||||
if (ent != null) {
|
if (ent != null) {
|
||||||
|
|||||||
@@ -138,10 +138,10 @@ export const ed25519ph = twistedEdwards({
|
|||||||
|
|
||||||
export const x25519 = montgomery({
|
export const x25519 = montgomery({
|
||||||
P: ED25519_P,
|
P: ED25519_P,
|
||||||
a24: BigInt('121665'),
|
a: BigInt(486662),
|
||||||
montgomeryBits: 255, // n is 253 bits
|
montgomeryBits: 255, // n is 253 bits
|
||||||
nByteLength: 32,
|
nByteLength: 32,
|
||||||
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
|
Gu: BigInt(9),
|
||||||
powPminus2: (x: bigint): bigint => {
|
powPminus2: (x: bigint): bigint => {
|
||||||
const P = ED25519_P;
|
const P = ED25519_P;
|
||||||
// x^(p-2) aka x^(2^255-21)
|
// x^(p-2) aka x^(2^255-21)
|
||||||
@@ -149,6 +149,7 @@ export const x25519 = montgomery({
|
|||||||
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
||||||
},
|
},
|
||||||
adjustScalarBytes,
|
adjustScalarBytes,
|
||||||
|
randomBytes,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
||||||
|
|||||||
@@ -122,11 +122,11 @@ export const ed448 = twistedEdwards(ED448_DEF);
|
|||||||
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
|
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
|
||||||
|
|
||||||
export const x448 = montgomery({
|
export const x448 = montgomery({
|
||||||
a24: BigInt(39081),
|
a: BigInt(156326),
|
||||||
montgomeryBits: 448,
|
montgomeryBits: 448,
|
||||||
nByteLength: 57,
|
nByteLength: 57,
|
||||||
P: ed448P,
|
P: ed448P,
|
||||||
Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
Gu: BigInt(5),
|
||||||
powPminus2: (x: bigint): bigint => {
|
powPminus2: (x: bigint): bigint => {
|
||||||
const P = ed448P;
|
const P = ed448P;
|
||||||
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
||||||
@@ -134,6 +134,7 @@ export const x448 = montgomery({
|
|||||||
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
||||||
},
|
},
|
||||||
adjustScalarBytes,
|
adjustScalarBytes,
|
||||||
|
randomBytes,
|
||||||
// The 4-isogeny maps between the Montgomery curve and this Edwards
|
// The 4-isogeny maps between the Montgomery curve and this Edwards
|
||||||
// curve are:
|
// curve are:
|
||||||
// (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)
|
// (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)
|
||||||
|
|||||||
25
src/p192.ts
25
src/p192.ts
@@ -1,25 +0,0 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
||||||
import { createCurve } from './_shortw_utils.js';
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
|
||||||
import { Fp } from './abstract/modular.js';
|
|
||||||
|
|
||||||
// NIST secp192r1 aka P192
|
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
|
|
||||||
export const P192 = createCurve(
|
|
||||||
{
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
|
||||||
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
|
||||||
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
|
|
||||||
Fp: Fp(BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff')),
|
|
||||||
// Curve order, total count of valid points in the field.
|
|
||||||
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
|
||||||
Gy: BigInt('0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811'),
|
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
} as const,
|
|
||||||
sha256
|
|
||||||
);
|
|
||||||
export const secp192r1 = P192;
|
|
||||||
25
src/p224.ts
25
src/p224.ts
@@ -1,25 +0,0 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
||||||
import { createCurve } from './_shortw_utils.js';
|
|
||||||
import { sha224 } from '@noble/hashes/sha256';
|
|
||||||
import { Fp } from './abstract/modular.js';
|
|
||||||
|
|
||||||
// NIST secp224r1 aka P224
|
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-224
|
|
||||||
export const P224 = createCurve(
|
|
||||||
{
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
|
||||||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
|
||||||
// Field over which we'll do calculations;
|
|
||||||
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
|
|
||||||
// Curve order, total count of valid points in the field
|
|
||||||
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
|
||||||
Gy: BigInt('0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34'),
|
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
} as const,
|
|
||||||
sha224
|
|
||||||
);
|
|
||||||
export const secp224r1 = P224;
|
|
||||||
@@ -107,6 +107,7 @@ function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
|||||||
return sha256(concatBytes(tagP, ...messages));
|
return sha256(concatBytes(tagP, ...messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
|
||||||
const pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
const pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||||
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
||||||
const modP = (x: bigint) => mod(x, secp256k1P);
|
const modP = (x: bigint) => mod(x, secp256k1P);
|
||||||
@@ -114,12 +115,17 @@ const modN = (x: bigint) => mod(x, secp256k1N);
|
|||||||
const Point = secp256k1.ProjectivePoint;
|
const Point = secp256k1.ProjectivePoint;
|
||||||
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
||||||
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
||||||
|
// Calculate point, scalar and bytes
|
||||||
function schnorrGetExtPubKey(priv: PrivKey) {
|
function schnorrGetExtPubKey(priv: PrivKey) {
|
||||||
const d = secp256k1.utils.normPrivateKeyToScalar(priv);
|
const d = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
||||||
const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside
|
const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside
|
||||||
const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d'
|
const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d'
|
||||||
return { point, scalar, bytes: pointToBytes(point) };
|
return { point, scalar, bytes: pointToBytes(point) };
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
|
||||||
|
* @returns valid point checked for being on-curve
|
||||||
|
*/
|
||||||
function lift_x(x: bigint): PointType<bigint> {
|
function lift_x(x: bigint): PointType<bigint> {
|
||||||
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
||||||
const xx = modP(x * x);
|
const xx = modP(x * x);
|
||||||
@@ -130,6 +136,9 @@ function lift_x(x: bigint): PointType<bigint> {
|
|||||||
p.assertValidity();
|
p.assertValidity();
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Create tagged hash, convert it to bigint, reduce modulo-n.
|
||||||
|
*/
|
||||||
function challenge(...args: Uint8Array[]): bigint {
|
function challenge(...args: Uint8Array[]): bigint {
|
||||||
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
|
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
|
||||||
}
|
}
|
||||||
@@ -169,6 +178,7 @@ function schnorrSign(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies Schnorr signature.
|
* Verifies Schnorr signature.
|
||||||
|
* Will swallow errors & return false except for initial type validation of arguments.
|
||||||
*/
|
*/
|
||||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||||
const sig = ensureBytes('signature', signature, 64);
|
const sig = ensureBytes('signature', signature, 64);
|
||||||
|
|||||||
228
src/stark.ts
228
src/stark.ts
@@ -1,164 +1,126 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { keccak_256 } from '@noble/hashes/sha3';
|
import { keccak_256 } from '@noble/hashes/sha3';
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { weierstrass, ProjPointType } from './abstract/weierstrass.js';
|
|
||||||
import * as cutils from './abstract/utils.js';
|
|
||||||
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
|
||||||
import { getHash } from './_shortw_utils.js';
|
|
||||||
import * as poseidon from './abstract/poseidon.js';
|
|
||||||
import { utf8ToBytes } from '@noble/hashes/utils';
|
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||||
|
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
||||||
|
import { poseidon } from './abstract/poseidon.js';
|
||||||
|
import { weierstrass, ProjPointType, SignatureType } from './abstract/weierstrass.js';
|
||||||
|
import {
|
||||||
|
Hex,
|
||||||
|
bitMask,
|
||||||
|
bytesToHex,
|
||||||
|
bytesToNumberBE,
|
||||||
|
concatBytes,
|
||||||
|
ensureBytes as ensureBytesOrig,
|
||||||
|
hexToBytes,
|
||||||
|
hexToNumber,
|
||||||
|
numberToVarBytesBE,
|
||||||
|
} from './abstract/utils.js';
|
||||||
|
import { getHash } from './_shortw_utils.js';
|
||||||
|
|
||||||
type ProjectivePoint = ProjPointType<bigint>;
|
|
||||||
// Stark-friendly elliptic curve
|
// Stark-friendly elliptic curve
|
||||||
// https://docs.starkware.co/starkex/stark-curve.html
|
// https://docs.starkware.co/starkex/stark-curve.html
|
||||||
|
|
||||||
const CURVE_N = BigInt(
|
type ProjectivePoint = ProjPointType<bigint>;
|
||||||
|
const CURVE_ORDER = BigInt(
|
||||||
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
||||||
);
|
);
|
||||||
const nBitLength = 252;
|
const nBitLength = 252;
|
||||||
// Copy-pasted from weierstrass.ts
|
|
||||||
function bits2int(bytes: Uint8Array): bigint {
|
function bits2int(bytes: Uint8Array): bigint {
|
||||||
|
while (bytes[0] === 0) bytes = bytes.subarray(1); // strip leading 0s
|
||||||
|
// Copy-pasted from weierstrass.ts
|
||||||
const delta = bytes.length * 8 - nBitLength;
|
const delta = bytes.length * 8 - nBitLength;
|
||||||
const num = cutils.bytesToNumberBE(bytes);
|
const num = bytesToNumberBE(bytes);
|
||||||
return delta > 0 ? num >> BigInt(delta) : num;
|
return delta > 0 ? num >> BigInt(delta) : num;
|
||||||
}
|
}
|
||||||
function bits2int_modN(bytes: Uint8Array): bigint {
|
function hex0xToBytes(hex: string): Uint8Array {
|
||||||
return mod(bits2int(bytes), CURVE_N);
|
if (typeof hex === 'string') {
|
||||||
|
hex = strip0x(hex); // allow 0x prefix
|
||||||
|
if (hex.length & 1) hex = '0' + hex; // allow unpadded hex
|
||||||
|
}
|
||||||
|
return hexToBytes(hex);
|
||||||
}
|
}
|
||||||
export const starkCurve = weierstrass({
|
const curve = weierstrass({
|
||||||
// Params: a, b
|
a: BigInt(1), // Params: a, b
|
||||||
a: BigInt(1),
|
|
||||||
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
|
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
|
||||||
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
|
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
|
||||||
// There is no efficient sqrt for field (P%4==1)
|
// There is no efficient sqrt for field (P%4==1)
|
||||||
Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
|
Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
|
||||||
// Curve order, total count of valid points in the field.
|
n: CURVE_ORDER, // Curve order, total count of valid points in the field.
|
||||||
n: CURVE_N,
|
nBitLength, // len(bin(N).replace('0b',''))
|
||||||
nBitLength: nBitLength, // len(bin(N).replace('0b',''))
|
|
||||||
// Base point (x, y) aka generator point
|
// Base point (x, y) aka generator point
|
||||||
Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
|
Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
|
||||||
Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
|
Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
|
||||||
h: BigInt(1),
|
h: BigInt(1), // cofactor
|
||||||
// Default options
|
lowS: false, // Allow high-s signatures
|
||||||
lowS: false,
|
|
||||||
...getHash(sha256),
|
...getHash(sha256),
|
||||||
// Custom truncation routines for stark curve
|
// Custom truncation routines for stark curve
|
||||||
bits2int: (bytes: Uint8Array): bigint => {
|
bits2int,
|
||||||
while (bytes[0] === 0) bytes = bytes.subarray(1);
|
|
||||||
return bits2int(bytes);
|
|
||||||
},
|
|
||||||
bits2int_modN: (bytes: Uint8Array): bigint => {
|
bits2int_modN: (bytes: Uint8Array): bigint => {
|
||||||
let hashS = cutils.bytesToNumberBE(bytes).toString(16);
|
// 2102820b232636d200cb21f1d330f20d096cae09d1bf3edb1cc333ddee11318 =>
|
||||||
if (hashS.length === 63) {
|
// 2102820b232636d200cb21f1d330f20d096cae09d1bf3edb1cc333ddee113180
|
||||||
hashS += '0';
|
const hex = bytesToNumberBE(bytes).toString(16); // toHex unpadded
|
||||||
bytes = hexToBytes0x(hashS);
|
if (hex.length === 63) bytes = hex0xToBytes(hex + '0'); // append trailing 0
|
||||||
}
|
return mod(bits2int(bytes), CURVE_ORDER);
|
||||||
// Truncate zero bytes on left (compat with elliptic)
|
|
||||||
while (bytes[0] === 0) bytes = bytes.subarray(1);
|
|
||||||
return bits2int_modN(bytes);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
export const _starkCurve = curve;
|
||||||
|
|
||||||
// Custom Starknet type conversion functions that can handle 0x and unpadded hex
|
function ensureBytes(hex: Hex): Uint8Array {
|
||||||
function hexToBytes0x(hex: string): Uint8Array {
|
return ensureBytesOrig('', typeof hex === 'string' ? hex0xToBytes(hex) : hex);
|
||||||
if (typeof hex !== 'string') {
|
|
||||||
throw new Error('hexToBytes: expected string, got ' + typeof hex);
|
|
||||||
}
|
|
||||||
hex = strip0x(hex);
|
|
||||||
if (hex.length & 1) hex = '0' + hex; // padding
|
|
||||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
|
|
||||||
const array = new Uint8Array(hex.length / 2);
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
|
||||||
const j = i * 2;
|
|
||||||
const hexByte = hex.slice(j, j + 2);
|
|
||||||
const byte = Number.parseInt(hexByte, 16);
|
|
||||||
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
|
|
||||||
array[i] = byte;
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
function hexToNumber0x(hex: string): bigint {
|
|
||||||
if (typeof hex !== 'string') {
|
|
||||||
throw new Error('hexToNumber: expected string, got ' + typeof hex);
|
|
||||||
}
|
|
||||||
// Big Endian
|
|
||||||
// TODO: strip vs no strip?
|
|
||||||
return BigInt(`0x${strip0x(hex)}`);
|
|
||||||
}
|
|
||||||
function bytesToNumber0x(bytes: Uint8Array): bigint {
|
|
||||||
return hexToNumber0x(cutils.bytesToHex(bytes));
|
|
||||||
}
|
|
||||||
function ensureBytes0x(hex: Hex): Uint8Array {
|
|
||||||
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
|
||||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
|
||||||
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePrivateKey(privKey: Hex) {
|
function normPrivKey(privKey: Hex): string {
|
||||||
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(64, '0');
|
return bytesToHex(ensureBytes(privKey)).padStart(64, '0');
|
||||||
}
|
}
|
||||||
function getPublicKey0x(privKey: Hex, isCompressed = false) {
|
export function getPublicKey(privKey: Hex, isCompressed = false): Uint8Array {
|
||||||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
return curve.getPublicKey(normPrivKey(privKey), isCompressed);
|
||||||
}
|
}
|
||||||
function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
|
export function getSharedSecret(privKeyA: Hex, pubKeyB: Hex): Uint8Array {
|
||||||
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
|
return curve.getSharedSecret(normPrivKey(privKeyA), pubKeyB);
|
||||||
|
}
|
||||||
|
export function sign(msgHash: Hex, privKey: Hex, opts?: any): SignatureType {
|
||||||
|
return curve.sign(ensureBytes(msgHash), normPrivKey(privKey), opts);
|
||||||
|
}
|
||||||
|
export function verify(signature: SignatureType | Hex, msgHash: Hex, pubKey: Hex) {
|
||||||
|
const sig = signature instanceof Signature ? signature : ensureBytes(signature);
|
||||||
|
return curve.verify(sig, ensureBytes(msgHash), ensureBytes(pubKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
function sign0x(msgHash: Hex, privKey: Hex, opts?: any) {
|
const { CURVE, ProjectivePoint, Signature, utils } = curve;
|
||||||
if (typeof privKey === 'string') privKey = strip0x(privKey).padStart(64, '0');
|
export { CURVE, ProjectivePoint, Signature, utils };
|
||||||
return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
|
|
||||||
|
function extractX(bytes: Uint8Array): string {
|
||||||
|
const hex = bytesToHex(bytes.subarray(1));
|
||||||
|
const stripped = hex.replace(/^0+/gm, ''); // strip leading 0s
|
||||||
|
return `0x${stripped}`;
|
||||||
}
|
}
|
||||||
function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
|
function strip0x(hex: string) {
|
||||||
const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
|
return hex.replace(/^0x/i, '');
|
||||||
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
}
|
||||||
}
|
function numberTo0x16(num: bigint) {
|
||||||
|
// can't use utils.numberToHexUnpadded: adds leading 0 for even byte length
|
||||||
const { CURVE, ProjectivePoint, Signature } = starkCurve;
|
return `0x${num.toString(16)}`;
|
||||||
export const utils = starkCurve.utils;
|
|
||||||
export {
|
|
||||||
CURVE,
|
|
||||||
Signature,
|
|
||||||
ProjectivePoint,
|
|
||||||
getPublicKey0x as getPublicKey,
|
|
||||||
getSharedSecret0x as getSharedSecret,
|
|
||||||
sign0x as sign,
|
|
||||||
verify0x as verify,
|
|
||||||
};
|
|
||||||
|
|
||||||
const stripLeadingZeros = (s: string) => s.replace(/^0+/gm, '');
|
|
||||||
export const bytesToHexEth = (uint8a: Uint8Array): string =>
|
|
||||||
`0x${stripLeadingZeros(cutils.bytesToHex(uint8a))}`;
|
|
||||||
export const strip0x = (hex: string) => hex.replace(/^0x/i, '');
|
|
||||||
export const numberToHexEth = (num: bigint | number) => `0x${num.toString(16)}`;
|
|
||||||
|
|
||||||
// We accept hex strings besides Uint8Array for simplicity
|
|
||||||
type Hex = Uint8Array | string;
|
|
||||||
|
|
||||||
// 1. seed generation
|
|
||||||
function hashKeyWithIndex(key: Uint8Array, index: number) {
|
|
||||||
let indexHex = cutils.numberToHexUnpadded(index);
|
|
||||||
if (indexHex.length & 1) indexHex = '0' + indexHex;
|
|
||||||
return sha256Num(cutils.concatBytes(key, hexToBytes0x(indexHex)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// seed generation
|
||||||
export function grindKey(seed: Hex) {
|
export function grindKey(seed: Hex) {
|
||||||
const _seed = ensureBytes0x(seed);
|
const _seed = ensureBytes(seed);
|
||||||
const sha256mask = 2n ** 256n;
|
const sha256mask = 2n ** 256n;
|
||||||
|
const limit = sha256mask - mod(sha256mask, CURVE_ORDER);
|
||||||
const limit = sha256mask - mod(sha256mask, CURVE_N);
|
|
||||||
for (let i = 0; ; i++) {
|
for (let i = 0; ; i++) {
|
||||||
const key = hashKeyWithIndex(_seed, i);
|
const key = sha256Num(concatBytes(_seed, numberToVarBytesBE(BigInt(i))));
|
||||||
// key should be in [0, limit)
|
if (key < limit) return mod(key, CURVE_ORDER).toString(16); // key should be in [0, limit)
|
||||||
if (key < limit) return mod(key, CURVE_N).toString(16);
|
if (i === 100000) throw new Error('grindKey is broken: tried 100k vals'); // prevent dos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStarkKey(privateKey: Hex) {
|
export function getStarkKey(privateKey: Hex): string {
|
||||||
return bytesToHexEth(getPublicKey0x(privateKey, true).slice(1));
|
return extractX(getPublicKey(privateKey, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ethSigToPrivate(signature: string) {
|
export function ethSigToPrivate(signature: string): string {
|
||||||
signature = strip0x(signature.replace(/^0x/, ''));
|
signature = strip0x(signature);
|
||||||
if (signature.length !== 130) throw new Error('Wrong ethereum signature');
|
if (signature.length !== 130) throw new Error('Wrong ethereum signature');
|
||||||
return grindKey(signature.substring(0, 64));
|
return grindKey(signature.substring(0, 64));
|
||||||
}
|
}
|
||||||
@@ -170,15 +132,15 @@ export function getAccountPath(
|
|||||||
application: string,
|
application: string,
|
||||||
ethereumAddress: string,
|
ethereumAddress: string,
|
||||||
index: number
|
index: number
|
||||||
) {
|
): string {
|
||||||
const layerNum = int31(sha256Num(layer));
|
const layerNum = int31(sha256Num(layer));
|
||||||
const applicationNum = int31(sha256Num(application));
|
const applicationNum = int31(sha256Num(application));
|
||||||
const eth = hexToNumber0x(ethereumAddress);
|
const eth = hexToNumber(strip0x(ethereumAddress));
|
||||||
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
||||||
const PEDERSEN_POINTS_AFFINE = [
|
const PEDERSEN_POINTS = [
|
||||||
new ProjectivePoint(
|
new ProjectivePoint(
|
||||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
||||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n,
|
1713931329540660377023406109199410414810705867260802078187082345529207694986n,
|
||||||
@@ -205,8 +167,6 @@ const PEDERSEN_POINTS_AFFINE = [
|
|||||||
1n
|
1n
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
|
|
||||||
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE;
|
|
||||||
|
|
||||||
function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
|
function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
|
||||||
const out: ProjectivePoint[] = [];
|
const out: ProjectivePoint[] = [];
|
||||||
@@ -230,14 +190,16 @@ const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS[3], PEDERSEN_POINTS[
|
|||||||
type PedersenArg = Hex | bigint | number;
|
type PedersenArg = Hex | bigint | number;
|
||||||
function pedersenArg(arg: PedersenArg): bigint {
|
function pedersenArg(arg: PedersenArg): bigint {
|
||||||
let value: bigint;
|
let value: bigint;
|
||||||
if (typeof arg === 'bigint') value = arg;
|
if (typeof arg === 'bigint') {
|
||||||
else if (typeof arg === 'number') {
|
value = arg;
|
||||||
|
} else if (typeof arg === 'number') {
|
||||||
if (!Number.isSafeInteger(arg)) throw new Error(`Invalid pedersenArg: ${arg}`);
|
if (!Number.isSafeInteger(arg)) throw new Error(`Invalid pedersenArg: ${arg}`);
|
||||||
value = BigInt(arg);
|
value = BigInt(arg);
|
||||||
} else value = bytesToNumber0x(ensureBytes0x(arg));
|
} else {
|
||||||
// [0..Fp)
|
value = bytesToNumberBE(ensureBytes(arg));
|
||||||
if (!(0n <= value && value < starkCurve.CURVE.Fp.ORDER))
|
}
|
||||||
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`);
|
if (!(0n <= value && value < curve.CURVE.Fp.ORDER))
|
||||||
|
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`); // [0..Fp)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,17 +215,17 @@ function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: P
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
||||||
export function pedersen(x: PedersenArg, y: PedersenArg) {
|
export function pedersen(x: PedersenArg, y: PedersenArg): string {
|
||||||
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
||||||
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
||||||
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
||||||
return bytesToHexEth(point.toRawBytes(true).slice(1));
|
return extractX(point.toRawBytes(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||||
if (!Array.isArray(data) || data.length < 1)
|
if (!Array.isArray(data) || data.length < 1)
|
||||||
throw new Error('data should be array of at least 1 element');
|
throw new Error('data should be array of at least 1 element');
|
||||||
if (data.length === 1) return numberToHexEth(pedersenArg(data[0]));
|
if (data.length === 1) return numberTo0x16(pedersenArg(data[0]));
|
||||||
return Array.from(data)
|
return Array.from(data)
|
||||||
.reverse()
|
.reverse()
|
||||||
.reduce((acc, i) => fn(i, acc));
|
.reduce((acc, i) => fn(i, acc));
|
||||||
@@ -272,9 +234,9 @@ export function hashChain(data: PedersenArg[], fn = pedersen) {
|
|||||||
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
|
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
|
||||||
[0, ...data, data.length].reduce((x, y) => fn(x, y));
|
[0, ...data, data.length].reduce((x, y) => fn(x, y));
|
||||||
|
|
||||||
const MASK_250 = cutils.bitMask(250);
|
const MASK_250 = bitMask(250);
|
||||||
export const keccak = (data: Uint8Array): bigint => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
export const keccak = (data: Uint8Array): bigint => bytesToNumberBE(keccak_256(data)) & MASK_250;
|
||||||
const sha256Num = (data: Uint8Array | string): bigint => cutils.bytesToNumberBE(sha256(data));
|
const sha256Num = (data: Uint8Array | string): bigint => bytesToNumberBE(sha256(data));
|
||||||
|
|
||||||
// Poseidon hash
|
// Poseidon hash
|
||||||
export const Fp253 = Fp(
|
export const Fp253 = Fp(
|
||||||
@@ -330,7 +292,7 @@ export function poseidonBasic(opts: PoseidonOpts, mds: bigint[][]) {
|
|||||||
for (let j = 0; j < m; j++) row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
|
for (let j = 0; j < m; j++) row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
|
||||||
roundConstants.push(row);
|
roundConstants.push(row);
|
||||||
}
|
}
|
||||||
return poseidon.poseidon({
|
return poseidon({
|
||||||
...opts,
|
...opts,
|
||||||
t: m,
|
t: m,
|
||||||
sboxPower: 3,
|
sboxPower: 3,
|
||||||
|
|||||||
44
test/_more-curves.helpers.js
Normal file
44
test/_more-curves.helpers.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { createCurve } from '../_shortw_utils.js';
|
||||||
|
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { Fp } from '../abstract/modular.js';
|
||||||
|
|
||||||
|
// NIST secp192r1 aka P192
|
||||||
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
|
||||||
|
export const P192 = createCurve(
|
||||||
|
{
|
||||||
|
// Params: a, b
|
||||||
|
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
||||||
|
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
||||||
|
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
|
||||||
|
Fp: Fp(BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff')),
|
||||||
|
// Curve order, total count of valid points in the field.
|
||||||
|
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
||||||
|
// Base point (x, y) aka generator point
|
||||||
|
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
||||||
|
Gy: BigInt('0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811'),
|
||||||
|
h: BigInt(1),
|
||||||
|
lowS: false,
|
||||||
|
},
|
||||||
|
sha256
|
||||||
|
);
|
||||||
|
export const secp192r1 = P192;
|
||||||
|
|
||||||
|
export const P224 = createCurve(
|
||||||
|
{
|
||||||
|
// Params: a, b
|
||||||
|
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
||||||
|
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
||||||
|
// Field over which we'll do calculations;
|
||||||
|
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
|
||||||
|
// Curve order, total count of valid points in the field
|
||||||
|
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
||||||
|
// Base point (x, y) aka generator point
|
||||||
|
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
||||||
|
Gy: BigInt('0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34'),
|
||||||
|
h: BigInt(1),
|
||||||
|
lowS: false,
|
||||||
|
},
|
||||||
|
sha224
|
||||||
|
);
|
||||||
|
export const secp224r1 = P224;
|
||||||
@@ -1,22 +1,21 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should, describe } from 'micro-should';
|
import { should, describe } from 'micro-should';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import * as mod from '../lib/esm/abstract/modular.js';
|
import * as mod from '../esm/abstract/modular.js';
|
||||||
import { bytesToHex as toHex } from '../lib/esm/abstract/utils.js';
|
import { bytesToHex as toHex } from '../esm/abstract/utils.js';
|
||||||
// Generic tests for all curves in package
|
// Generic tests for all curves in package
|
||||||
import { secp192r1 } from '../lib/esm/p192.js';
|
import { secp192r1, secp224r1 } from './_more-curves.helpers.js';
|
||||||
import { secp224r1 } from '../lib/esm/p224.js';
|
import { secp256r1 } from '../esm/p256.js';
|
||||||
import { secp256r1 } from '../lib/esm/p256.js';
|
import { secp384r1 } from '../esm/p384.js';
|
||||||
import { secp384r1 } from '../lib/esm/p384.js';
|
import { secp521r1 } from '../esm/p521.js';
|
||||||
import { secp521r1 } from '../lib/esm/p521.js';
|
import { secp256k1 } from '../esm/secp256k1.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../esm/ed25519.js';
|
||||||
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../lib/esm/ed25519.js';
|
import { ed448, ed448ph } from '../esm/ed448.js';
|
||||||
import { ed448, ed448ph } from '../lib/esm/ed448.js';
|
import { _starkCurve as starkCurve } from '../esm/stark.js';
|
||||||
import { starkCurve } from '../lib/esm/stark.js';
|
import { pallas, vesta } from '../esm/pasta.js';
|
||||||
import { pallas, vesta } from '../lib/esm/pasta.js';
|
import { bn254 } from '../esm/bn.js';
|
||||||
import { bn254 } from '../lib/esm/bn.js';
|
import { jubjub } from '../esm/jubjub.js';
|
||||||
import { jubjub } from '../lib/esm/jubjub.js';
|
import { bls12_381 } from '../esm/bls12-381.js';
|
||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
|
||||||
|
|
||||||
// Fields tests
|
// Fields tests
|
||||||
const FIELDS = {
|
const FIELDS = {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { deepStrictEqual, notDeepStrictEqual, throws } from 'assert';
|
|||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { wNAF } from '../lib/esm/abstract/curve.js';
|
import { wNAF } from '../esm/abstract/curve.js';
|
||||||
import { bytesToHex, utf8ToBytes } from '../lib/esm/abstract/utils.js';
|
import { bytesToHex, utf8ToBytes } from '../esm/abstract/utils.js';
|
||||||
import { hash_to_field } from '../lib/esm/abstract/hash-to-curve.js';
|
import { hash_to_field } from '../esm/abstract/hash-to-curve.js';
|
||||||
import { bls12_381 as bls } from '../lib/esm/bls12-381.js';
|
import { bls12_381 as bls } from '../esm/bls12-381.js';
|
||||||
|
|
||||||
import zkVectors from './bls12-381/zkcrypto/converted.json' assert { type: 'json' };
|
import zkVectors from './bls12-381/zkcrypto/converted.json' assert { type: 'json' };
|
||||||
import pairingVectors from './bls12-381/go_pairing_vectors/pairing.json' assert { type: 'json' };
|
import pairingVectors from './bls12-381/go_pairing_vectors/pairing.json' assert { type: 'json' };
|
||||||
@@ -857,7 +857,7 @@ describe('bls12-381/basic', () => {
|
|||||||
const options = {
|
const options = {
|
||||||
p: bls.CURVE.r,
|
p: bls.CURVE.r,
|
||||||
m: 1,
|
m: 1,
|
||||||
expand: false,
|
expand: undefined,
|
||||||
};
|
};
|
||||||
for (let vector of SCALAR_VECTORS) {
|
for (let vector of SCALAR_VECTORS) {
|
||||||
const [okmAscii, expectedHex] = vector;
|
const [okmAscii, expectedHex] = vector;
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { sha512 } from '@noble/hashes/sha512';
|
|||||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||||
import { deepStrictEqual, strictEqual, throws } from 'assert';
|
import { deepStrictEqual, strictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
import { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||||
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
||||||
import { ed25519ctx, ed25519ph, RistrettoPoint, x25519 } from '../lib/esm/ed25519.js';
|
import { ed25519ctx, ed25519ph, RistrettoPoint, x25519 } from '../esm/ed25519.js';
|
||||||
|
|
||||||
// const ed = ed25519;
|
// const ed = ed25519;
|
||||||
const hex = bytesToHex;
|
const hex = bytesToHex;
|
||||||
@@ -97,7 +97,7 @@ should('X25519 base point', () => {
|
|||||||
const { y } = ed25519ph.ExtendedPoint.BASE;
|
const { y } = ed25519ph.ExtendedPoint.BASE;
|
||||||
const { Fp } = ed25519ph.CURVE;
|
const { Fp } = ed25519ph.CURVE;
|
||||||
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
|
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
|
||||||
deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu);
|
deepStrictEqual(numberToBytesLE(u, 32), x25519.GuBytes);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('RFC7748', () => {
|
describe('RFC7748', () => {
|
||||||
@@ -128,7 +128,7 @@ describe('RFC7748', () => {
|
|||||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
for (let i = 0; i < rfc7748Iter.length; i++) {
|
||||||
const { scalar, iters } = rfc7748Iter[i];
|
const { scalar, iters } = rfc7748Iter[i];
|
||||||
should(`scalarMult iteration (${i})`, () => {
|
should(`scalarMult iteration (${i})`, () => {
|
||||||
let k = x25519.Gu;
|
let k = x25519.GuBytes;
|
||||||
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
|
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
|
||||||
deepStrictEqual(hex(k), scalar);
|
deepStrictEqual(hex(k), scalar);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export { ed25519, ED25519_TORSION_SUBGROUP } from '../lib/esm/ed25519.js';
|
export { ed25519, ED25519_TORSION_SUBGROUP } from '../esm/ed25519.js';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import { ed448, ed448ph, x448 } from '../lib/esm/ed448.js';
|
import { ed448, ed448ph, x448 } from '../esm/ed448.js';
|
||||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
import { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||||
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
|
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
|
||||||
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
|
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
@@ -509,7 +509,7 @@ describe('ed448', () => {
|
|||||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
for (let i = 0; i < rfc7748Iter.length; i++) {
|
||||||
const { scalar, iters } = rfc7748Iter[i];
|
const { scalar, iters } = rfc7748Iter[i];
|
||||||
should(`RFC7748: scalarMult iteration (${i})`, () => {
|
should(`RFC7748: scalarMult iteration (${i})`, () => {
|
||||||
let k = x448.Gu;
|
let k = x448.GuBytes;
|
||||||
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
|
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
|
||||||
deepStrictEqual(hex(k), scalar);
|
deepStrictEqual(hex(k), scalar);
|
||||||
});
|
});
|
||||||
@@ -664,7 +664,7 @@ describe('ed448', () => {
|
|||||||
// const invX = Fp.invert(x * x); // x²
|
// const invX = Fp.invert(x * x); // x²
|
||||||
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
|
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
|
||||||
// const u = Fp.create(y * y * invX);
|
// const u = Fp.create(y * y * invX);
|
||||||
deepStrictEqual(hex(numberToBytesLE(u, 56)), x448.Gu);
|
deepStrictEqual(numberToBytesLE(u, 56), x448.GuBytes);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ import { bytesToHex } from '@noble/hashes/utils';
|
|||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { shake128, shake256 } from '@noble/hashes/sha3';
|
import { shake128, shake256 } from '@noble/hashes/sha3';
|
||||||
import * as secp256r1 from '../lib/esm/p256.js';
|
import * as secp256r1 from '../esm/p256.js';
|
||||||
import * as secp384r1 from '../lib/esm/p384.js';
|
import * as secp384r1 from '../esm/p384.js';
|
||||||
import * as secp521r1 from '../lib/esm/p521.js';
|
import * as secp521r1 from '../esm/p521.js';
|
||||||
import * as ed25519 from '../lib/esm/ed25519.js';
|
import * as ed25519 from '../esm/ed25519.js';
|
||||||
import * as ed448 from '../lib/esm/ed448.js';
|
import * as ed448 from '../esm/ed448.js';
|
||||||
import * as secp256k1 from '../lib/esm/secp256k1.js';
|
import * as secp256k1 from '../esm/secp256k1.js';
|
||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
import { bls12_381 } from '../esm/bls12-381.js';
|
||||||
import { expand_message_xmd, expand_message_xof } from '../lib/esm/abstract/hash-to-curve.js';
|
import { expand_message_xmd, expand_message_xof } from '../esm/abstract/hash-to-curve.js';
|
||||||
import { utf8ToBytes } from '../lib/esm/abstract/utils.js';
|
import { utf8ToBytes } from '../esm/abstract/utils.js';
|
||||||
// XMD
|
// XMD
|
||||||
import { default as xmd_sha256_38 } from './hash-to-curve/expand_message_xmd_SHA256_38.json' assert { type: 'json' };
|
import { default as xmd_sha256_38 } from './hash-to-curve/expand_message_xmd_SHA256_38.json' assert { type: 'json' };
|
||||||
import { default as xmd_sha256_256 } from './hash-to-curve/expand_message_xmd_SHA256_256.json' assert { type: 'json' };
|
import { default as xmd_sha256_256 } from './hash-to-curve/expand_message_xmd_SHA256_256.json' assert { type: 'json' };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { jubjub, findGroupHash } from '../lib/esm/jubjub.js';
|
import { jubjub, findGroupHash } from '../esm/jubjub.js';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
const Point = jubjub.ExtendedPoint;
|
const Point = jubjub.ExtendedPoint;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { secp192r1, P192 } from '../lib/esm/p192.js';
|
import { secp192r1, secp224r1, P192, P224 } from './_more-curves.helpers.js';
|
||||||
import { secp224r1, P224 } from '../lib/esm/p224.js';
|
import { secp256r1, P256 } from '../esm/p256.js';
|
||||||
import { secp256r1, P256 } from '../lib/esm/p256.js';
|
import { secp384r1, P384 } from '../esm/p384.js';
|
||||||
import { secp384r1, P384 } from '../lib/esm/p384.js';
|
import { secp521r1, P521 } from '../esm/p521.js';
|
||||||
import { secp521r1, P521 } from '../lib/esm/p521.js';
|
import { secp256k1 } from '../esm/secp256k1.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { hexToBytes, bytesToHex } from '../esm/abstract/utils.js';
|
||||||
import { hexToBytes, bytesToHex } from '../lib/esm/abstract/utils.js';
|
|
||||||
import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: 'json' };
|
import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: 'json' };
|
||||||
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
|
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
|
||||||
import { default as rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' };
|
import { default as rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' };
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should, describe } from 'micro-should';
|
import { should, describe } from 'micro-should';
|
||||||
import * as poseidon from '../lib/esm/abstract/poseidon.js';
|
import * as poseidon from '../esm/abstract/poseidon.js';
|
||||||
import * as stark from '../lib/esm/stark.js';
|
import * as stark from '../esm/stark.js';
|
||||||
import * as mod from '../lib/esm/abstract/modular.js';
|
import * as mod from '../esm/abstract/modular.js';
|
||||||
import { default as pvectors } from './vectors/poseidon.json' assert { type: 'json' };
|
import { default as pvectors } from './vectors/poseidon.json' assert { type: 'json' };
|
||||||
const { st1, st2, st3, st4 } = pvectors;
|
const { st1, st2, st3, st4 } = pvectors;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { deepStrictEqual, throws } from 'assert';
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { should, describe } from 'micro-should';
|
import { should, describe } from 'micro-should';
|
||||||
import { bytesToHex as hex } from '@noble/hashes/utils';
|
import { bytesToHex as hex } from '@noble/hashes/utils';
|
||||||
import { schnorr } from '../lib/esm/secp256k1.js';
|
import { schnorr } from '../esm/secp256k1.js';
|
||||||
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
||||||
|
|
||||||
describe('schnorr.sign()', () => {
|
describe('schnorr.sign()', () => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export { secp256k1 as secp } from '../lib/esm/secp256k1.js';
|
export { secp256k1 as secp } from '../esm/secp256k1.js';
|
||||||
import { secp256k1 as _secp } from '../lib/esm/secp256k1.js';
|
import { secp256k1 as _secp } from '../esm/secp256k1.js';
|
||||||
export { bytesToNumberBE, numberToBytesBE } from '../lib/esm/abstract/utils.js';
|
export { bytesToNumberBE, numberToBytesBE } from '../esm/abstract/utils.js';
|
||||||
export { mod } from '../lib/esm/abstract/modular.js';
|
export { mod } from '../esm/abstract/modular.js';
|
||||||
export const sigFromDER = (der) => {
|
export const sigFromDER = (der) => {
|
||||||
return _secp.Signature.fromDER(der);
|
return _secp.Signature.fromDER(der);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
import * as starknet from '../../esm/stark.js';
|
||||||
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
||||||
import * as bip32 from '@scure/bip32';
|
import * as bip32 from '@scure/bip32';
|
||||||
import * as bip39 from '@scure/bip39';
|
import * as bip39 from '@scure/bip39';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as microStark from '../../../lib/esm/stark.js';
|
import * as microStark from '../../../esm/stark.js';
|
||||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||||
import * as bench from 'micro-bmark';
|
import * as bench from 'micro-bmark';
|
||||||
const { run, mark } = bench; // or bench.mark
|
const { run, mark } = bench; // or bench.mark
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
|
import { describe, should } from 'micro-should';
|
||||||
import './basic.test.js';
|
import './basic.test.js';
|
||||||
import './stark.test.js';
|
import './stark.test.js';
|
||||||
import './property.test.js';
|
import './property.test.js';
|
||||||
import './poseidon.test.js';
|
import './poseidon.test.js';
|
||||||
|
|
||||||
|
// ESM is broken.
|
||||||
|
import url from 'url';
|
||||||
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
should.run();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
import * as starknet from '../../esm/stark.js';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
function parseTest(path) {
|
function parseTest(path) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
import * as starknet from '../../esm/stark.js';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
|
|
||||||
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
|
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { utf8ToBytes } from '@noble/hashes/utils.js';
|
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||||
import * as bip32 from '@scure/bip32';
|
import * as bip32 from '@scure/bip32';
|
||||||
import * as bip39 from '@scure/bip39';
|
import * as bip39 from '@scure/bip39';
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
import * as starknet from '../../esm/stark.js';
|
||||||
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
|
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
|
||||||
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
|
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user