@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,15 @@
|
|
|
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 { validateBasic, wNAF } from './curve.js';
|
|
3
|
+
import { validateBasic, wNAF, pippenger, } from './curve.js';
|
|
4
4
|
import * as mod from './modular.js';
|
|
5
5
|
import * as ut from './utils.js';
|
|
6
|
-
import { ensureBytes } from './utils.js';
|
|
6
|
+
import { ensureBytes, memoized, abool } from './utils.js';
|
|
7
|
+
function validateSigVerOpts(opts) {
|
|
8
|
+
if (opts.lowS !== undefined)
|
|
9
|
+
abool('lowS', opts.lowS);
|
|
10
|
+
if (opts.prehash !== undefined)
|
|
11
|
+
abool('prehash', opts.prehash);
|
|
12
|
+
}
|
|
7
13
|
function validatePointOpts(curve) {
|
|
8
14
|
const opts = validateBasic(curve);
|
|
9
15
|
ut.validateObject(opts, {
|
|
@@ -31,8 +37,14 @@ function validatePointOpts(curve) {
|
|
|
31
37
|
}
|
|
32
38
|
return Object.freeze({ ...opts });
|
|
33
39
|
}
|
|
34
|
-
// ASN.1 DER encoding utilities
|
|
35
40
|
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
41
|
+
/**
|
|
42
|
+
* ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
|
|
43
|
+
*
|
|
44
|
+
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
|
|
45
|
+
*
|
|
46
|
+
* Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
|
|
47
|
+
*/
|
|
36
48
|
export const DER = {
|
|
37
49
|
// asn.1 DER encoding utils
|
|
38
50
|
Err: class DERErr extends Error {
|
|
@@ -40,54 +52,103 @@ export const DER = {
|
|
|
40
52
|
super(m);
|
|
41
53
|
}
|
|
42
54
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
// Basic building block is TLV (Tag-Length-Value)
|
|
56
|
+
_tlv: {
|
|
57
|
+
encode: (tag, data) => {
|
|
58
|
+
const { Err: E } = DER;
|
|
59
|
+
if (tag < 0 || tag > 256)
|
|
60
|
+
throw new E('tlv.encode: wrong tag');
|
|
61
|
+
if (data.length & 1)
|
|
62
|
+
throw new E('tlv.encode: unpadded data');
|
|
63
|
+
const dataLen = data.length / 2;
|
|
64
|
+
const len = ut.numberToHexUnpadded(dataLen);
|
|
65
|
+
if ((len.length / 2) & 128)
|
|
66
|
+
throw new E('tlv.encode: long form length too big');
|
|
67
|
+
// length of length with long form flag
|
|
68
|
+
const lenLen = dataLen > 127 ? ut.numberToHexUnpadded((len.length / 2) | 128) : '';
|
|
69
|
+
return `${ut.numberToHexUnpadded(tag)}${lenLen}${len}${data}`;
|
|
70
|
+
},
|
|
71
|
+
// v - value, l - left bytes (unparsed)
|
|
72
|
+
decode(tag, data) {
|
|
73
|
+
const { Err: E } = DER;
|
|
74
|
+
let pos = 0;
|
|
75
|
+
if (tag < 0 || tag > 256)
|
|
76
|
+
throw new E('tlv.encode: wrong tag');
|
|
77
|
+
if (data.length < 2 || data[pos++] !== tag)
|
|
78
|
+
throw new E('tlv.decode: wrong tlv');
|
|
79
|
+
const first = data[pos++];
|
|
80
|
+
const isLong = !!(first & 128); // First bit of first length byte is flag for short/long form
|
|
81
|
+
let length = 0;
|
|
82
|
+
if (!isLong)
|
|
83
|
+
length = first;
|
|
84
|
+
else {
|
|
85
|
+
// Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]
|
|
86
|
+
const lenLen = first & 127;
|
|
87
|
+
if (!lenLen)
|
|
88
|
+
throw new E('tlv.decode(long): indefinite length not supported');
|
|
89
|
+
if (lenLen > 4)
|
|
90
|
+
throw new E('tlv.decode(long): byte length is too big'); // this will overflow u32 in js
|
|
91
|
+
const lengthBytes = data.subarray(pos, pos + lenLen);
|
|
92
|
+
if (lengthBytes.length !== lenLen)
|
|
93
|
+
throw new E('tlv.decode: length bytes not complete');
|
|
94
|
+
if (lengthBytes[0] === 0)
|
|
95
|
+
throw new E('tlv.decode(long): zero leftmost byte');
|
|
96
|
+
for (const b of lengthBytes)
|
|
97
|
+
length = (length << 8) | b;
|
|
98
|
+
pos += lenLen;
|
|
99
|
+
if (length < 128)
|
|
100
|
+
throw new E('tlv.decode(long): not minimal encoding');
|
|
101
|
+
}
|
|
102
|
+
const v = data.subarray(pos, pos + length);
|
|
103
|
+
if (v.length !== length)
|
|
104
|
+
throw new E('tlv.decode: wrong value length');
|
|
105
|
+
return { v, l: data.subarray(pos + length) };
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
|
109
|
+
// since we always use positive integers here. It must always be empty:
|
|
110
|
+
// - add zero byte if exists
|
|
111
|
+
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
|
|
112
|
+
_int: {
|
|
113
|
+
encode(num) {
|
|
114
|
+
const { Err: E } = DER;
|
|
115
|
+
if (num < _0n)
|
|
116
|
+
throw new E('integer: negative integers are not allowed');
|
|
117
|
+
let hex = ut.numberToHexUnpadded(num);
|
|
118
|
+
// Pad with zero byte if negative flag is present
|
|
119
|
+
if (Number.parseInt(hex[0], 16) & 0b1000)
|
|
120
|
+
hex = '00' + hex;
|
|
121
|
+
if (hex.length & 1)
|
|
122
|
+
throw new E('unexpected assertion');
|
|
123
|
+
return hex;
|
|
124
|
+
},
|
|
125
|
+
decode(data) {
|
|
126
|
+
const { Err: E } = DER;
|
|
127
|
+
if (data[0] & 128)
|
|
128
|
+
throw new E('Invalid signature integer: negative');
|
|
129
|
+
if (data[0] === 0x00 && !(data[1] & 128))
|
|
130
|
+
throw new E('Invalid signature integer: unnecessary leading zero');
|
|
131
|
+
return b2n(data);
|
|
132
|
+
},
|
|
60
133
|
},
|
|
61
134
|
toSig(hex) {
|
|
62
135
|
// parse DER signature
|
|
63
|
-
const { Err: E } = DER;
|
|
136
|
+
const { Err: E, _int: int, _tlv: tlv } = DER;
|
|
64
137
|
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
|
65
138
|
ut.abytes(data);
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
throw new E('Invalid signature
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const { d: s, l: rBytesLeft } = DER._parseInt(sBytes);
|
|
73
|
-
if (rBytesLeft.length)
|
|
139
|
+
const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);
|
|
140
|
+
if (seqLeftBytes.length)
|
|
141
|
+
throw new E('Invalid signature: left bytes after parsing');
|
|
142
|
+
const { v: rBytes, l: rLeftBytes } = tlv.decode(0x02, seqBytes);
|
|
143
|
+
const { v: sBytes, l: sLeftBytes } = tlv.decode(0x02, rLeftBytes);
|
|
144
|
+
if (sLeftBytes.length)
|
|
74
145
|
throw new E('Invalid signature: left bytes after parsing');
|
|
75
|
-
return { r, s };
|
|
146
|
+
return { r: int.decode(rBytes), s: int.decode(sBytes) };
|
|
76
147
|
},
|
|
77
148
|
hexFromSig(sig) {
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
const hex = num.toString(16);
|
|
82
|
-
return hex.length & 1 ? `0${hex}` : hex;
|
|
83
|
-
};
|
|
84
|
-
const s = slice(h(sig.s));
|
|
85
|
-
const r = slice(h(sig.r));
|
|
86
|
-
const shl = s.length / 2;
|
|
87
|
-
const rhl = r.length / 2;
|
|
88
|
-
const sl = h(shl);
|
|
89
|
-
const rl = h(rhl);
|
|
90
|
-
return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
|
|
149
|
+
const { _tlv: tlv, _int: int } = DER;
|
|
150
|
+
const seq = `${tlv.encode(0x02, int.encode(sig.r))}${tlv.encode(0x02, int.encode(sig.s))}`;
|
|
151
|
+
return tlv.encode(0x30, seq);
|
|
91
152
|
},
|
|
92
153
|
};
|
|
93
154
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
@@ -96,6 +157,7 @@ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n =
|
|
|
96
157
|
export function weierstrassPoints(opts) {
|
|
97
158
|
const CURVE = validatePointOpts(opts);
|
|
98
159
|
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
160
|
+
const Fn = mod.Field(CURVE.n, CURVE.nBitLength);
|
|
99
161
|
const toBytes = CURVE.toBytes ||
|
|
100
162
|
((_c, point, _isCompressed) => {
|
|
101
163
|
const a = point.toAffine();
|
|
@@ -128,16 +190,12 @@ export function weierstrassPoints(opts) {
|
|
|
128
190
|
throw new Error('bad generator point: equation left != right');
|
|
129
191
|
// Valid group elements reside in range 1..n-1
|
|
130
192
|
function isWithinCurveOrder(num) {
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
function assertGE(num) {
|
|
134
|
-
if (!isWithinCurveOrder(num))
|
|
135
|
-
throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
|
193
|
+
return ut.inRange(num, _1n, CURVE.n);
|
|
136
194
|
}
|
|
137
195
|
// Validates if priv key is valid and converts it to bigint.
|
|
138
196
|
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
139
197
|
function normPrivateKeyToScalar(key) {
|
|
140
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
|
|
198
|
+
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
141
199
|
if (lengths && typeof key !== 'bigint') {
|
|
142
200
|
if (ut.isBytes(key))
|
|
143
201
|
key = ut.bytesToHex(key);
|
|
@@ -157,15 +215,61 @@ export function weierstrassPoints(opts) {
|
|
|
157
215
|
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
|
|
158
216
|
}
|
|
159
217
|
if (wrapPrivateKey)
|
|
160
|
-
num = mod.mod(num,
|
|
161
|
-
|
|
218
|
+
num = mod.mod(num, N); // disabled by default, enabled for BLS
|
|
219
|
+
ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
|
|
162
220
|
return num;
|
|
163
221
|
}
|
|
164
|
-
const pointPrecomputes = new Map();
|
|
165
222
|
function assertPrjPoint(other) {
|
|
166
223
|
if (!(other instanceof Point))
|
|
167
224
|
throw new Error('ProjectivePoint expected');
|
|
168
225
|
}
|
|
226
|
+
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
227
|
+
// Converts Projective point to affine (x, y) coordinates.
|
|
228
|
+
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
229
|
+
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
230
|
+
const toAffineMemo = memoized((p, iz) => {
|
|
231
|
+
const { px: x, py: y, pz: z } = p;
|
|
232
|
+
// Fast-path for normalized points
|
|
233
|
+
if (Fp.eql(z, Fp.ONE))
|
|
234
|
+
return { x, y };
|
|
235
|
+
const is0 = p.is0();
|
|
236
|
+
// If invZ was 0, we return zero point. However we still want to execute
|
|
237
|
+
// all operations, so we replace invZ with a random number, 1.
|
|
238
|
+
if (iz == null)
|
|
239
|
+
iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
240
|
+
const ax = Fp.mul(x, iz);
|
|
241
|
+
const ay = Fp.mul(y, iz);
|
|
242
|
+
const zz = Fp.mul(z, iz);
|
|
243
|
+
if (is0)
|
|
244
|
+
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
245
|
+
if (!Fp.eql(zz, Fp.ONE))
|
|
246
|
+
throw new Error('invZ was invalid');
|
|
247
|
+
return { x: ax, y: ay };
|
|
248
|
+
});
|
|
249
|
+
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
250
|
+
// Otherwise true will be return
|
|
251
|
+
const assertValidMemo = memoized((p) => {
|
|
252
|
+
if (p.is0()) {
|
|
253
|
+
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
254
|
+
// In BLS, ZERO can be serialized, so we allow it.
|
|
255
|
+
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
|
256
|
+
if (CURVE.allowInfinityPoint && !Fp.is0(p.py))
|
|
257
|
+
return;
|
|
258
|
+
throw new Error('bad point: ZERO');
|
|
259
|
+
}
|
|
260
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
261
|
+
const { x, y } = p.toAffine();
|
|
262
|
+
// Check if x, y are valid field elements
|
|
263
|
+
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
264
|
+
throw new Error('bad point: x or y not FE');
|
|
265
|
+
const left = Fp.sqr(y); // y²
|
|
266
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
267
|
+
if (!Fp.eql(left, right))
|
|
268
|
+
throw new Error('bad point: equation left != right');
|
|
269
|
+
if (!p.isTorsionFree())
|
|
270
|
+
throw new Error('bad point: not in prime-order subgroup');
|
|
271
|
+
return true;
|
|
272
|
+
});
|
|
169
273
|
/**
|
|
170
274
|
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
|
171
275
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
@@ -182,6 +286,7 @@ export function weierstrassPoints(opts) {
|
|
|
182
286
|
throw new Error('y required');
|
|
183
287
|
if (pz == null || !Fp.isValid(pz))
|
|
184
288
|
throw new Error('z required');
|
|
289
|
+
Object.freeze(this);
|
|
185
290
|
}
|
|
186
291
|
// Does not validate if the point is on-curve.
|
|
187
292
|
// Use fromHex instead, or call assertValidity() later.
|
|
@@ -226,32 +331,17 @@ export function weierstrassPoints(opts) {
|
|
|
226
331
|
static fromPrivateKey(privateKey) {
|
|
227
332
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
228
333
|
}
|
|
334
|
+
// Multiscalar Multiplication
|
|
335
|
+
static msm(points, scalars) {
|
|
336
|
+
return pippenger(Point, Fn, points, scalars);
|
|
337
|
+
}
|
|
229
338
|
// "Private method", don't use it directly
|
|
230
339
|
_setWindowSize(windowSize) {
|
|
231
|
-
this
|
|
232
|
-
pointPrecomputes.delete(this);
|
|
340
|
+
wnaf.setWindowSize(this, windowSize);
|
|
233
341
|
}
|
|
234
342
|
// A point on curve is valid if it conforms to equation.
|
|
235
343
|
assertValidity() {
|
|
236
|
-
|
|
237
|
-
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
238
|
-
// In BLS, ZERO can be serialized, so we allow it.
|
|
239
|
-
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
|
240
|
-
if (CURVE.allowInfinityPoint && !Fp.is0(this.py))
|
|
241
|
-
return;
|
|
242
|
-
throw new Error('bad point: ZERO');
|
|
243
|
-
}
|
|
244
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
245
|
-
const { x, y } = this.toAffine();
|
|
246
|
-
// Check if x, y are valid field elements
|
|
247
|
-
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
248
|
-
throw new Error('bad point: x or y not FE');
|
|
249
|
-
const left = Fp.sqr(y); // y²
|
|
250
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
251
|
-
if (!Fp.eql(left, right))
|
|
252
|
-
throw new Error('bad point: equation left != right');
|
|
253
|
-
if (!this.isTorsionFree())
|
|
254
|
-
throw new Error('bad point: not in prime-order subgroup');
|
|
344
|
+
assertValidMemo(this);
|
|
255
345
|
}
|
|
256
346
|
hasEvenY() {
|
|
257
347
|
const { y } = this.toAffine();
|
|
@@ -378,28 +468,25 @@ export function weierstrassPoints(opts) {
|
|
|
378
468
|
return this.equals(Point.ZERO);
|
|
379
469
|
}
|
|
380
470
|
wNAF(n) {
|
|
381
|
-
return wnaf.wNAFCached(this,
|
|
382
|
-
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
|
|
383
|
-
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
384
|
-
});
|
|
471
|
+
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
385
472
|
}
|
|
386
473
|
/**
|
|
387
474
|
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
388
475
|
* It's faster, but should only be used when you don't care about
|
|
389
476
|
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
390
477
|
*/
|
|
391
|
-
multiplyUnsafe(
|
|
478
|
+
multiplyUnsafe(sc) {
|
|
479
|
+
ut.aInRange('scalar', sc, _0n, CURVE.n);
|
|
392
480
|
const I = Point.ZERO;
|
|
393
|
-
if (
|
|
481
|
+
if (sc === _0n)
|
|
394
482
|
return I;
|
|
395
|
-
|
|
396
|
-
if (n === _1n)
|
|
483
|
+
if (sc === _1n)
|
|
397
484
|
return this;
|
|
398
485
|
const { endo } = CURVE;
|
|
399
486
|
if (!endo)
|
|
400
|
-
return wnaf.unsafeLadder(this,
|
|
487
|
+
return wnaf.unsafeLadder(this, sc);
|
|
401
488
|
// Apply endomorphism
|
|
402
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(
|
|
489
|
+
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
403
490
|
let k1p = I;
|
|
404
491
|
let k2p = I;
|
|
405
492
|
let d = this;
|
|
@@ -429,12 +516,11 @@ export function weierstrassPoints(opts) {
|
|
|
429
516
|
* @returns New point
|
|
430
517
|
*/
|
|
431
518
|
multiply(scalar) {
|
|
432
|
-
|
|
433
|
-
|
|
519
|
+
const { endo, n: N } = CURVE;
|
|
520
|
+
ut.aInRange('scalar', scalar, _1n, N);
|
|
434
521
|
let point, fake; // Fake point is used to const-time mult
|
|
435
|
-
const { endo } = CURVE;
|
|
436
522
|
if (endo) {
|
|
437
|
-
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(
|
|
523
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
438
524
|
let { p: k1p, f: f1p } = this.wNAF(k1);
|
|
439
525
|
let { p: k2p, f: f2p } = this.wNAF(k2);
|
|
440
526
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
@@ -444,7 +530,7 @@ export function weierstrassPoints(opts) {
|
|
|
444
530
|
fake = f1p.add(f2p);
|
|
445
531
|
}
|
|
446
532
|
else {
|
|
447
|
-
const { p, f } = this.wNAF(
|
|
533
|
+
const { p, f } = this.wNAF(scalar);
|
|
448
534
|
point = p;
|
|
449
535
|
fake = f;
|
|
450
536
|
}
|
|
@@ -468,20 +554,7 @@ export function weierstrassPoints(opts) {
|
|
|
468
554
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
469
555
|
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
470
556
|
toAffine(iz) {
|
|
471
|
-
|
|
472
|
-
const is0 = this.is0();
|
|
473
|
-
// If invZ was 0, we return zero point. However we still want to execute
|
|
474
|
-
// all operations, so we replace invZ with a random number, 1.
|
|
475
|
-
if (iz == null)
|
|
476
|
-
iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
477
|
-
const ax = Fp.mul(x, iz);
|
|
478
|
-
const ay = Fp.mul(y, iz);
|
|
479
|
-
const zz = Fp.mul(z, iz);
|
|
480
|
-
if (is0)
|
|
481
|
-
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
482
|
-
if (!Fp.eql(zz, Fp.ONE))
|
|
483
|
-
throw new Error('invZ was invalid');
|
|
484
|
-
return { x: ax, y: ay };
|
|
557
|
+
return toAffineMemo(this, iz);
|
|
485
558
|
}
|
|
486
559
|
isTorsionFree() {
|
|
487
560
|
const { h: cofactor, isTorsionFree } = CURVE;
|
|
@@ -500,10 +573,12 @@ export function weierstrassPoints(opts) {
|
|
|
500
573
|
return this.multiplyUnsafe(CURVE.h);
|
|
501
574
|
}
|
|
502
575
|
toRawBytes(isCompressed = true) {
|
|
576
|
+
abool('isCompressed', isCompressed);
|
|
503
577
|
this.assertValidity();
|
|
504
578
|
return toBytes(Point, this, isCompressed);
|
|
505
579
|
}
|
|
506
580
|
toHex(isCompressed = true) {
|
|
581
|
+
abool('isCompressed', isCompressed);
|
|
507
582
|
return ut.bytesToHex(this.toRawBytes(isCompressed));
|
|
508
583
|
}
|
|
509
584
|
}
|
|
@@ -533,14 +608,18 @@ function validateOpts(curve) {
|
|
|
533
608
|
});
|
|
534
609
|
return Object.freeze({ lowS: true, ...opts });
|
|
535
610
|
}
|
|
611
|
+
/**
|
|
612
|
+
* Creates short weierstrass curve and ECDSA signature methods for it.
|
|
613
|
+
* @example
|
|
614
|
+
* import { Field } from '@noble/curves/abstract/modular';
|
|
615
|
+
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
|
|
616
|
+
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
|
|
617
|
+
*/
|
|
536
618
|
export function weierstrass(curveDef) {
|
|
537
619
|
const CURVE = validateOpts(curveDef);
|
|
538
620
|
const { Fp, n: CURVE_ORDER } = CURVE;
|
|
539
621
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
540
622
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
541
|
-
function isValidFieldElement(num) {
|
|
542
|
-
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
|
|
543
|
-
}
|
|
544
623
|
function modN(a) {
|
|
545
624
|
return mod.mod(a, CURVE_ORDER);
|
|
546
625
|
}
|
|
@@ -553,6 +632,7 @@ export function weierstrass(curveDef) {
|
|
|
553
632
|
const a = point.toAffine();
|
|
554
633
|
const x = Fp.toBytes(a.x);
|
|
555
634
|
const cat = ut.concatBytes;
|
|
635
|
+
abool('isCompressed', isCompressed);
|
|
556
636
|
if (isCompressed) {
|
|
557
637
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
558
638
|
}
|
|
@@ -567,7 +647,7 @@ export function weierstrass(curveDef) {
|
|
|
567
647
|
// this.assertValidity() is done inside of fromHex
|
|
568
648
|
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
569
649
|
const x = ut.bytesToNumberBE(tail);
|
|
570
|
-
if (!
|
|
650
|
+
if (!ut.inRange(x, _1n, Fp.ORDER))
|
|
571
651
|
throw new Error('Point is not on curve');
|
|
572
652
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
573
653
|
let y;
|
|
@@ -628,11 +708,8 @@ export function weierstrass(curveDef) {
|
|
|
628
708
|
return new Signature(r, s);
|
|
629
709
|
}
|
|
630
710
|
assertValidity() {
|
|
631
|
-
//
|
|
632
|
-
|
|
633
|
-
throw new Error('r must be 0 < r < CURVE.n');
|
|
634
|
-
if (!isWithinCurveOrder(this.s))
|
|
635
|
-
throw new Error('s must be 0 < s < CURVE.n');
|
|
711
|
+
ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
|
|
712
|
+
ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
|
|
636
713
|
}
|
|
637
714
|
addRecoveryBit(recovery) {
|
|
638
715
|
return new Signature(this.r, this.s, recovery);
|
|
@@ -775,10 +852,7 @@ export function weierstrass(curveDef) {
|
|
|
775
852
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
776
853
|
*/
|
|
777
854
|
function int2octets(num) {
|
|
778
|
-
|
|
779
|
-
throw new Error('bigint expected');
|
|
780
|
-
if (!(_0n <= num && num < ORDER_MASK))
|
|
781
|
-
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
|
|
855
|
+
ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
|
|
782
856
|
// works with order, can have different size than numToField!
|
|
783
857
|
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
|
784
858
|
}
|
|
@@ -795,6 +869,7 @@ export function weierstrass(curveDef) {
|
|
|
795
869
|
if (lowS == null)
|
|
796
870
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
797
871
|
msgHash = ensureBytes('msgHash', msgHash);
|
|
872
|
+
validateSigVerOpts(opts);
|
|
798
873
|
if (prehash)
|
|
799
874
|
msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
|
800
875
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
@@ -881,6 +956,7 @@ export function weierstrass(curveDef) {
|
|
|
881
956
|
publicKey = ensureBytes('publicKey', publicKey);
|
|
882
957
|
if ('strict' in opts)
|
|
883
958
|
throw new Error('options.strict was renamed to lowS');
|
|
959
|
+
validateSigVerOpts(opts);
|
|
884
960
|
const { lowS, prehash } = opts;
|
|
885
961
|
let _sig = undefined;
|
|
886
962
|
let P;
|