@noble/curves 2.0.0-beta.1 → 2.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +443 -276
- package/abstract/bls.d.ts +17 -17
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +14 -9
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +9 -3
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +7 -9
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +12 -16
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +32 -31
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +15 -14
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +7 -5
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +3 -3
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +9 -13
- package/abstract/montgomery.js.map +1 -1
- package/abstract/oprf.d.ts +4 -4
- package/abstract/oprf.d.ts.map +1 -1
- package/abstract/oprf.js +3 -3
- package/abstract/oprf.js.map +1 -1
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +8 -9
- package/abstract/poseidon.js.map +1 -1
- package/abstract/utils.d.ts +74 -1
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +67 -17
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +66 -20
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +72 -68
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +3 -9
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +3 -14
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +4 -4
- package/bn254.d.ts.map +1 -1
- package/bn254.js +1 -1
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +22 -18
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +59 -31
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +17 -8
- package/ed448.d.ts.map +1 -1
- package/ed448.js +69 -52
- package/ed448.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +20 -4
- package/index.js.map +1 -1
- package/misc.d.ts.map +1 -1
- package/misc.js +6 -8
- package/misc.js.map +1 -1
- package/nist.d.ts +20 -2
- package/nist.d.ts.map +1 -1
- package/nist.js +30 -10
- package/nist.js.map +1 -1
- package/package.json +34 -37
- package/secp256k1.d.ts +10 -7
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +15 -16
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +22 -22
- package/src/abstract/curve.ts +19 -5
- package/src/abstract/edwards.ts +20 -23
- package/src/abstract/hash-to-curve.ts +50 -51
- package/src/abstract/modular.ts +7 -5
- package/src/abstract/montgomery.ts +12 -18
- package/src/abstract/oprf.ts +6 -6
- package/src/abstract/poseidon.ts +6 -8
- package/src/abstract/weierstrass.ts +139 -89
- package/src/bls12-381.ts +4 -15
- package/src/bn254.ts +7 -7
- package/src/ed25519.ts +65 -40
- package/src/ed448.ts +87 -69
- package/src/index.ts +19 -3
- package/src/misc.ts +7 -8
- package/src/nist.ts +31 -15
- package/src/secp256k1.ts +16 -18
- package/src/utils.ts +33 -83
- package/src/webcrypto.ts +148 -107
- package/utils.d.ts +5 -21
- package/utils.d.ts.map +1 -1
- package/utils.js +31 -74
- package/utils.js.map +1 -1
- package/webcrypto.d.ts +73 -21
- package/webcrypto.d.ts.map +1 -1
- package/webcrypto.js +101 -76
- package/webcrypto.js.map +1 -1
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
|
|
29
29
|
import { ahash } from '@noble/hashes/utils.js';
|
|
30
30
|
import {
|
|
31
|
-
_validateObject,
|
|
32
31
|
abool,
|
|
33
32
|
abytes,
|
|
34
33
|
aInRange,
|
|
@@ -42,12 +41,14 @@ import {
|
|
|
42
41
|
isBytes,
|
|
43
42
|
memoized,
|
|
44
43
|
numberToHexUnpadded,
|
|
44
|
+
validateObject,
|
|
45
45
|
randomBytes as wcRandomBytes,
|
|
46
46
|
type CHash,
|
|
47
47
|
type Signer,
|
|
48
48
|
} from '../utils.ts';
|
|
49
49
|
import {
|
|
50
|
-
|
|
50
|
+
createCurveFields,
|
|
51
|
+
createKeygen,
|
|
51
52
|
mulEndoUnsafe,
|
|
52
53
|
negateCt,
|
|
53
54
|
normalizeZ,
|
|
@@ -66,7 +67,6 @@ import {
|
|
|
66
67
|
} from './modular.ts';
|
|
67
68
|
|
|
68
69
|
export type { AffinePoint };
|
|
69
|
-
export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
70
70
|
|
|
71
71
|
type EndoBasis = [[bigint, bigint], [bigint, bigint]];
|
|
72
72
|
/**
|
|
@@ -129,26 +129,71 @@ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): Scalar
|
|
|
129
129
|
return { k1neg, k1, k2neg, k2 };
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Option to enable hedged signatures with improved security.
|
|
134
|
+
*
|
|
135
|
+
* * Randomly generated k is bad, because broken CSPRNG would leak private keys.
|
|
136
|
+
* * Deterministic k (RFC6979) is better; but is suspectible to fault attacks.
|
|
137
|
+
*
|
|
138
|
+
* We allow using technique described in RFC6979 3.6: additional k', a.k.a. adding randomness
|
|
139
|
+
* to deterministic sig. If CSPRNG is broken & randomness is weak, it would STILL be as secure
|
|
140
|
+
* as ordinary sig without ExtraEntropy.
|
|
141
|
+
*
|
|
142
|
+
* * `true` means "fetch data, from CSPRNG, incorporate it into k generation"
|
|
143
|
+
* * `false` means "disable extra entropy, use purely deterministic k"
|
|
144
|
+
* * `Uint8Array` passed means "incorporate following data into k generation"
|
|
145
|
+
*
|
|
146
|
+
* https://paulmillr.com/posts/deterministic-signatures/
|
|
147
|
+
*/
|
|
148
|
+
export type ECDSAExtraEntropy = boolean | Uint8Array;
|
|
149
|
+
/**
|
|
150
|
+
* - `compact` is the default format
|
|
151
|
+
* - `recovered` is the same as compact, but with an extra byte indicating recovery byte
|
|
152
|
+
* - `der` is ASN.1 DER encoding
|
|
153
|
+
*/
|
|
154
|
+
export type ECDSASignatureFormat = 'compact' | 'recovered' | 'der';
|
|
155
|
+
/**
|
|
156
|
+
* - `prehash`: (default: true) indicates whether to do sha256(message).
|
|
157
|
+
* When a custom hash is used, it must be set to `false`.
|
|
158
|
+
*/
|
|
133
159
|
export type ECDSARecoverOpts = {
|
|
134
160
|
prehash?: boolean;
|
|
135
161
|
};
|
|
162
|
+
/**
|
|
163
|
+
* - `prehash`: (default: true) indicates whether to do sha256(message).
|
|
164
|
+
* When a custom hash is used, it must be set to `false`.
|
|
165
|
+
* - `lowS`: (default: true) prohibits signatures which have (sig.s >= CURVE.n/2n).
|
|
166
|
+
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
|
|
167
|
+
* which is default openssl behavior.
|
|
168
|
+
* Non-malleable signatures can still be successfully verified in openssl.
|
|
169
|
+
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
|
|
170
|
+
*/
|
|
136
171
|
export type ECDSAVerifyOpts = {
|
|
137
172
|
prehash?: boolean;
|
|
138
173
|
lowS?: boolean;
|
|
139
|
-
format?:
|
|
174
|
+
format?: ECDSASignatureFormat;
|
|
140
175
|
};
|
|
176
|
+
/**
|
|
177
|
+
* - `prehash`: (default: true) indicates whether to do sha256(message).
|
|
178
|
+
* When a custom hash is used, it must be set to `false`.
|
|
179
|
+
* - `lowS`: (default: true) prohibits signatures which have (sig.s >= CURVE.n/2n).
|
|
180
|
+
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
|
|
181
|
+
* which is default openssl behavior.
|
|
182
|
+
* Non-malleable signatures can still be successfully verified in openssl.
|
|
183
|
+
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
|
|
184
|
+
* - `extraEntropy`: (default: false) creates sigs with increased security, see {@link ECDSAExtraEntropy}
|
|
185
|
+
*/
|
|
141
186
|
export type ECDSASignOpts = {
|
|
142
187
|
prehash?: boolean;
|
|
143
188
|
lowS?: boolean;
|
|
144
|
-
format?:
|
|
145
|
-
extraEntropy?:
|
|
189
|
+
format?: ECDSASignatureFormat;
|
|
190
|
+
extraEntropy?: ECDSAExtraEntropy;
|
|
146
191
|
};
|
|
147
192
|
|
|
148
|
-
function validateSigFormat(format: string):
|
|
193
|
+
function validateSigFormat(format: string): ECDSASignatureFormat {
|
|
149
194
|
if (!['compact', 'recovered', 'der'].includes(format))
|
|
150
195
|
throw new Error('Signature format must be "compact", "recovered", or "der"');
|
|
151
|
-
return format as
|
|
196
|
+
return format as ECDSASignatureFormat;
|
|
152
197
|
}
|
|
153
198
|
|
|
154
199
|
function validateSigOpts<T extends ECDSASignOpts, D extends Required<ECDSASignOpts>>(
|
|
@@ -214,7 +259,6 @@ export type WeierstrassOpts<T> = Readonly<{
|
|
|
214
259
|
// When a cofactor != 1, there can be an effective methods to:
|
|
215
260
|
// 1. Determine whether a point is torsion-free
|
|
216
261
|
// 2. Clear torsion component
|
|
217
|
-
// wrapPrivateKey: bls12-381 requires mod(n) instead of rejecting keys >= n
|
|
218
262
|
export type WeierstrassExtraOpts<T> = Partial<{
|
|
219
263
|
Fp: IField<T>;
|
|
220
264
|
Fn: IField<bigint>;
|
|
@@ -240,7 +284,7 @@ export type WeierstrassExtraOpts<T> = Partial<{
|
|
|
240
284
|
*/
|
|
241
285
|
export type ECDSAOpts = Partial<{
|
|
242
286
|
lowS: boolean;
|
|
243
|
-
hmac:
|
|
287
|
+
hmac: (key: Uint8Array, message: Uint8Array) => Uint8Array;
|
|
244
288
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
|
245
289
|
bits2int: (bytes: Uint8Array) => bigint;
|
|
246
290
|
bits2int_modN: (bytes: Uint8Array) => bigint;
|
|
@@ -407,29 +451,31 @@ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n =
|
|
|
407
451
|
/**
|
|
408
452
|
* Creates weierstrass Point constructor, based on specified curve options.
|
|
409
453
|
*
|
|
454
|
+
* See {@link WeierstrassOpts}.
|
|
455
|
+
*
|
|
410
456
|
* @example
|
|
411
457
|
```js
|
|
412
458
|
const opts = {
|
|
413
|
-
p:
|
|
414
|
-
n:
|
|
415
|
-
h:
|
|
416
|
-
a:
|
|
417
|
-
b:
|
|
418
|
-
Gx:
|
|
419
|
-
Gy:
|
|
459
|
+
p: 0xfffffffffffffffffffffffffffffffeffffac73n,
|
|
460
|
+
n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
|
|
461
|
+
h: 1n,
|
|
462
|
+
a: 0n,
|
|
463
|
+
b: 7n,
|
|
464
|
+
Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
|
|
465
|
+
Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
|
|
420
466
|
};
|
|
421
|
-
const
|
|
467
|
+
const secp160k1_Point = weierstrass(opts);
|
|
422
468
|
```
|
|
423
469
|
*/
|
|
424
470
|
export function weierstrass<T>(
|
|
425
471
|
params: WeierstrassOpts<T>,
|
|
426
472
|
extraOpts: WeierstrassExtraOpts<T> = {}
|
|
427
473
|
): WeierstrassPointCons<T> {
|
|
428
|
-
const validated =
|
|
474
|
+
const validated = createCurveFields('weierstrass', params, extraOpts);
|
|
429
475
|
const { Fp, Fn } = validated;
|
|
430
476
|
let CURVE = validated.CURVE as WeierstrassOpts<T>;
|
|
431
477
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
432
|
-
|
|
478
|
+
validateObject(
|
|
433
479
|
extraOpts,
|
|
434
480
|
{},
|
|
435
481
|
{
|
|
@@ -439,7 +485,6 @@ export function weierstrass<T>(
|
|
|
439
485
|
fromBytes: 'function',
|
|
440
486
|
toBytes: 'function',
|
|
441
487
|
endo: 'object',
|
|
442
|
-
wrapPrivateKey: 'boolean',
|
|
443
488
|
}
|
|
444
489
|
);
|
|
445
490
|
|
|
@@ -493,9 +538,9 @@ export function weierstrass<T>(
|
|
|
493
538
|
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
494
539
|
}
|
|
495
540
|
assertCompressionIsSupported();
|
|
496
|
-
const
|
|
497
|
-
const
|
|
498
|
-
if (
|
|
541
|
+
const evenY = Fp.isOdd!(y);
|
|
542
|
+
const evenH = (head & 1) === 1; // ECDSA-specific
|
|
543
|
+
if (evenH !== evenY) y = Fp.neg(y);
|
|
499
544
|
return { x, y };
|
|
500
545
|
} else if (length === uncomp && head === 0x04) {
|
|
501
546
|
// TODO: more checks
|
|
@@ -544,7 +589,7 @@ export function weierstrass<T>(
|
|
|
544
589
|
}
|
|
545
590
|
|
|
546
591
|
function aprjpoint(other: unknown) {
|
|
547
|
-
if (!(other instanceof Point)) throw new Error('
|
|
592
|
+
if (!(other instanceof Point)) throw new Error('Weierstrass Point expected');
|
|
548
593
|
}
|
|
549
594
|
|
|
550
595
|
function splitEndoScalarN(k: bigint) {
|
|
@@ -844,9 +889,11 @@ export function weierstrass<T>(
|
|
|
844
889
|
const { endo } = extraOpts;
|
|
845
890
|
const p = this as Point;
|
|
846
891
|
if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
847
|
-
if (sc === _0n || p.is0()) return Point.ZERO;
|
|
848
|
-
if (sc === _1n) return p; //
|
|
849
|
-
if (wnaf.hasCache(this)) return this.multiply(sc);
|
|
892
|
+
if (sc === _0n || p.is0()) return Point.ZERO; // 0
|
|
893
|
+
if (sc === _1n) return p; // 1
|
|
894
|
+
if (wnaf.hasCache(this)) return this.multiply(sc); // precomputes
|
|
895
|
+
// We don't have method for double scalar multiplication (aP + bQ):
|
|
896
|
+
// Even with using Strauss-Shamir trick, it's 35% slower than naïve mul+add.
|
|
850
897
|
if (endo) {
|
|
851
898
|
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|
852
899
|
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
|
|
@@ -856,11 +903,6 @@ export function weierstrass<T>(
|
|
|
856
903
|
}
|
|
857
904
|
}
|
|
858
905
|
|
|
859
|
-
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
|
860
|
-
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
861
|
-
return sum.is0() ? undefined : sum;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
906
|
/**
|
|
865
907
|
* Converts Projective point to affine (x, y) coordinates.
|
|
866
908
|
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
@@ -919,14 +961,15 @@ export interface ECDSASignature {
|
|
|
919
961
|
readonly recovery?: number;
|
|
920
962
|
addRecoveryBit(recovery: number): ECDSASignature & { readonly recovery: number };
|
|
921
963
|
hasHighS(): boolean;
|
|
964
|
+
recoverPublicKey(messageHash: Uint8Array): WeierstrassPoint<bigint>;
|
|
922
965
|
toBytes(format?: string): Uint8Array;
|
|
923
966
|
toHex(format?: string): string;
|
|
924
967
|
}
|
|
925
968
|
/** Methods of ECDSA signature constructor. */
|
|
926
969
|
export type ECDSASignatureCons = {
|
|
927
970
|
new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
|
|
928
|
-
fromBytes(bytes: Uint8Array, format?:
|
|
929
|
-
fromHex(hex: string, format?:
|
|
971
|
+
fromBytes(bytes: Uint8Array, format?: ECDSASignatureFormat): ECDSASignature;
|
|
972
|
+
fromHex(hex: string, format?: ECDSASignatureFormat): ECDSASignature;
|
|
930
973
|
};
|
|
931
974
|
|
|
932
975
|
// Points start with byte 0x02 when y is even; otherwise 0x03
|
|
@@ -1127,11 +1170,6 @@ export function ecdh(
|
|
|
1127
1170
|
return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed);
|
|
1128
1171
|
}
|
|
1129
1172
|
|
|
1130
|
-
function keygen(seed?: Uint8Array) {
|
|
1131
|
-
const secretKey = randomSecretKey(seed);
|
|
1132
|
-
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
1173
|
/**
|
|
1136
1174
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
1137
1175
|
*/
|
|
@@ -1168,17 +1206,17 @@ export function ecdh(
|
|
|
1168
1206
|
isValidPublicKey,
|
|
1169
1207
|
randomSecretKey,
|
|
1170
1208
|
};
|
|
1209
|
+
const keygen = createKeygen(randomSecretKey, getPublicKey);
|
|
1171
1210
|
|
|
1172
1211
|
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
1173
1212
|
}
|
|
1174
1213
|
|
|
1175
1214
|
/**
|
|
1176
1215
|
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
1177
|
-
* We need `hash` for 2 features:
|
|
1178
|
-
* 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
|
|
1179
|
-
* 2. k generation in `sign`, using HMAC-drbg(hash)
|
|
1180
1216
|
*
|
|
1181
|
-
*
|
|
1217
|
+
* @param Point created using {@link weierstrass} function
|
|
1218
|
+
* @param hash used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
|
|
1219
|
+
* @param ecdsaOpts rarely needed, see {@link ECDSAOpts}
|
|
1182
1220
|
*
|
|
1183
1221
|
* @example
|
|
1184
1222
|
* ```js
|
|
@@ -1194,7 +1232,7 @@ export function ecdsa(
|
|
|
1194
1232
|
ecdsaOpts: ECDSAOpts = {}
|
|
1195
1233
|
): ECDSA {
|
|
1196
1234
|
ahash(hash);
|
|
1197
|
-
|
|
1235
|
+
validateObject(
|
|
1198
1236
|
ecdsaOpts,
|
|
1199
1237
|
{},
|
|
1200
1238
|
{
|
|
@@ -1207,9 +1245,7 @@ export function ecdsa(
|
|
|
1207
1245
|
);
|
|
1208
1246
|
ecdsaOpts = Object.assign({}, ecdsaOpts);
|
|
1209
1247
|
const randomBytes = ecdsaOpts.randomBytes || wcRandomBytes;
|
|
1210
|
-
const hmac
|
|
1211
|
-
ecdsaOpts.hmac ||
|
|
1212
|
-
(((key, ...msgs) => nobleHmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
|
|
1248
|
+
const hmac = ecdsaOpts.hmac || ((key, msg) => nobleHmac(hash, key, msg));
|
|
1213
1249
|
|
|
1214
1250
|
const { Fp, Fn } = Point;
|
|
1215
1251
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
@@ -1217,9 +1253,10 @@ export function ecdsa(
|
|
|
1217
1253
|
const defaultSigOpts: Required<ECDSASignOpts> = {
|
|
1218
1254
|
prehash: true,
|
|
1219
1255
|
lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : true,
|
|
1220
|
-
format: 'compact' as
|
|
1256
|
+
format: 'compact' as ECDSASignatureFormat,
|
|
1221
1257
|
extraEntropy: false,
|
|
1222
1258
|
};
|
|
1259
|
+
const hasLargeCofactor = CURVE_ORDER * _2n < Fp.ORDER; // Won't CURVE().h > 2n be more effective?
|
|
1223
1260
|
|
|
1224
1261
|
function isBiggerThanHalfOrder(number: bigint) {
|
|
1225
1262
|
const HALF = CURVE_ORDER >> _1n;
|
|
@@ -1230,7 +1267,19 @@ export function ecdsa(
|
|
|
1230
1267
|
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|
1231
1268
|
return num;
|
|
1232
1269
|
}
|
|
1233
|
-
function
|
|
1270
|
+
function assertSmallCofactor(): void {
|
|
1271
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
1272
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
1273
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
1274
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
1275
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
1276
|
+
// To easily get i, we either need to:
|
|
1277
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
1278
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
1279
|
+
if (hasLargeCofactor)
|
|
1280
|
+
throw new Error('"recovered" sig type is not supported for cofactor >2 curves');
|
|
1281
|
+
}
|
|
1282
|
+
function validateSigLength(bytes: Uint8Array, format: ECDSASignatureFormat) {
|
|
1234
1283
|
validateSigFormat(format);
|
|
1235
1284
|
const size = lengths.signature!;
|
|
1236
1285
|
const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
|
|
@@ -1248,11 +1297,18 @@ export function ecdsa(
|
|
|
1248
1297
|
constructor(r: bigint, s: bigint, recovery?: number) {
|
|
1249
1298
|
this.r = validateRS('r', r); // r in [1..N-1];
|
|
1250
1299
|
this.s = validateRS('s', s); // s in [1..N-1];
|
|
1251
|
-
if (recovery != null)
|
|
1300
|
+
if (recovery != null) {
|
|
1301
|
+
assertSmallCofactor();
|
|
1302
|
+
if (![0, 1, 2, 3].includes(recovery)) throw new Error('invalid recovery id');
|
|
1303
|
+
this.recovery = recovery;
|
|
1304
|
+
}
|
|
1252
1305
|
Object.freeze(this);
|
|
1253
1306
|
}
|
|
1254
1307
|
|
|
1255
|
-
static fromBytes(
|
|
1308
|
+
static fromBytes(
|
|
1309
|
+
bytes: Uint8Array,
|
|
1310
|
+
format: ECDSASignatureFormat = defaultSigOpts.format
|
|
1311
|
+
): Signature {
|
|
1256
1312
|
validateSigLength(bytes, format);
|
|
1257
1313
|
let recid: number | undefined;
|
|
1258
1314
|
if (format === 'der') {
|
|
@@ -1270,41 +1326,34 @@ export function ecdsa(
|
|
|
1270
1326
|
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
1271
1327
|
}
|
|
1272
1328
|
|
|
1273
|
-
static fromHex(hex: string, format?:
|
|
1329
|
+
static fromHex(hex: string, format?: ECDSASignatureFormat) {
|
|
1274
1330
|
return this.fromBytes(hexToBytes(hex), format);
|
|
1275
1331
|
}
|
|
1276
1332
|
|
|
1333
|
+
private assertRecovery(): number {
|
|
1334
|
+
const { recovery } = this;
|
|
1335
|
+
if (recovery == null) throw new Error('invalid recovery id: must be present');
|
|
1336
|
+
return recovery;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1277
1339
|
addRecoveryBit(recovery: number): RecoveredSignature {
|
|
1278
1340
|
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
|
|
1279
1341
|
}
|
|
1280
1342
|
|
|
1281
1343
|
recoverPublicKey(messageHash: Uint8Array): WeierstrassPoint<bigint> {
|
|
1282
|
-
const
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
1287
|
-
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
1288
|
-
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
1289
|
-
// However, for cofactor>1, r+n may not get q.x:
|
|
1290
|
-
// r+n*i would need to be done instead where i is unknown.
|
|
1291
|
-
// To easily get i, we either need to:
|
|
1292
|
-
// a. increase amount of valid recid values (4, 5...); OR
|
|
1293
|
-
// b. prohibit non-prime-order signatures (recid > 1).
|
|
1294
|
-
const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
|
|
1295
|
-
if (hasCofactor && rec > 1) throw new Error('recovery id is ambiguous for h>1 curve');
|
|
1296
|
-
|
|
1297
|
-
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
1298
|
-
if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
|
|
1344
|
+
const { r, s } = this;
|
|
1345
|
+
const recovery = this.assertRecovery();
|
|
1346
|
+
const radj = recovery === 2 || recovery === 3 ? r + CURVE_ORDER : r;
|
|
1347
|
+
if (!Fp.isValid(radj)) throw new Error('invalid recovery id: sig.r+curve.n != R.x');
|
|
1299
1348
|
const x = Fp.toBytes(radj);
|
|
1300
|
-
const R = Point.fromBytes(concatBytes(pprefix((
|
|
1349
|
+
const R = Point.fromBytes(concatBytes(pprefix((recovery & 1) === 0), x));
|
|
1301
1350
|
const ir = Fn.inv(radj); // r^-1
|
|
1302
1351
|
const h = bits2int_modN(abytes(messageHash, undefined, 'msgHash')); // Truncate hash
|
|
1303
1352
|
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
1304
1353
|
const u2 = Fn.create(s * ir); // sr^-1
|
|
1305
1354
|
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
1306
1355
|
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
1307
|
-
if (Q.is0()) throw new Error('point at infinify');
|
|
1356
|
+
if (Q.is0()) throw new Error('invalid recovery: point at infinify');
|
|
1308
1357
|
Q.assertValidity();
|
|
1309
1358
|
return Q;
|
|
1310
1359
|
}
|
|
@@ -1314,19 +1363,20 @@ export function ecdsa(
|
|
|
1314
1363
|
return isBiggerThanHalfOrder(this.s);
|
|
1315
1364
|
}
|
|
1316
1365
|
|
|
1317
|
-
toBytes(format:
|
|
1366
|
+
toBytes(format: ECDSASignatureFormat = defaultSigOpts.format) {
|
|
1318
1367
|
validateSigFormat(format);
|
|
1319
1368
|
if (format === 'der') return hexToBytes(DER.hexFromSig(this));
|
|
1320
|
-
const r =
|
|
1321
|
-
const
|
|
1369
|
+
const { r, s } = this;
|
|
1370
|
+
const rb = Fn.toBytes(r);
|
|
1371
|
+
const sb = Fn.toBytes(s);
|
|
1322
1372
|
if (format === 'recovered') {
|
|
1323
|
-
|
|
1324
|
-
return concatBytes(Uint8Array.of(this.
|
|
1373
|
+
assertSmallCofactor();
|
|
1374
|
+
return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb);
|
|
1325
1375
|
}
|
|
1326
|
-
return concatBytes(
|
|
1376
|
+
return concatBytes(rb, sb);
|
|
1327
1377
|
}
|
|
1328
1378
|
|
|
1329
|
-
toHex(format?:
|
|
1379
|
+
toHex(format?: ECDSASignatureFormat) {
|
|
1330
1380
|
return bytesToHex(this.toBytes(format));
|
|
1331
1381
|
}
|
|
1332
1382
|
}
|
|
@@ -1374,14 +1424,14 @@ export function ecdsa(
|
|
|
1374
1424
|
* Warning: we cannot assume here that message has same amount of bytes as curve order,
|
|
1375
1425
|
* this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
|
|
1376
1426
|
*/
|
|
1377
|
-
function prepSig(message: Uint8Array,
|
|
1427
|
+
function prepSig(message: Uint8Array, secretKey: Uint8Array, opts: ECDSASignOpts) {
|
|
1378
1428
|
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|
1379
1429
|
message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
|
|
1380
1430
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
1381
1431
|
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
1382
1432
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
1383
1433
|
const h1int = bits2int_modN(message);
|
|
1384
|
-
const d = Fn.fromBytes(
|
|
1434
|
+
const d = Fn.fromBytes(secretKey); // validate secret key, convert to bigint
|
|
1385
1435
|
if (!Fn.isValidNot0(d)) throw new Error('invalid private key');
|
|
1386
1436
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
1387
1437
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
@@ -1401,7 +1451,7 @@ export function ecdsa(
|
|
|
1401
1451
|
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
|
1402
1452
|
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
|
1403
1453
|
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
|
1404
|
-
function k2sig(kBytes: Uint8Array):
|
|
1454
|
+
function k2sig(kBytes: Uint8Array): Signature | undefined {
|
|
1405
1455
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
1406
1456
|
// Important: all mod() calls here must be done over N
|
|
1407
1457
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
@@ -1410,15 +1460,15 @@ export function ecdsa(
|
|
|
1410
1460
|
const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
|
|
1411
1461
|
const r = Fn.create(q.x); // r = q.x mod n
|
|
1412
1462
|
if (r === _0n) return;
|
|
1413
|
-
const s = Fn.create(ik * Fn.create(m + r * d)); //
|
|
1463
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // s = k^-1(m + rd) mod n
|
|
1414
1464
|
if (s === _0n) return;
|
|
1415
|
-
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3
|
|
1465
|
+
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3 when q.x>n)
|
|
1416
1466
|
let normS = s;
|
|
1417
1467
|
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
1418
|
-
normS = Fn.neg(s); // if lowS was passed, ensure s is always
|
|
1419
|
-
recovery ^= 1;
|
|
1468
|
+
normS = Fn.neg(s); // if lowS was passed, ensure s is always in the bottom half of N
|
|
1469
|
+
recovery ^= 1;
|
|
1420
1470
|
}
|
|
1421
|
-
return new Signature(r, normS,
|
|
1471
|
+
return new Signature(r, normS, hasLargeCofactor ? undefined : recovery);
|
|
1422
1472
|
}
|
|
1423
1473
|
return { seed, k2sig };
|
|
1424
1474
|
}
|
|
@@ -1436,7 +1486,7 @@ export function ecdsa(
|
|
|
1436
1486
|
*/
|
|
1437
1487
|
function sign(message: Uint8Array, secretKey: Uint8Array, opts: ECDSASignOpts = {}): Uint8Array {
|
|
1438
1488
|
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
1439
|
-
const drbg = createHmacDrbg<
|
|
1489
|
+
const drbg = createHmacDrbg<Signature>(hash.outputLen, Fn.BYTES, hmac);
|
|
1440
1490
|
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1441
1491
|
return sig.toBytes(opts.format);
|
|
1442
1492
|
}
|
package/src/bls12-381.ts
CHANGED
|
@@ -149,6 +149,7 @@ const bls12_381_CURVE_G1: WeierstrassOpts<bigint> = {
|
|
|
149
149
|
|
|
150
150
|
// CURVE FIELDS
|
|
151
151
|
// r = z⁴ − z² + 1; CURVE.n from other curves
|
|
152
|
+
/** bls12-381 Fr (Fn) field. Note: does mod() on fromBytes, due to modFromBytes option. */
|
|
152
153
|
export const bls12_381_Fr: IField<bigint> = Field(bls12_381_CURVE_G1.n, {
|
|
153
154
|
modFromBytes: true,
|
|
154
155
|
});
|
|
@@ -570,30 +571,18 @@ const bls12_hasher_opts = {
|
|
|
570
571
|
hasherOpts: hasher_opts,
|
|
571
572
|
hasherOptsG1: { ...hasher_opts, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
|
|
572
573
|
hasherOptsG2: { ...hasher_opts },
|
|
573
|
-
};
|
|
574
|
+
} as const;
|
|
574
575
|
|
|
575
576
|
const bls12_params = {
|
|
576
577
|
ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
|
|
577
578
|
xNegative: true,
|
|
578
579
|
twistType: 'multiplicative' as const,
|
|
579
580
|
randomBytes: randomBytes,
|
|
580
|
-
// https://datatracker.ietf.org/doc/html/rfc9380#name-clearing-the-cofactor
|
|
581
|
-
// https://datatracker.ietf.org/doc/html/rfc9380#name-cofactor-clearing-for-bls12
|
|
582
|
-
// G2hEff: BigInt(
|
|
583
|
-
// '0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
|
|
584
|
-
// ),
|
|
585
581
|
};
|
|
586
582
|
|
|
587
583
|
/**
|
|
588
|
-
* bls12-381 pairing-friendly curve.
|
|
589
|
-
*
|
|
590
|
-
* import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
|
591
|
-
* // G1 keys, G2 signatures
|
|
592
|
-
* const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
|
|
593
|
-
* const message = '64726e3da8';
|
|
594
|
-
* const publicKey = bls.getPublicKey(privateKey);
|
|
595
|
-
* const signature = bls.sign(message, privateKey);
|
|
596
|
-
* const isValid = bls.verify(signature, message, publicKey);
|
|
584
|
+
* bls12-381 pairing-friendly curve construction.
|
|
585
|
+
* Provides both longSignatures and shortSignatures.
|
|
597
586
|
*/
|
|
598
587
|
export const bls12_381: BlsCurvePairWithSignatures = bls(
|
|
599
588
|
fields,
|
package/src/bn254.ts
CHANGED
|
@@ -35,7 +35,7 @@ because it at least has specs:
|
|
|
35
35
|
- https://github.com/arkworks-rs/curves/blob/master/bn254/src/lib.rs
|
|
36
36
|
- Python implementations use different towers and produce different Fp12 outputs:
|
|
37
37
|
- https://github.com/ethereum/py_pairing
|
|
38
|
-
- https://github.com/ethereum/
|
|
38
|
+
- https://github.com/ethereum/py_ecc/tree/main/py_ecc/bn128
|
|
39
39
|
- Points are encoded differently in different implementations
|
|
40
40
|
|
|
41
41
|
### Params
|
|
@@ -56,9 +56,9 @@ Ate loop size: 6x+2
|
|
|
56
56
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
57
57
|
import {
|
|
58
58
|
blsBasic,
|
|
59
|
-
type
|
|
60
|
-
type
|
|
61
|
-
type
|
|
59
|
+
type BlsCurvePair,
|
|
60
|
+
type BlsPostPrecomputeFn,
|
|
61
|
+
type BlsPostPrecomputePointAddFn,
|
|
62
62
|
} from './abstract/bls.ts';
|
|
63
63
|
import { Field, type IField } from './abstract/modular.ts';
|
|
64
64
|
import type { Fp, Fp12, Fp2, Fp6 } from './abstract/tower.ts';
|
|
@@ -121,13 +121,13 @@ const { Fp, Fp2, Fp6, Fp12 } = tower12({
|
|
|
121
121
|
// END OF CURVE FIELDS
|
|
122
122
|
const { G2psi, psi } = psiFrobenius(Fp, Fp2, Fp2.NONRESIDUE);
|
|
123
123
|
|
|
124
|
-
export const _postPrecompute:
|
|
124
|
+
export const _postPrecompute: BlsPostPrecomputeFn = (
|
|
125
125
|
Rx: Fp2,
|
|
126
126
|
Ry: Fp2,
|
|
127
127
|
Rz: Fp2,
|
|
128
128
|
Qx: Fp2,
|
|
129
129
|
Qy: Fp2,
|
|
130
|
-
pointAdd:
|
|
130
|
+
pointAdd: BlsPostPrecomputePointAddFn
|
|
131
131
|
) => {
|
|
132
132
|
const q = psi(Qx, Qy);
|
|
133
133
|
({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1]));
|
|
@@ -216,4 +216,4 @@ const bn254_params = {
|
|
|
216
216
|
* Contains G1 / G2 operations and pairings.
|
|
217
217
|
*/
|
|
218
218
|
// bn254_hasher
|
|
219
|
-
export const bn254:
|
|
219
|
+
export const bn254: BlsCurvePair = blsBasic(fields, bn254_G1, bn254_G2, bn254_params);
|