@noble/curves 1.4.2 → 1.6.0
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 +159 -128
- package/_shortw_utils.d.ts.map +1 -1
- package/abstract/bls.d.ts +37 -34
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +167 -115
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +14 -1
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +77 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +12 -0
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +84 -75
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +4 -2
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +4 -0
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +13 -2
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +4 -9
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +107 -0
- package/abstract/tower.d.ts.map +1 -0
- package/abstract/tower.js +498 -0
- package/abstract/tower.js.map +1 -0
- package/abstract/utils.d.ts +17 -0
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +50 -1
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +25 -3
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +189 -113
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +1 -65
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +48 -575
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +10 -6
- package/bn254.d.ts.map +1 -1
- package/bn254.js +207 -10
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +7 -4
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +3 -0
- package/ed25519.js.map +1 -1
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/abstract/bls.d.ts +37 -34
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +168 -116
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +14 -1
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +77 -8
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +12 -0
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +87 -78
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +4 -2
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +4 -0
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +12 -2
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -10
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +107 -0
- package/esm/abstract/tower.d.ts.map +1 -0
- package/esm/abstract/tower.js +494 -0
- package/esm/abstract/tower.js.map +1 -0
- package/esm/abstract/utils.d.ts +17 -0
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +44 -0
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +25 -3
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +191 -115
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +1 -65
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +50 -577
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +10 -6
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +206 -9
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +7 -4
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +3 -0
- package/esm/ed25519.js.map +1 -1
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +8 -2
- package/esm/jubjub.js.map +1 -1
- package/esm/p256.d.ts.map +1 -1
- package/esm/p384.d.ts.map +1 -1
- package/esm/p521.d.ts.map +1 -1
- package/esm/secp256k1.d.ts +6 -0
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +17 -13
- package/esm/secp256k1.js.map +1 -1
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +8 -2
- package/jubjub.js.map +1 -1
- package/p256.d.ts.map +1 -1
- package/p384.d.ts.map +1 -1
- package/p521.d.ts.map +1 -1
- package/package.json +27 -19
- package/secp256k1.d.ts +6 -0
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +16 -12
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +222 -168
- package/src/abstract/curve.ts +80 -8
- package/src/abstract/edwards.ts +97 -70
- package/src/abstract/hash-to-curve.ts +3 -1
- package/src/abstract/modular.ts +13 -3
- package/src/abstract/montgomery.ts +11 -10
- package/src/abstract/tower.ts +605 -0
- package/src/abstract/utils.ts +49 -0
- package/src/abstract/weierstrass.ts +179 -104
- package/src/bls12-381.ts +53 -707
- package/src/bn254.ts +224 -9
- package/src/ed25519.ts +5 -2
- package/src/jubjub.ts +7 -2
- package/src/secp256k1.ts +24 -12
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Short Weierstrass curve. The formula is: y² = x³ + ax + b
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AffinePoint,
|
|
5
|
+
BasicCurve,
|
|
6
|
+
Group,
|
|
7
|
+
GroupConstructor,
|
|
8
|
+
validateBasic,
|
|
9
|
+
wNAF,
|
|
10
|
+
pippenger,
|
|
11
|
+
} from './curve.js';
|
|
4
12
|
import * as mod from './modular.js';
|
|
5
13
|
import * as ut from './utils.js';
|
|
6
|
-
import { CHash, Hex, PrivKey, ensureBytes } from './utils.js';
|
|
14
|
+
import { CHash, Hex, PrivKey, ensureBytes, memoized, abool } from './utils.js';
|
|
7
15
|
|
|
8
16
|
export type { AffinePoint };
|
|
9
17
|
type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
|
@@ -31,6 +39,11 @@ type Entropy = Hex | boolean;
|
|
|
31
39
|
export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
|
|
32
40
|
export type VerOpts = { lowS?: boolean; prehash?: boolean };
|
|
33
41
|
|
|
42
|
+
function validateSigVerOpts(opts: SignOpts | VerOpts) {
|
|
43
|
+
if (opts.lowS !== undefined) abool('lowS', opts.lowS);
|
|
44
|
+
if (opts.prehash !== undefined) abool('prehash', opts.prehash);
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
/**
|
|
35
48
|
* ### Design rationale for types
|
|
36
49
|
*
|
|
@@ -80,6 +93,7 @@ export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
|
|
80
93
|
fromHex(hex: Hex): ProjPointType<T>;
|
|
81
94
|
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
|
|
82
95
|
normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
|
|
96
|
+
msm(points: ProjPointType<T>[], scalars: bigint[]): ProjPointType<T>;
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
export type CurvePointsType<T> = BasicWCurve<T> & {
|
|
@@ -130,8 +144,15 @@ export type CurvePointsRes<T> = {
|
|
|
130
144
|
isWithinCurveOrder: (num: bigint) => boolean;
|
|
131
145
|
};
|
|
132
146
|
|
|
133
|
-
// ASN.1 DER encoding utilities
|
|
134
147
|
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
|
|
151
|
+
*
|
|
152
|
+
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
|
|
153
|
+
*
|
|
154
|
+
* Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
|
|
155
|
+
*/
|
|
135
156
|
export const DER = {
|
|
136
157
|
// asn.1 DER encoding utils
|
|
137
158
|
Err: class DERErr extends Error {
|
|
@@ -139,48 +160,84 @@ export const DER = {
|
|
|
139
160
|
super(m);
|
|
140
161
|
}
|
|
141
162
|
},
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
163
|
+
// Basic building block is TLV (Tag-Length-Value)
|
|
164
|
+
_tlv: {
|
|
165
|
+
encode: (tag: number, data: string) => {
|
|
166
|
+
const { Err: E } = DER;
|
|
167
|
+
if (tag < 0 || tag > 256) throw new E('tlv.encode: wrong tag');
|
|
168
|
+
if (data.length & 1) throw new E('tlv.encode: unpadded data');
|
|
169
|
+
const dataLen = data.length / 2;
|
|
170
|
+
const len = ut.numberToHexUnpadded(dataLen);
|
|
171
|
+
if ((len.length / 2) & 0b1000_0000) throw new E('tlv.encode: long form length too big');
|
|
172
|
+
// length of length with long form flag
|
|
173
|
+
const lenLen = dataLen > 127 ? ut.numberToHexUnpadded((len.length / 2) | 0b1000_0000) : '';
|
|
174
|
+
return `${ut.numberToHexUnpadded(tag)}${lenLen}${len}${data}`;
|
|
175
|
+
},
|
|
176
|
+
// v - value, l - left bytes (unparsed)
|
|
177
|
+
decode(tag: number, data: Uint8Array): { v: Uint8Array; l: Uint8Array } {
|
|
178
|
+
const { Err: E } = DER;
|
|
179
|
+
let pos = 0;
|
|
180
|
+
if (tag < 0 || tag > 256) throw new E('tlv.encode: wrong tag');
|
|
181
|
+
if (data.length < 2 || data[pos++] !== tag) throw new E('tlv.decode: wrong tlv');
|
|
182
|
+
const first = data[pos++];
|
|
183
|
+
const isLong = !!(first & 0b1000_0000); // First bit of first length byte is flag for short/long form
|
|
184
|
+
let length = 0;
|
|
185
|
+
if (!isLong) length = first;
|
|
186
|
+
else {
|
|
187
|
+
// Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]
|
|
188
|
+
const lenLen = first & 0b0111_1111;
|
|
189
|
+
if (!lenLen) throw new E('tlv.decode(long): indefinite length not supported');
|
|
190
|
+
if (lenLen > 4) throw new E('tlv.decode(long): byte length is too big'); // this will overflow u32 in js
|
|
191
|
+
const lengthBytes = data.subarray(pos, pos + lenLen);
|
|
192
|
+
if (lengthBytes.length !== lenLen) throw new E('tlv.decode: length bytes not complete');
|
|
193
|
+
if (lengthBytes[0] === 0) throw new E('tlv.decode(long): zero leftmost byte');
|
|
194
|
+
for (const b of lengthBytes) length = (length << 8) | b;
|
|
195
|
+
pos += lenLen;
|
|
196
|
+
if (length < 128) throw new E('tlv.decode(long): not minimal encoding');
|
|
197
|
+
}
|
|
198
|
+
const v = data.subarray(pos, pos + length);
|
|
199
|
+
if (v.length !== length) throw new E('tlv.decode: wrong value length');
|
|
200
|
+
return { v, l: data.subarray(pos + length) };
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
|
204
|
+
// since we always use positive integers here. It must always be empty:
|
|
205
|
+
// - add zero byte if exists
|
|
206
|
+
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
|
|
207
|
+
_int: {
|
|
208
|
+
encode(num: bigint) {
|
|
209
|
+
const { Err: E } = DER;
|
|
210
|
+
if (num < _0n) throw new E('integer: negative integers are not allowed');
|
|
211
|
+
let hex = ut.numberToHexUnpadded(num);
|
|
212
|
+
// Pad with zero byte if negative flag is present
|
|
213
|
+
if (Number.parseInt(hex[0], 16) & 0b1000) hex = '00' + hex;
|
|
214
|
+
if (hex.length & 1) throw new E('unexpected assertion');
|
|
215
|
+
return hex;
|
|
216
|
+
},
|
|
217
|
+
decode(data: Uint8Array): bigint {
|
|
218
|
+
const { Err: E } = DER;
|
|
219
|
+
if (data[0] & 0b1000_0000) throw new E('Invalid signature integer: negative');
|
|
220
|
+
if (data[0] === 0x00 && !(data[1] & 0b1000_0000))
|
|
221
|
+
throw new E('Invalid signature integer: unnecessary leading zero');
|
|
222
|
+
return b2n(data);
|
|
223
|
+
},
|
|
156
224
|
},
|
|
157
225
|
toSig(hex: string | Uint8Array): { r: bigint; s: bigint } {
|
|
158
226
|
// parse DER signature
|
|
159
|
-
const { Err: E } = DER;
|
|
227
|
+
const { Err: E, _int: int, _tlv: tlv } = DER;
|
|
160
228
|
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
|
161
229
|
ut.abytes(data);
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
const {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return { r, s };
|
|
230
|
+
const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);
|
|
231
|
+
if (seqLeftBytes.length) throw new E('Invalid signature: left bytes after parsing');
|
|
232
|
+
const { v: rBytes, l: rLeftBytes } = tlv.decode(0x02, seqBytes);
|
|
233
|
+
const { v: sBytes, l: sLeftBytes } = tlv.decode(0x02, rLeftBytes);
|
|
234
|
+
if (sLeftBytes.length) throw new E('Invalid signature: left bytes after parsing');
|
|
235
|
+
return { r: int.decode(rBytes), s: int.decode(sBytes) };
|
|
169
236
|
},
|
|
170
237
|
hexFromSig(sig: { r: bigint; s: bigint }): string {
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
const hex = num.toString(16);
|
|
175
|
-
return hex.length & 1 ? `0${hex}` : hex;
|
|
176
|
-
};
|
|
177
|
-
const s = slice(h(sig.s));
|
|
178
|
-
const r = slice(h(sig.r));
|
|
179
|
-
const shl = s.length / 2;
|
|
180
|
-
const rhl = r.length / 2;
|
|
181
|
-
const sl = h(shl);
|
|
182
|
-
const rl = h(rhl);
|
|
183
|
-
return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
|
|
238
|
+
const { _tlv: tlv, _int: int } = DER;
|
|
239
|
+
const seq = `${tlv.encode(0x02, int.encode(sig.r))}${tlv.encode(0x02, int.encode(sig.s))}`;
|
|
240
|
+
return tlv.encode(0x30, seq);
|
|
184
241
|
},
|
|
185
242
|
};
|
|
186
243
|
|
|
@@ -191,6 +248,7 @@ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n =
|
|
|
191
248
|
export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T> {
|
|
192
249
|
const CURVE = validatePointOpts(opts);
|
|
193
250
|
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
251
|
+
const Fn = mod.Field(CURVE.n, CURVE.nBitLength);
|
|
194
252
|
|
|
195
253
|
const toBytes =
|
|
196
254
|
CURVE.toBytes ||
|
|
@@ -228,15 +286,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
228
286
|
|
|
229
287
|
// Valid group elements reside in range 1..n-1
|
|
230
288
|
function isWithinCurveOrder(num: bigint): boolean {
|
|
231
|
-
return
|
|
232
|
-
}
|
|
233
|
-
function assertGE(num: bigint) {
|
|
234
|
-
if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
|
289
|
+
return ut.inRange(num, _1n, CURVE.n);
|
|
235
290
|
}
|
|
236
291
|
// Validates if priv key is valid and converts it to bigint.
|
|
237
292
|
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
238
293
|
function normPrivateKeyToScalar(key: PrivKey): bigint {
|
|
239
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
|
|
294
|
+
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
240
295
|
if (lengths && typeof key !== 'bigint') {
|
|
241
296
|
if (ut.isBytes(key)) key = ut.bytesToHex(key);
|
|
242
297
|
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
|
@@ -252,15 +307,56 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
252
307
|
} catch (error) {
|
|
253
308
|
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
|
|
254
309
|
}
|
|
255
|
-
if (wrapPrivateKey) num = mod.mod(num,
|
|
256
|
-
|
|
310
|
+
if (wrapPrivateKey) num = mod.mod(num, N); // disabled by default, enabled for BLS
|
|
311
|
+
ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
|
|
257
312
|
return num;
|
|
258
313
|
}
|
|
259
314
|
|
|
260
|
-
const pointPrecomputes = new Map<Point, Point[]>();
|
|
261
315
|
function assertPrjPoint(other: unknown) {
|
|
262
316
|
if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
|
|
263
317
|
}
|
|
318
|
+
|
|
319
|
+
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
320
|
+
|
|
321
|
+
// Converts Projective point to affine (x, y) coordinates.
|
|
322
|
+
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
323
|
+
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
324
|
+
const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
|
|
325
|
+
const { px: x, py: y, pz: z } = p;
|
|
326
|
+
// Fast-path for normalized points
|
|
327
|
+
if (Fp.eql(z, Fp.ONE)) return { x, y };
|
|
328
|
+
const is0 = p.is0();
|
|
329
|
+
// If invZ was 0, we return zero point. However we still want to execute
|
|
330
|
+
// all operations, so we replace invZ with a random number, 1.
|
|
331
|
+
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
332
|
+
const ax = Fp.mul(x, iz);
|
|
333
|
+
const ay = Fp.mul(y, iz);
|
|
334
|
+
const zz = Fp.mul(z, iz);
|
|
335
|
+
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
|
|
336
|
+
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
|
337
|
+
return { x: ax, y: ay };
|
|
338
|
+
});
|
|
339
|
+
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
340
|
+
// Otherwise true will be return
|
|
341
|
+
const assertValidMemo = memoized((p: Point) => {
|
|
342
|
+
if (p.is0()) {
|
|
343
|
+
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
344
|
+
// In BLS, ZERO can be serialized, so we allow it.
|
|
345
|
+
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
|
346
|
+
if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return;
|
|
347
|
+
throw new Error('bad point: ZERO');
|
|
348
|
+
}
|
|
349
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
350
|
+
const { x, y } = p.toAffine();
|
|
351
|
+
// Check if x, y are valid field elements
|
|
352
|
+
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
|
|
353
|
+
const left = Fp.sqr(y); // y²
|
|
354
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
355
|
+
if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
|
|
356
|
+
if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
357
|
+
return true;
|
|
358
|
+
});
|
|
359
|
+
|
|
264
360
|
/**
|
|
265
361
|
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
|
266
362
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
@@ -278,6 +374,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
278
374
|
if (px == null || !Fp.isValid(px)) throw new Error('x required');
|
|
279
375
|
if (py == null || !Fp.isValid(py)) throw new Error('y required');
|
|
280
376
|
if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
|
|
377
|
+
Object.freeze(this);
|
|
281
378
|
}
|
|
282
379
|
|
|
283
380
|
// Does not validate if the point is on-curve.
|
|
@@ -325,35 +422,21 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
325
422
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
326
423
|
}
|
|
327
424
|
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
425
|
+
// Multiscalar Multiplication
|
|
426
|
+
static msm(points: Point[], scalars: bigint[]) {
|
|
427
|
+
return pippenger(Point, Fn, points, scalars);
|
|
428
|
+
}
|
|
332
429
|
|
|
333
430
|
// "Private method", don't use it directly
|
|
334
431
|
_setWindowSize(windowSize: number) {
|
|
335
|
-
this
|
|
336
|
-
pointPrecomputes.delete(this);
|
|
432
|
+
wnaf.setWindowSize(this, windowSize);
|
|
337
433
|
}
|
|
338
434
|
|
|
339
435
|
// A point on curve is valid if it conforms to equation.
|
|
340
436
|
assertValidity(): void {
|
|
341
|
-
|
|
342
|
-
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
343
|
-
// In BLS, ZERO can be serialized, so we allow it.
|
|
344
|
-
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
|
345
|
-
if (CURVE.allowInfinityPoint && !Fp.is0(this.py)) return;
|
|
346
|
-
throw new Error('bad point: ZERO');
|
|
347
|
-
}
|
|
348
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
349
|
-
const { x, y } = this.toAffine();
|
|
350
|
-
// Check if x, y are valid field elements
|
|
351
|
-
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
|
|
352
|
-
const left = Fp.sqr(y); // y²
|
|
353
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
354
|
-
if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
|
|
355
|
-
if (!this.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
|
|
437
|
+
assertValidMemo(this);
|
|
356
438
|
}
|
|
439
|
+
|
|
357
440
|
hasEvenY(): boolean {
|
|
358
441
|
const { y } = this.toAffine();
|
|
359
442
|
if (Fp.isOdd) return !Fp.isOdd(y);
|
|
@@ -480,14 +563,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
480
563
|
return this.add(other.negate());
|
|
481
564
|
}
|
|
482
565
|
|
|
483
|
-
|
|
566
|
+
is0() {
|
|
484
567
|
return this.equals(Point.ZERO);
|
|
485
568
|
}
|
|
486
569
|
private wNAF(n: bigint): { p: Point; f: Point } {
|
|
487
|
-
return wnaf.wNAFCached(this,
|
|
488
|
-
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
|
|
489
|
-
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
490
|
-
});
|
|
570
|
+
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
491
571
|
}
|
|
492
572
|
|
|
493
573
|
/**
|
|
@@ -495,16 +575,16 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
495
575
|
* It's faster, but should only be used when you don't care about
|
|
496
576
|
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
497
577
|
*/
|
|
498
|
-
multiplyUnsafe(
|
|
578
|
+
multiplyUnsafe(sc: bigint): Point {
|
|
579
|
+
ut.aInRange('scalar', sc, _0n, CURVE.n);
|
|
499
580
|
const I = Point.ZERO;
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
if (n === _1n) return this;
|
|
581
|
+
if (sc === _0n) return I;
|
|
582
|
+
if (sc === _1n) return this;
|
|
503
583
|
const { endo } = CURVE;
|
|
504
|
-
if (!endo) return wnaf.unsafeLadder(this,
|
|
584
|
+
if (!endo) return wnaf.unsafeLadder(this, sc);
|
|
505
585
|
|
|
506
586
|
// Apply endomorphism
|
|
507
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(
|
|
587
|
+
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
508
588
|
let k1p = I;
|
|
509
589
|
let k2p = I;
|
|
510
590
|
let d: Point = this;
|
|
@@ -531,12 +611,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
531
611
|
* @returns New point
|
|
532
612
|
*/
|
|
533
613
|
multiply(scalar: bigint): Point {
|
|
534
|
-
|
|
535
|
-
|
|
614
|
+
const { endo, n: N } = CURVE;
|
|
615
|
+
ut.aInRange('scalar', scalar, _1n, N);
|
|
536
616
|
let point: Point, fake: Point; // Fake point is used to const-time mult
|
|
537
|
-
const { endo } = CURVE;
|
|
538
617
|
if (endo) {
|
|
539
|
-
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(
|
|
618
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
540
619
|
let { p: k1p, f: f1p } = this.wNAF(k1);
|
|
541
620
|
let { p: k2p, f: f2p } = this.wNAF(k2);
|
|
542
621
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
@@ -545,7 +624,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
545
624
|
point = k1p.add(k2p);
|
|
546
625
|
fake = f1p.add(f2p);
|
|
547
626
|
} else {
|
|
548
|
-
const { p, f } = this.wNAF(
|
|
627
|
+
const { p, f } = this.wNAF(scalar);
|
|
549
628
|
point = p;
|
|
550
629
|
fake = f;
|
|
551
630
|
}
|
|
@@ -573,17 +652,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
573
652
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
574
653
|
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
575
654
|
toAffine(iz?: T): AffinePoint<T> {
|
|
576
|
-
|
|
577
|
-
const is0 = this.is0();
|
|
578
|
-
// If invZ was 0, we return zero point. However we still want to execute
|
|
579
|
-
// all operations, so we replace invZ with a random number, 1.
|
|
580
|
-
if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
581
|
-
const ax = Fp.mul(x, iz);
|
|
582
|
-
const ay = Fp.mul(y, iz);
|
|
583
|
-
const zz = Fp.mul(z, iz);
|
|
584
|
-
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
|
|
585
|
-
if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
|
586
|
-
return { x: ax, y: ay };
|
|
655
|
+
return toAffineMemo(this, iz);
|
|
587
656
|
}
|
|
588
657
|
isTorsionFree(): boolean {
|
|
589
658
|
const { h: cofactor, isTorsionFree } = CURVE;
|
|
@@ -599,11 +668,13 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
|
|
|
599
668
|
}
|
|
600
669
|
|
|
601
670
|
toRawBytes(isCompressed = true): Uint8Array {
|
|
671
|
+
abool('isCompressed', isCompressed);
|
|
602
672
|
this.assertValidity();
|
|
603
673
|
return toBytes(Point, this, isCompressed);
|
|
604
674
|
}
|
|
605
675
|
|
|
606
676
|
toHex(isCompressed = true): string {
|
|
677
|
+
abool('isCompressed', isCompressed);
|
|
607
678
|
return ut.bytesToHex(this.toRawBytes(isCompressed));
|
|
608
679
|
}
|
|
609
680
|
}
|
|
@@ -691,15 +762,19 @@ export type CurveFn = {
|
|
|
691
762
|
};
|
|
692
763
|
};
|
|
693
764
|
|
|
765
|
+
/**
|
|
766
|
+
* Creates short weierstrass curve and ECDSA signature methods for it.
|
|
767
|
+
* @example
|
|
768
|
+
* import { Field } from '@noble/curves/abstract/modular';
|
|
769
|
+
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
|
|
770
|
+
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
|
|
771
|
+
*/
|
|
694
772
|
export function weierstrass(curveDef: CurveType): CurveFn {
|
|
695
773
|
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
|
696
774
|
const { Fp, n: CURVE_ORDER } = CURVE;
|
|
697
775
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
698
776
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
699
777
|
|
|
700
|
-
function isValidFieldElement(num: bigint): boolean {
|
|
701
|
-
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
|
|
702
|
-
}
|
|
703
778
|
function modN(a: bigint) {
|
|
704
779
|
return mod.mod(a, CURVE_ORDER);
|
|
705
780
|
}
|
|
@@ -718,6 +793,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
718
793
|
const a = point.toAffine();
|
|
719
794
|
const x = Fp.toBytes(a.x);
|
|
720
795
|
const cat = ut.concatBytes;
|
|
796
|
+
abool('isCompressed', isCompressed);
|
|
721
797
|
if (isCompressed) {
|
|
722
798
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
723
799
|
} else {
|
|
@@ -731,7 +807,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
731
807
|
// this.assertValidity() is done inside of fromHex
|
|
732
808
|
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
733
809
|
const x = ut.bytesToNumberBE(tail);
|
|
734
|
-
if (!
|
|
810
|
+
if (!ut.inRange(x, _1n, Fp.ORDER)) throw new Error('Point is not on curve');
|
|
735
811
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
736
812
|
let y: bigint;
|
|
737
813
|
try {
|
|
@@ -797,9 +873,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
797
873
|
}
|
|
798
874
|
|
|
799
875
|
assertValidity(): void {
|
|
800
|
-
//
|
|
801
|
-
|
|
802
|
-
if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < CURVE.n');
|
|
876
|
+
ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
|
|
877
|
+
ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
|
|
803
878
|
}
|
|
804
879
|
|
|
805
880
|
addRecoveryBit(recovery: number): RecoveredSignature {
|
|
@@ -949,9 +1024,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
949
1024
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
950
1025
|
*/
|
|
951
1026
|
function int2octets(num: bigint): Uint8Array {
|
|
952
|
-
|
|
953
|
-
if (!(_0n <= num && num < ORDER_MASK))
|
|
954
|
-
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
|
|
1027
|
+
ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
|
|
955
1028
|
// works with order, can have different size than numToField!
|
|
956
1029
|
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
|
957
1030
|
}
|
|
@@ -968,6 +1041,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
968
1041
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
969
1042
|
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
970
1043
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
1044
|
+
validateSigVerOpts(opts);
|
|
971
1045
|
if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
972
1046
|
|
|
973
1047
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
@@ -1058,6 +1132,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1058
1132
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
1059
1133
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
1060
1134
|
if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
|
|
1135
|
+
validateSigVerOpts(opts);
|
|
1061
1136
|
const { lowS, prehash } = opts;
|
|
1062
1137
|
|
|
1063
1138
|
let _sig: Signature | undefined = undefined;
|