@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
package/abstract/weierstrass.js
CHANGED
|
@@ -11,6 +11,12 @@ const curve_js_1 = require("./curve.js");
|
|
|
11
11
|
const mod = require("./modular.js");
|
|
12
12
|
const ut = require("./utils.js");
|
|
13
13
|
const utils_js_1 = require("./utils.js");
|
|
14
|
+
function validateSigVerOpts(opts) {
|
|
15
|
+
if (opts.lowS !== undefined)
|
|
16
|
+
(0, utils_js_1.abool)('lowS', opts.lowS);
|
|
17
|
+
if (opts.prehash !== undefined)
|
|
18
|
+
(0, utils_js_1.abool)('prehash', opts.prehash);
|
|
19
|
+
}
|
|
14
20
|
function validatePointOpts(curve) {
|
|
15
21
|
const opts = (0, curve_js_1.validateBasic)(curve);
|
|
16
22
|
ut.validateObject(opts, {
|
|
@@ -38,8 +44,14 @@ function validatePointOpts(curve) {
|
|
|
38
44
|
}
|
|
39
45
|
return Object.freeze({ ...opts });
|
|
40
46
|
}
|
|
41
|
-
// ASN.1 DER encoding utilities
|
|
42
47
|
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
48
|
+
/**
|
|
49
|
+
* ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
|
|
50
|
+
*
|
|
51
|
+
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
|
|
52
|
+
*
|
|
53
|
+
* Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
|
|
54
|
+
*/
|
|
43
55
|
exports.DER = {
|
|
44
56
|
// asn.1 DER encoding utils
|
|
45
57
|
Err: class DERErr extends Error {
|
|
@@ -47,54 +59,103 @@ exports.DER = {
|
|
|
47
59
|
super(m);
|
|
48
60
|
}
|
|
49
61
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
// Basic building block is TLV (Tag-Length-Value)
|
|
63
|
+
_tlv: {
|
|
64
|
+
encode: (tag, data) => {
|
|
65
|
+
const { Err: E } = exports.DER;
|
|
66
|
+
if (tag < 0 || tag > 256)
|
|
67
|
+
throw new E('tlv.encode: wrong tag');
|
|
68
|
+
if (data.length & 1)
|
|
69
|
+
throw new E('tlv.encode: unpadded data');
|
|
70
|
+
const dataLen = data.length / 2;
|
|
71
|
+
const len = ut.numberToHexUnpadded(dataLen);
|
|
72
|
+
if ((len.length / 2) & 128)
|
|
73
|
+
throw new E('tlv.encode: long form length too big');
|
|
74
|
+
// length of length with long form flag
|
|
75
|
+
const lenLen = dataLen > 127 ? ut.numberToHexUnpadded((len.length / 2) | 128) : '';
|
|
76
|
+
return `${ut.numberToHexUnpadded(tag)}${lenLen}${len}${data}`;
|
|
77
|
+
},
|
|
78
|
+
// v - value, l - left bytes (unparsed)
|
|
79
|
+
decode(tag, data) {
|
|
80
|
+
const { Err: E } = exports.DER;
|
|
81
|
+
let pos = 0;
|
|
82
|
+
if (tag < 0 || tag > 256)
|
|
83
|
+
throw new E('tlv.encode: wrong tag');
|
|
84
|
+
if (data.length < 2 || data[pos++] !== tag)
|
|
85
|
+
throw new E('tlv.decode: wrong tlv');
|
|
86
|
+
const first = data[pos++];
|
|
87
|
+
const isLong = !!(first & 128); // First bit of first length byte is flag for short/long form
|
|
88
|
+
let length = 0;
|
|
89
|
+
if (!isLong)
|
|
90
|
+
length = first;
|
|
91
|
+
else {
|
|
92
|
+
// Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]
|
|
93
|
+
const lenLen = first & 127;
|
|
94
|
+
if (!lenLen)
|
|
95
|
+
throw new E('tlv.decode(long): indefinite length not supported');
|
|
96
|
+
if (lenLen > 4)
|
|
97
|
+
throw new E('tlv.decode(long): byte length is too big'); // this will overflow u32 in js
|
|
98
|
+
const lengthBytes = data.subarray(pos, pos + lenLen);
|
|
99
|
+
if (lengthBytes.length !== lenLen)
|
|
100
|
+
throw new E('tlv.decode: length bytes not complete');
|
|
101
|
+
if (lengthBytes[0] === 0)
|
|
102
|
+
throw new E('tlv.decode(long): zero leftmost byte');
|
|
103
|
+
for (const b of lengthBytes)
|
|
104
|
+
length = (length << 8) | b;
|
|
105
|
+
pos += lenLen;
|
|
106
|
+
if (length < 128)
|
|
107
|
+
throw new E('tlv.decode(long): not minimal encoding');
|
|
108
|
+
}
|
|
109
|
+
const v = data.subarray(pos, pos + length);
|
|
110
|
+
if (v.length !== length)
|
|
111
|
+
throw new E('tlv.decode: wrong value length');
|
|
112
|
+
return { v, l: data.subarray(pos + length) };
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
|
116
|
+
// since we always use positive integers here. It must always be empty:
|
|
117
|
+
// - add zero byte if exists
|
|
118
|
+
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
|
|
119
|
+
_int: {
|
|
120
|
+
encode(num) {
|
|
121
|
+
const { Err: E } = exports.DER;
|
|
122
|
+
if (num < _0n)
|
|
123
|
+
throw new E('integer: negative integers are not allowed');
|
|
124
|
+
let hex = ut.numberToHexUnpadded(num);
|
|
125
|
+
// Pad with zero byte if negative flag is present
|
|
126
|
+
if (Number.parseInt(hex[0], 16) & 0b1000)
|
|
127
|
+
hex = '00' + hex;
|
|
128
|
+
if (hex.length & 1)
|
|
129
|
+
throw new E('unexpected assertion');
|
|
130
|
+
return hex;
|
|
131
|
+
},
|
|
132
|
+
decode(data) {
|
|
133
|
+
const { Err: E } = exports.DER;
|
|
134
|
+
if (data[0] & 128)
|
|
135
|
+
throw new E('Invalid signature integer: negative');
|
|
136
|
+
if (data[0] === 0x00 && !(data[1] & 128))
|
|
137
|
+
throw new E('Invalid signature integer: unnecessary leading zero');
|
|
138
|
+
return b2n(data);
|
|
139
|
+
},
|
|
67
140
|
},
|
|
68
141
|
toSig(hex) {
|
|
69
142
|
// parse DER signature
|
|
70
|
-
const { Err: E } = exports.DER;
|
|
143
|
+
const { Err: E, _int: int, _tlv: tlv } = exports.DER;
|
|
71
144
|
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
|
72
145
|
ut.abytes(data);
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
throw new E('Invalid signature
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const { d: s, l: rBytesLeft } = exports.DER._parseInt(sBytes);
|
|
80
|
-
if (rBytesLeft.length)
|
|
146
|
+
const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);
|
|
147
|
+
if (seqLeftBytes.length)
|
|
148
|
+
throw new E('Invalid signature: left bytes after parsing');
|
|
149
|
+
const { v: rBytes, l: rLeftBytes } = tlv.decode(0x02, seqBytes);
|
|
150
|
+
const { v: sBytes, l: sLeftBytes } = tlv.decode(0x02, rLeftBytes);
|
|
151
|
+
if (sLeftBytes.length)
|
|
81
152
|
throw new E('Invalid signature: left bytes after parsing');
|
|
82
|
-
return { r, s };
|
|
153
|
+
return { r: int.decode(rBytes), s: int.decode(sBytes) };
|
|
83
154
|
},
|
|
84
155
|
hexFromSig(sig) {
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
const hex = num.toString(16);
|
|
89
|
-
return hex.length & 1 ? `0${hex}` : hex;
|
|
90
|
-
};
|
|
91
|
-
const s = slice(h(sig.s));
|
|
92
|
-
const r = slice(h(sig.r));
|
|
93
|
-
const shl = s.length / 2;
|
|
94
|
-
const rhl = r.length / 2;
|
|
95
|
-
const sl = h(shl);
|
|
96
|
-
const rl = h(rhl);
|
|
97
|
-
return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
|
|
156
|
+
const { _tlv: tlv, _int: int } = exports.DER;
|
|
157
|
+
const seq = `${tlv.encode(0x02, int.encode(sig.r))}${tlv.encode(0x02, int.encode(sig.s))}`;
|
|
158
|
+
return tlv.encode(0x30, seq);
|
|
98
159
|
},
|
|
99
160
|
};
|
|
100
161
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
@@ -103,6 +164,7 @@ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n =
|
|
|
103
164
|
function weierstrassPoints(opts) {
|
|
104
165
|
const CURVE = validatePointOpts(opts);
|
|
105
166
|
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
167
|
+
const Fn = mod.Field(CURVE.n, CURVE.nBitLength);
|
|
106
168
|
const toBytes = CURVE.toBytes ||
|
|
107
169
|
((_c, point, _isCompressed) => {
|
|
108
170
|
const a = point.toAffine();
|
|
@@ -135,16 +197,12 @@ function weierstrassPoints(opts) {
|
|
|
135
197
|
throw new Error('bad generator point: equation left != right');
|
|
136
198
|
// Valid group elements reside in range 1..n-1
|
|
137
199
|
function isWithinCurveOrder(num) {
|
|
138
|
-
return
|
|
139
|
-
}
|
|
140
|
-
function assertGE(num) {
|
|
141
|
-
if (!isWithinCurveOrder(num))
|
|
142
|
-
throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
|
200
|
+
return ut.inRange(num, _1n, CURVE.n);
|
|
143
201
|
}
|
|
144
202
|
// Validates if priv key is valid and converts it to bigint.
|
|
145
203
|
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
|
146
204
|
function normPrivateKeyToScalar(key) {
|
|
147
|
-
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
|
|
205
|
+
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
|
|
148
206
|
if (lengths && typeof key !== 'bigint') {
|
|
149
207
|
if (ut.isBytes(key))
|
|
150
208
|
key = ut.bytesToHex(key);
|
|
@@ -164,15 +222,61 @@ function weierstrassPoints(opts) {
|
|
|
164
222
|
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
|
|
165
223
|
}
|
|
166
224
|
if (wrapPrivateKey)
|
|
167
|
-
num = mod.mod(num,
|
|
168
|
-
|
|
225
|
+
num = mod.mod(num, N); // disabled by default, enabled for BLS
|
|
226
|
+
ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
|
|
169
227
|
return num;
|
|
170
228
|
}
|
|
171
|
-
const pointPrecomputes = new Map();
|
|
172
229
|
function assertPrjPoint(other) {
|
|
173
230
|
if (!(other instanceof Point))
|
|
174
231
|
throw new Error('ProjectivePoint expected');
|
|
175
232
|
}
|
|
233
|
+
// Memoized toAffine / validity check. They are heavy. Points are immutable.
|
|
234
|
+
// Converts Projective point to affine (x, y) coordinates.
|
|
235
|
+
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
236
|
+
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
237
|
+
const toAffineMemo = (0, utils_js_1.memoized)((p, iz) => {
|
|
238
|
+
const { px: x, py: y, pz: z } = p;
|
|
239
|
+
// Fast-path for normalized points
|
|
240
|
+
if (Fp.eql(z, Fp.ONE))
|
|
241
|
+
return { x, y };
|
|
242
|
+
const is0 = p.is0();
|
|
243
|
+
// If invZ was 0, we return zero point. However we still want to execute
|
|
244
|
+
// all operations, so we replace invZ with a random number, 1.
|
|
245
|
+
if (iz == null)
|
|
246
|
+
iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
247
|
+
const ax = Fp.mul(x, iz);
|
|
248
|
+
const ay = Fp.mul(y, iz);
|
|
249
|
+
const zz = Fp.mul(z, iz);
|
|
250
|
+
if (is0)
|
|
251
|
+
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
252
|
+
if (!Fp.eql(zz, Fp.ONE))
|
|
253
|
+
throw new Error('invZ was invalid');
|
|
254
|
+
return { x: ax, y: ay };
|
|
255
|
+
});
|
|
256
|
+
// NOTE: on exception this will crash 'cached' and no value will be set.
|
|
257
|
+
// Otherwise true will be return
|
|
258
|
+
const assertValidMemo = (0, utils_js_1.memoized)((p) => {
|
|
259
|
+
if (p.is0()) {
|
|
260
|
+
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
261
|
+
// In BLS, ZERO can be serialized, so we allow it.
|
|
262
|
+
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
|
263
|
+
if (CURVE.allowInfinityPoint && !Fp.is0(p.py))
|
|
264
|
+
return;
|
|
265
|
+
throw new Error('bad point: ZERO');
|
|
266
|
+
}
|
|
267
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
268
|
+
const { x, y } = p.toAffine();
|
|
269
|
+
// Check if x, y are valid field elements
|
|
270
|
+
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
271
|
+
throw new Error('bad point: x or y not FE');
|
|
272
|
+
const left = Fp.sqr(y); // y²
|
|
273
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
274
|
+
if (!Fp.eql(left, right))
|
|
275
|
+
throw new Error('bad point: equation left != right');
|
|
276
|
+
if (!p.isTorsionFree())
|
|
277
|
+
throw new Error('bad point: not in prime-order subgroup');
|
|
278
|
+
return true;
|
|
279
|
+
});
|
|
176
280
|
/**
|
|
177
281
|
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
|
178
282
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
@@ -189,6 +293,7 @@ function weierstrassPoints(opts) {
|
|
|
189
293
|
throw new Error('y required');
|
|
190
294
|
if (pz == null || !Fp.isValid(pz))
|
|
191
295
|
throw new Error('z required');
|
|
296
|
+
Object.freeze(this);
|
|
192
297
|
}
|
|
193
298
|
// Does not validate if the point is on-curve.
|
|
194
299
|
// Use fromHex instead, or call assertValidity() later.
|
|
@@ -233,32 +338,17 @@ function weierstrassPoints(opts) {
|
|
|
233
338
|
static fromPrivateKey(privateKey) {
|
|
234
339
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
|
235
340
|
}
|
|
341
|
+
// Multiscalar Multiplication
|
|
342
|
+
static msm(points, scalars) {
|
|
343
|
+
return (0, curve_js_1.pippenger)(Point, Fn, points, scalars);
|
|
344
|
+
}
|
|
236
345
|
// "Private method", don't use it directly
|
|
237
346
|
_setWindowSize(windowSize) {
|
|
238
|
-
this
|
|
239
|
-
pointPrecomputes.delete(this);
|
|
347
|
+
wnaf.setWindowSize(this, windowSize);
|
|
240
348
|
}
|
|
241
349
|
// A point on curve is valid if it conforms to equation.
|
|
242
350
|
assertValidity() {
|
|
243
|
-
|
|
244
|
-
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
|
245
|
-
// In BLS, ZERO can be serialized, so we allow it.
|
|
246
|
-
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
|
247
|
-
if (CURVE.allowInfinityPoint && !Fp.is0(this.py))
|
|
248
|
-
return;
|
|
249
|
-
throw new Error('bad point: ZERO');
|
|
250
|
-
}
|
|
251
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
252
|
-
const { x, y } = this.toAffine();
|
|
253
|
-
// Check if x, y are valid field elements
|
|
254
|
-
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
255
|
-
throw new Error('bad point: x or y not FE');
|
|
256
|
-
const left = Fp.sqr(y); // y²
|
|
257
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
258
|
-
if (!Fp.eql(left, right))
|
|
259
|
-
throw new Error('bad point: equation left != right');
|
|
260
|
-
if (!this.isTorsionFree())
|
|
261
|
-
throw new Error('bad point: not in prime-order subgroup');
|
|
351
|
+
assertValidMemo(this);
|
|
262
352
|
}
|
|
263
353
|
hasEvenY() {
|
|
264
354
|
const { y } = this.toAffine();
|
|
@@ -385,28 +475,25 @@ function weierstrassPoints(opts) {
|
|
|
385
475
|
return this.equals(Point.ZERO);
|
|
386
476
|
}
|
|
387
477
|
wNAF(n) {
|
|
388
|
-
return wnaf.wNAFCached(this,
|
|
389
|
-
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
|
|
390
|
-
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
391
|
-
});
|
|
478
|
+
return wnaf.wNAFCached(this, n, Point.normalizeZ);
|
|
392
479
|
}
|
|
393
480
|
/**
|
|
394
481
|
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
395
482
|
* It's faster, but should only be used when you don't care about
|
|
396
483
|
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
397
484
|
*/
|
|
398
|
-
multiplyUnsafe(
|
|
485
|
+
multiplyUnsafe(sc) {
|
|
486
|
+
ut.aInRange('scalar', sc, _0n, CURVE.n);
|
|
399
487
|
const I = Point.ZERO;
|
|
400
|
-
if (
|
|
488
|
+
if (sc === _0n)
|
|
401
489
|
return I;
|
|
402
|
-
|
|
403
|
-
if (n === _1n)
|
|
490
|
+
if (sc === _1n)
|
|
404
491
|
return this;
|
|
405
492
|
const { endo } = CURVE;
|
|
406
493
|
if (!endo)
|
|
407
|
-
return wnaf.unsafeLadder(this,
|
|
494
|
+
return wnaf.unsafeLadder(this, sc);
|
|
408
495
|
// Apply endomorphism
|
|
409
|
-
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(
|
|
496
|
+
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
|
|
410
497
|
let k1p = I;
|
|
411
498
|
let k2p = I;
|
|
412
499
|
let d = this;
|
|
@@ -436,12 +523,11 @@ function weierstrassPoints(opts) {
|
|
|
436
523
|
* @returns New point
|
|
437
524
|
*/
|
|
438
525
|
multiply(scalar) {
|
|
439
|
-
|
|
440
|
-
|
|
526
|
+
const { endo, n: N } = CURVE;
|
|
527
|
+
ut.aInRange('scalar', scalar, _1n, N);
|
|
441
528
|
let point, fake; // Fake point is used to const-time mult
|
|
442
|
-
const { endo } = CURVE;
|
|
443
529
|
if (endo) {
|
|
444
|
-
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(
|
|
530
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
|
|
445
531
|
let { p: k1p, f: f1p } = this.wNAF(k1);
|
|
446
532
|
let { p: k2p, f: f2p } = this.wNAF(k2);
|
|
447
533
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
@@ -451,7 +537,7 @@ function weierstrassPoints(opts) {
|
|
|
451
537
|
fake = f1p.add(f2p);
|
|
452
538
|
}
|
|
453
539
|
else {
|
|
454
|
-
const { p, f } = this.wNAF(
|
|
540
|
+
const { p, f } = this.wNAF(scalar);
|
|
455
541
|
point = p;
|
|
456
542
|
fake = f;
|
|
457
543
|
}
|
|
@@ -475,20 +561,7 @@ function weierstrassPoints(opts) {
|
|
|
475
561
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
476
562
|
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
477
563
|
toAffine(iz) {
|
|
478
|
-
|
|
479
|
-
const is0 = this.is0();
|
|
480
|
-
// If invZ was 0, we return zero point. However we still want to execute
|
|
481
|
-
// all operations, so we replace invZ with a random number, 1.
|
|
482
|
-
if (iz == null)
|
|
483
|
-
iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
484
|
-
const ax = Fp.mul(x, iz);
|
|
485
|
-
const ay = Fp.mul(y, iz);
|
|
486
|
-
const zz = Fp.mul(z, iz);
|
|
487
|
-
if (is0)
|
|
488
|
-
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
489
|
-
if (!Fp.eql(zz, Fp.ONE))
|
|
490
|
-
throw new Error('invZ was invalid');
|
|
491
|
-
return { x: ax, y: ay };
|
|
564
|
+
return toAffineMemo(this, iz);
|
|
492
565
|
}
|
|
493
566
|
isTorsionFree() {
|
|
494
567
|
const { h: cofactor, isTorsionFree } = CURVE;
|
|
@@ -507,10 +580,12 @@ function weierstrassPoints(opts) {
|
|
|
507
580
|
return this.multiplyUnsafe(CURVE.h);
|
|
508
581
|
}
|
|
509
582
|
toRawBytes(isCompressed = true) {
|
|
583
|
+
(0, utils_js_1.abool)('isCompressed', isCompressed);
|
|
510
584
|
this.assertValidity();
|
|
511
585
|
return toBytes(Point, this, isCompressed);
|
|
512
586
|
}
|
|
513
587
|
toHex(isCompressed = true) {
|
|
588
|
+
(0, utils_js_1.abool)('isCompressed', isCompressed);
|
|
514
589
|
return ut.bytesToHex(this.toRawBytes(isCompressed));
|
|
515
590
|
}
|
|
516
591
|
}
|
|
@@ -540,14 +615,18 @@ function validateOpts(curve) {
|
|
|
540
615
|
});
|
|
541
616
|
return Object.freeze({ lowS: true, ...opts });
|
|
542
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* Creates short weierstrass curve and ECDSA signature methods for it.
|
|
620
|
+
* @example
|
|
621
|
+
* import { Field } from '@noble/curves/abstract/modular';
|
|
622
|
+
* // Before that, define BigInt-s: a, b, p, n, Gx, Gy
|
|
623
|
+
* const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
|
|
624
|
+
*/
|
|
543
625
|
function weierstrass(curveDef) {
|
|
544
626
|
const CURVE = validateOpts(curveDef);
|
|
545
627
|
const { Fp, n: CURVE_ORDER } = CURVE;
|
|
546
628
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
547
629
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
548
|
-
function isValidFieldElement(num) {
|
|
549
|
-
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
|
|
550
|
-
}
|
|
551
630
|
function modN(a) {
|
|
552
631
|
return mod.mod(a, CURVE_ORDER);
|
|
553
632
|
}
|
|
@@ -560,6 +639,7 @@ function weierstrass(curveDef) {
|
|
|
560
639
|
const a = point.toAffine();
|
|
561
640
|
const x = Fp.toBytes(a.x);
|
|
562
641
|
const cat = ut.concatBytes;
|
|
642
|
+
(0, utils_js_1.abool)('isCompressed', isCompressed);
|
|
563
643
|
if (isCompressed) {
|
|
564
644
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
565
645
|
}
|
|
@@ -574,7 +654,7 @@ function weierstrass(curveDef) {
|
|
|
574
654
|
// this.assertValidity() is done inside of fromHex
|
|
575
655
|
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
576
656
|
const x = ut.bytesToNumberBE(tail);
|
|
577
|
-
if (!
|
|
657
|
+
if (!ut.inRange(x, _1n, Fp.ORDER))
|
|
578
658
|
throw new Error('Point is not on curve');
|
|
579
659
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
580
660
|
let y;
|
|
@@ -635,11 +715,8 @@ function weierstrass(curveDef) {
|
|
|
635
715
|
return new Signature(r, s);
|
|
636
716
|
}
|
|
637
717
|
assertValidity() {
|
|
638
|
-
//
|
|
639
|
-
|
|
640
|
-
throw new Error('r must be 0 < r < CURVE.n');
|
|
641
|
-
if (!isWithinCurveOrder(this.s))
|
|
642
|
-
throw new Error('s must be 0 < s < CURVE.n');
|
|
718
|
+
ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
|
|
719
|
+
ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
|
|
643
720
|
}
|
|
644
721
|
addRecoveryBit(recovery) {
|
|
645
722
|
return new Signature(this.r, this.s, recovery);
|
|
@@ -782,10 +859,7 @@ function weierstrass(curveDef) {
|
|
|
782
859
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
|
783
860
|
*/
|
|
784
861
|
function int2octets(num) {
|
|
785
|
-
|
|
786
|
-
throw new Error('bigint expected');
|
|
787
|
-
if (!(_0n <= num && num < ORDER_MASK))
|
|
788
|
-
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
|
|
862
|
+
ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
|
|
789
863
|
// works with order, can have different size than numToField!
|
|
790
864
|
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
|
791
865
|
}
|
|
@@ -802,6 +876,7 @@ function weierstrass(curveDef) {
|
|
|
802
876
|
if (lowS == null)
|
|
803
877
|
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
|
804
878
|
msgHash = (0, utils_js_1.ensureBytes)('msgHash', msgHash);
|
|
879
|
+
validateSigVerOpts(opts);
|
|
805
880
|
if (prehash)
|
|
806
881
|
msgHash = (0, utils_js_1.ensureBytes)('prehashed msgHash', hash(msgHash));
|
|
807
882
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
@@ -888,6 +963,7 @@ function weierstrass(curveDef) {
|
|
|
888
963
|
publicKey = (0, utils_js_1.ensureBytes)('publicKey', publicKey);
|
|
889
964
|
if ('strict' in opts)
|
|
890
965
|
throw new Error('options.strict was renamed to lowS');
|
|
966
|
+
validateSigVerOpts(opts);
|
|
891
967
|
const { lowS, prehash } = opts;
|
|
892
968
|
let _sig = undefined;
|
|
893
969
|
let P;
|