@noble/curves 0.5.2 → 0.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 +49 -5
- package/lib/_shortw_utils.d.ts +10 -21
- package/lib/abstract/bls.d.ts +39 -32
- package/lib/abstract/bls.js +74 -73
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -72
- package/lib/abstract/edwards.js +197 -375
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +61 -35
- package/lib/abstract/montgomery.js +4 -5
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -36
- package/lib/abstract/utils.js +23 -71
- package/lib/abstract/weierstrass.d.ts +51 -74
- package/lib/abstract/weierstrass.js +455 -628
- package/lib/bls12-381.js +63 -58
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +82 -79
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +86 -83
- package/lib/esm/abstract/bls.js +75 -74
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +196 -374
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +58 -34
- package/lib/esm/abstract/montgomery.js +5 -6
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -66
- package/lib/esm/abstract/weierstrass.js +454 -627
- package/lib/esm/bls12-381.js +75 -70
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +80 -78
- package/lib/esm/ed448.js +84 -82
- package/lib/esm/jubjub.js +1 -1
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +115 -151
- package/lib/esm/stark.js +104 -40
- package/lib/jubjub.d.ts +2 -2
- package/lib/jubjub.js +1 -1
- package/lib/p192.d.ts +20 -42
- package/lib/p224.d.ts +20 -42
- package/lib/p256.d.ts +23 -42
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +23 -42
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +23 -42
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +25 -37
- package/lib/secp256k1.js +115 -151
- package/lib/stark.d.ts +36 -19
- package/lib/stark.js +107 -40
- package/package.json +13 -8
|
@@ -1,66 +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
|
-
// Differences from @noble/secp256k1 1.7:
|
|
4
|
-
// 1. Different double() formula (but same addition)
|
|
5
|
-
// 2. Different sqrt() function
|
|
6
|
-
// 3. truncateHash() truncateOnly mode
|
|
7
|
-
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
|
8
|
-
// 5. Support for different hash functions
|
|
9
3
|
import * as mod from './modular.js';
|
|
10
4
|
import * as ut from './utils.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { wNAF } from './group.js';
|
|
14
|
-
// ASN.1 DER encoding utilities
|
|
15
|
-
class DERError extends Error {
|
|
16
|
-
constructor(message) {
|
|
17
|
-
super(message);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const DER = {
|
|
21
|
-
slice(s) {
|
|
22
|
-
// Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)])
|
|
23
|
-
// Padding done by numberToHex
|
|
24
|
-
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s;
|
|
25
|
-
},
|
|
26
|
-
parseInt(data) {
|
|
27
|
-
if (data.length < 2 || data[0] !== 0x02) {
|
|
28
|
-
throw new DERError(`Invalid signature integer tag: ${bytesToHex(data)}`);
|
|
29
|
-
}
|
|
30
|
-
const len = data[1];
|
|
31
|
-
const res = data.subarray(2, len + 2);
|
|
32
|
-
if (!len || res.length !== len) {
|
|
33
|
-
throw new DERError(`Invalid signature integer: wrong length`);
|
|
34
|
-
}
|
|
35
|
-
// Strange condition, its not about length, but about first bytes of number.
|
|
36
|
-
if (res[0] === 0x00 && res[1] <= 0x7f) {
|
|
37
|
-
throw new DERError('Invalid signature integer: trailing length');
|
|
38
|
-
}
|
|
39
|
-
return { data: ut.bytesToNumberBE(res), left: data.subarray(len + 2) };
|
|
40
|
-
},
|
|
41
|
-
parseSig(data) {
|
|
42
|
-
if (data.length < 2 || data[0] != 0x30) {
|
|
43
|
-
throw new DERError(`Invalid signature tag: ${bytesToHex(data)}`);
|
|
44
|
-
}
|
|
45
|
-
if (data[1] !== data.length - 2) {
|
|
46
|
-
throw new DERError('Invalid signature: incorrect length');
|
|
47
|
-
}
|
|
48
|
-
const { data: r, left: sBytes } = DER.parseInt(data.subarray(2));
|
|
49
|
-
const { data: s, left: rBytesLeft } = DER.parseInt(sBytes);
|
|
50
|
-
if (rBytesLeft.length) {
|
|
51
|
-
throw new DERError(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`);
|
|
52
|
-
}
|
|
53
|
-
return { r, s };
|
|
54
|
-
},
|
|
55
|
-
};
|
|
5
|
+
import { ensureBytes } from './utils.js';
|
|
6
|
+
import { wNAF, validateAbsOpts, } from './curve.js';
|
|
56
7
|
function validatePointOpts(curve) {
|
|
57
|
-
const opts =
|
|
8
|
+
const opts = validateAbsOpts(curve);
|
|
58
9
|
const Fp = opts.Fp;
|
|
59
10
|
for (const i of ['a', 'b']) {
|
|
60
11
|
if (!Fp.isValid(curve[i]))
|
|
61
12
|
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
62
13
|
}
|
|
63
|
-
for (const i of ['isTorsionFree', 'clearCofactor'
|
|
14
|
+
for (const i of ['isTorsionFree', 'clearCofactor']) {
|
|
64
15
|
if (curve[i] === undefined)
|
|
65
16
|
continue; // Optional
|
|
66
17
|
if (typeof curve[i] !== 'function')
|
|
@@ -68,7 +19,7 @@ function validatePointOpts(curve) {
|
|
|
68
19
|
}
|
|
69
20
|
const endo = opts.endo;
|
|
70
21
|
if (endo) {
|
|
71
|
-
if (!Fp.
|
|
22
|
+
if (!Fp.eql(opts.a, Fp.ZERO)) {
|
|
72
23
|
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
|
|
73
24
|
}
|
|
74
25
|
if (typeof endo !== 'object' ||
|
|
@@ -81,16 +32,66 @@ function validatePointOpts(curve) {
|
|
|
81
32
|
throw new Error('Invalid fromBytes function');
|
|
82
33
|
if (typeof opts.toBytes !== 'function')
|
|
83
34
|
throw new Error('Invalid fromBytes function');
|
|
84
|
-
// Requires including hashToCurve file
|
|
85
|
-
if (opts.htfDefaults !== undefined)
|
|
86
|
-
validateHTFOpts(opts.htfDefaults);
|
|
87
35
|
// Set defaults
|
|
88
36
|
return Object.freeze({ ...opts });
|
|
89
37
|
}
|
|
38
|
+
// ASN.1 DER encoding utilities
|
|
39
|
+
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
40
|
+
const DER = {
|
|
41
|
+
// asn.1 DER encoding utils
|
|
42
|
+
Err: class DERErr extends Error {
|
|
43
|
+
constructor(m = '') {
|
|
44
|
+
super(m);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
_parseInt(data) {
|
|
48
|
+
const { Err: E } = DER;
|
|
49
|
+
if (data.length < 2 || data[0] !== 0x02)
|
|
50
|
+
throw new E('Invalid signature integer tag');
|
|
51
|
+
const len = data[1];
|
|
52
|
+
const res = data.subarray(2, len + 2);
|
|
53
|
+
if (!len || res.length !== len)
|
|
54
|
+
throw new E('Invalid signature integer: wrong length');
|
|
55
|
+
if (res[0] === 0x00 && res[1] <= 0x7f)
|
|
56
|
+
throw new E('Invalid signature integer: trailing length');
|
|
57
|
+
// ^ Weird condition: not about length, but about first bytes of number.
|
|
58
|
+
return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
|
|
59
|
+
},
|
|
60
|
+
toSig(hex) {
|
|
61
|
+
// parse DER signature
|
|
62
|
+
const { Err: E } = DER;
|
|
63
|
+
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
|
64
|
+
if (!(data instanceof Uint8Array))
|
|
65
|
+
throw new Error('ui8a expected');
|
|
66
|
+
let l = data.length;
|
|
67
|
+
if (l < 2 || data[0] != 0x30)
|
|
68
|
+
throw new E('Invalid signature tag');
|
|
69
|
+
if (data[1] !== l - 2)
|
|
70
|
+
throw new E('Invalid signature: incorrect length');
|
|
71
|
+
const { d: r, l: sBytes } = DER._parseInt(data.subarray(2));
|
|
72
|
+
const { d: s, l: rBytesLeft } = DER._parseInt(sBytes);
|
|
73
|
+
if (rBytesLeft.length)
|
|
74
|
+
throw new E('Invalid signature: left bytes after parsing');
|
|
75
|
+
return { r, s };
|
|
76
|
+
},
|
|
77
|
+
hexFromSig(sig) {
|
|
78
|
+
const slice = (s) => (Number.parseInt(s[0], 16) >= 8 ? '00' + s : s); // slice DER
|
|
79
|
+
const h = (num) => {
|
|
80
|
+
const hex = num.toString(16);
|
|
81
|
+
return hex.length & 1 ? `0${hex}` : hex;
|
|
82
|
+
};
|
|
83
|
+
const s = slice(h(sig.s));
|
|
84
|
+
const r = slice(h(sig.r));
|
|
85
|
+
const shl = s.length / 2;
|
|
86
|
+
const rhl = r.length / 2;
|
|
87
|
+
const sl = h(shl);
|
|
88
|
+
const rl = h(rhl);
|
|
89
|
+
return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
90
92
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
91
93
|
const _0n = BigInt(0);
|
|
92
94
|
const _1n = BigInt(1);
|
|
93
|
-
const _3n = BigInt(3);
|
|
94
95
|
export function weierstrassPoints(opts) {
|
|
95
96
|
const CURVE = validatePointOpts(opts);
|
|
96
97
|
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
|
@@ -100,13 +101,17 @@ export function weierstrassPoints(opts) {
|
|
|
100
101
|
*/
|
|
101
102
|
function weierstrassEquation(x) {
|
|
102
103
|
const { a, b } = CURVE;
|
|
103
|
-
const x2 = Fp.
|
|
104
|
+
const x2 = Fp.sqr(x); // x * x
|
|
104
105
|
const x3 = Fp.mul(x2, x); // x2 * x
|
|
105
106
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
|
106
107
|
}
|
|
107
108
|
// Valid group elements reside in range 1..n-1
|
|
108
109
|
function isWithinCurveOrder(num) {
|
|
109
|
-
return _0n < num && num < CURVE.n;
|
|
110
|
+
return typeof num === 'bigint' && _0n < num && num < CURVE.n;
|
|
111
|
+
}
|
|
112
|
+
function assertGE(num) {
|
|
113
|
+
if (!isWithinCurveOrder(num))
|
|
114
|
+
throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
|
110
115
|
}
|
|
111
116
|
/**
|
|
112
117
|
* Validates if a private key is valid and converts it to bigint form.
|
|
@@ -115,7 +120,7 @@ export function weierstrassPoints(opts) {
|
|
|
115
120
|
* - `wrapPrivateKey` when true, executed after most checks, but before `0 < key < n`
|
|
116
121
|
*/
|
|
117
122
|
function normalizePrivateKey(key) {
|
|
118
|
-
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n
|
|
123
|
+
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n } = CURVE;
|
|
119
124
|
if (typeof custom === 'function')
|
|
120
125
|
key = custom(key);
|
|
121
126
|
let num;
|
|
@@ -123,92 +128,136 @@ export function weierstrassPoints(opts) {
|
|
|
123
128
|
// Curve order check is done below
|
|
124
129
|
num = key;
|
|
125
130
|
}
|
|
126
|
-
else if (ut.isPositiveInt(key)) {
|
|
127
|
-
num = BigInt(key);
|
|
128
|
-
}
|
|
129
131
|
else if (typeof key === 'string') {
|
|
130
132
|
if (key.length !== 2 * groupLen)
|
|
131
|
-
throw new Error(`
|
|
133
|
+
throw new Error(`must be ${groupLen} bytes`);
|
|
132
134
|
// Validates individual octets
|
|
133
|
-
num = ut.
|
|
135
|
+
num = ut.bytesToNumberBE(ensureBytes(key));
|
|
134
136
|
}
|
|
135
137
|
else if (key instanceof Uint8Array) {
|
|
136
138
|
if (key.length !== groupLen)
|
|
137
|
-
throw new Error(`
|
|
139
|
+
throw new Error(`must be ${groupLen} bytes`);
|
|
138
140
|
num = ut.bytesToNumberBE(key);
|
|
139
141
|
}
|
|
140
142
|
else {
|
|
141
|
-
throw new
|
|
143
|
+
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key);
|
|
142
144
|
}
|
|
143
145
|
// Useful for curves with cofactor != 1
|
|
144
146
|
if (wrapPrivateKey)
|
|
145
|
-
num = mod.mod(num,
|
|
146
|
-
|
|
147
|
-
throw new Error('Expected private key: 0 < key < n');
|
|
147
|
+
num = mod.mod(num, n);
|
|
148
|
+
assertGE(num);
|
|
148
149
|
return num;
|
|
149
150
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
function normalizeScalar(num) {
|
|
155
|
-
if (ut.isPositiveInt(num))
|
|
156
|
-
return BigInt(num);
|
|
157
|
-
if (typeof num === 'bigint' && isWithinCurveOrder(num))
|
|
158
|
-
return num;
|
|
159
|
-
throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n');
|
|
151
|
+
const pointPrecomputes = new Map();
|
|
152
|
+
function assertPrjPoint(other) {
|
|
153
|
+
if (!(other instanceof Point))
|
|
154
|
+
throw new Error('ProjectivePoint expected');
|
|
160
155
|
}
|
|
161
156
|
/**
|
|
162
157
|
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
|
163
158
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
164
159
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
165
160
|
*/
|
|
166
|
-
class
|
|
167
|
-
constructor(
|
|
168
|
-
this.
|
|
169
|
-
this.
|
|
170
|
-
this.
|
|
161
|
+
class Point {
|
|
162
|
+
constructor(px, py, pz) {
|
|
163
|
+
this.px = px;
|
|
164
|
+
this.py = py;
|
|
165
|
+
this.pz = pz;
|
|
166
|
+
if (px == null || !Fp.isValid(px))
|
|
167
|
+
throw new Error('x required');
|
|
168
|
+
if (py == null || !Fp.isValid(py))
|
|
169
|
+
throw new Error('y required');
|
|
170
|
+
if (pz == null || !Fp.isValid(pz))
|
|
171
|
+
throw new Error('z required');
|
|
171
172
|
}
|
|
172
173
|
static fromAffine(p) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
const { x, y } = p || {};
|
|
175
|
+
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|
176
|
+
throw new Error('invalid affine point');
|
|
177
|
+
if (p instanceof Point)
|
|
178
|
+
throw new Error('projective point not allowed');
|
|
179
|
+
const is0 = (i) => Fp.eql(i, Fp.ZERO);
|
|
176
180
|
// fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
|
|
177
|
-
if (
|
|
178
|
-
return
|
|
179
|
-
return new
|
|
181
|
+
if (is0(x) && is0(y))
|
|
182
|
+
return Point.ZERO;
|
|
183
|
+
return new Point(x, y, Fp.ONE);
|
|
184
|
+
}
|
|
185
|
+
get x() {
|
|
186
|
+
return this.toAffine().x;
|
|
187
|
+
}
|
|
188
|
+
get y() {
|
|
189
|
+
return this.toAffine().y;
|
|
180
190
|
}
|
|
181
191
|
/**
|
|
182
192
|
* Takes a bunch of Projective Points but executes only one
|
|
183
193
|
* inversion on all of them. Inversion is very slow operation,
|
|
184
194
|
* so this improves performance massively.
|
|
195
|
+
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
|
185
196
|
*/
|
|
186
|
-
static
|
|
187
|
-
const toInv = Fp.invertBatch(points.map((p) => p.
|
|
188
|
-
return points.map((p, i) => p.toAffine(toInv[i]));
|
|
197
|
+
static normalizeZ(points) {
|
|
198
|
+
const toInv = Fp.invertBatch(points.map((p) => p.pz));
|
|
199
|
+
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
189
200
|
}
|
|
190
201
|
/**
|
|
191
|
-
*
|
|
202
|
+
* Converts hash string or Uint8Array to Point.
|
|
203
|
+
* @param hex short/long ECDSA hex
|
|
192
204
|
*/
|
|
193
|
-
static
|
|
194
|
-
|
|
205
|
+
static fromHex(hex) {
|
|
206
|
+
const P = Point.fromAffine(CURVE.fromBytes(ensureBytes(hex)));
|
|
207
|
+
P.assertValidity();
|
|
208
|
+
return P;
|
|
209
|
+
}
|
|
210
|
+
// Multiplies generator point by privateKey.
|
|
211
|
+
static fromPrivateKey(privateKey) {
|
|
212
|
+
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
213
|
+
}
|
|
214
|
+
// "Private method", don't use it directly
|
|
215
|
+
_setWindowSize(windowSize) {
|
|
216
|
+
this._WINDOW_SIZE = windowSize;
|
|
217
|
+
pointPrecomputes.delete(this);
|
|
218
|
+
}
|
|
219
|
+
// A point on curve is valid if it conforms to equation.
|
|
220
|
+
assertValidity() {
|
|
221
|
+
// Zero is valid point too!
|
|
222
|
+
if (this.is0()) {
|
|
223
|
+
if (CURVE.allowInfinityPoint)
|
|
224
|
+
return;
|
|
225
|
+
throw new Error('bad point: ZERO');
|
|
226
|
+
}
|
|
227
|
+
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
228
|
+
const { x, y } = this.toAffine();
|
|
229
|
+
// Check if x, y are valid field elements
|
|
230
|
+
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
231
|
+
throw new Error('bad point: x or y not FE');
|
|
232
|
+
const left = Fp.sqr(y); // y²
|
|
233
|
+
const right = weierstrassEquation(x); // x³ + ax + b
|
|
234
|
+
if (!Fp.eql(left, right))
|
|
235
|
+
throw new Error('bad point: equation left != right');
|
|
236
|
+
if (!this.isTorsionFree())
|
|
237
|
+
throw new Error('bad point: not in prime-order subgroup');
|
|
238
|
+
}
|
|
239
|
+
hasEvenY() {
|
|
240
|
+
const { y } = this.toAffine();
|
|
241
|
+
if (Fp.isOdd)
|
|
242
|
+
return !Fp.isOdd(y);
|
|
243
|
+
throw new Error("Field doesn't support isOdd");
|
|
195
244
|
}
|
|
196
245
|
/**
|
|
197
246
|
* Compare one point to another.
|
|
198
247
|
*/
|
|
199
248
|
equals(other) {
|
|
200
249
|
assertPrjPoint(other);
|
|
201
|
-
const {
|
|
202
|
-
const {
|
|
203
|
-
const U1 = Fp.
|
|
204
|
-
const U2 = Fp.
|
|
250
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
251
|
+
const { px: X2, py: Y2, pz: Z2 } = other;
|
|
252
|
+
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|
253
|
+
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|
205
254
|
return U1 && U2;
|
|
206
255
|
}
|
|
207
256
|
/**
|
|
208
257
|
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
209
258
|
*/
|
|
210
259
|
negate() {
|
|
211
|
-
return new
|
|
260
|
+
return new Point(this.px, Fp.neg(this.py), this.pz);
|
|
212
261
|
}
|
|
213
262
|
// Renes-Costello-Batina exception-free doubling formula.
|
|
214
263
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -217,7 +266,7 @@ export function weierstrassPoints(opts) {
|
|
|
217
266
|
double() {
|
|
218
267
|
const { a, b } = CURVE;
|
|
219
268
|
const b3 = Fp.mul(b, 3n);
|
|
220
|
-
const {
|
|
269
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
221
270
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
222
271
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
223
272
|
let t1 = Fp.mul(Y1, Y1);
|
|
@@ -250,7 +299,7 @@ export function weierstrassPoints(opts) {
|
|
|
250
299
|
Z3 = Fp.mul(t2, t1);
|
|
251
300
|
Z3 = Fp.add(Z3, Z3); // step 30
|
|
252
301
|
Z3 = Fp.add(Z3, Z3);
|
|
253
|
-
return new
|
|
302
|
+
return new Point(X3, Y3, Z3);
|
|
254
303
|
}
|
|
255
304
|
// Renes-Costello-Batina exception-free addition formula.
|
|
256
305
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|
@@ -258,8 +307,8 @@ export function weierstrassPoints(opts) {
|
|
|
258
307
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|
259
308
|
add(other) {
|
|
260
309
|
assertPrjPoint(other);
|
|
261
|
-
const {
|
|
262
|
-
const {
|
|
310
|
+
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
311
|
+
const { px: X2, py: Y2, pz: Z2 } = other;
|
|
263
312
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
264
313
|
const a = CURVE.a;
|
|
265
314
|
const b3 = Fp.mul(CURVE.b, 3n);
|
|
@@ -303,30 +352,39 @@ export function weierstrassPoints(opts) {
|
|
|
303
352
|
t0 = Fp.mul(t3, t1);
|
|
304
353
|
Z3 = Fp.mul(t5, Z3);
|
|
305
354
|
Z3 = Fp.add(Z3, t0); // step 40
|
|
306
|
-
return new
|
|
355
|
+
return new Point(X3, Y3, Z3);
|
|
307
356
|
}
|
|
308
357
|
subtract(other) {
|
|
309
358
|
return this.add(other.negate());
|
|
310
359
|
}
|
|
360
|
+
is0() {
|
|
361
|
+
return this.equals(Point.ZERO);
|
|
362
|
+
}
|
|
363
|
+
wNAF(n) {
|
|
364
|
+
return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
|
|
365
|
+
const toInv = Fp.invertBatch(comp.map((p) => p.pz));
|
|
366
|
+
return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
311
369
|
/**
|
|
312
370
|
* Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
313
371
|
* It's faster, but should only be used when you don't care about
|
|
314
372
|
* an exposed private key e.g. sig verification, which works over *public* keys.
|
|
315
373
|
*/
|
|
316
|
-
multiplyUnsafe(
|
|
317
|
-
const
|
|
318
|
-
if (
|
|
319
|
-
return
|
|
320
|
-
// Will throw on 0
|
|
321
|
-
let n = normalizeScalar(scalar);
|
|
374
|
+
multiplyUnsafe(n) {
|
|
375
|
+
const I = Point.ZERO;
|
|
376
|
+
if (n === _0n)
|
|
377
|
+
return I;
|
|
378
|
+
assertGE(n); // Will throw on 0
|
|
322
379
|
if (n === _1n)
|
|
323
380
|
return this;
|
|
324
|
-
|
|
381
|
+
const { endo } = CURVE;
|
|
382
|
+
if (!endo)
|
|
325
383
|
return wnaf.unsafeLadder(this, n);
|
|
326
384
|
// Apply endomorphism
|
|
327
|
-
let { k1neg, k1, k2neg, k2 } =
|
|
328
|
-
let k1p =
|
|
329
|
-
let k2p =
|
|
385
|
+
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
|
|
386
|
+
let k1p = I;
|
|
387
|
+
let k2p = I;
|
|
330
388
|
let d = this;
|
|
331
389
|
while (k1 > _0n || k2 > _0n) {
|
|
332
390
|
if (k1 & _1n)
|
|
@@ -341,27 +399,9 @@ export function weierstrassPoints(opts) {
|
|
|
341
399
|
k1p = k1p.negate();
|
|
342
400
|
if (k2neg)
|
|
343
401
|
k2p = k2p.negate();
|
|
344
|
-
k2p = new
|
|
402
|
+
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
345
403
|
return k1p.add(k2p);
|
|
346
404
|
}
|
|
347
|
-
/**
|
|
348
|
-
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
349
|
-
*/
|
|
350
|
-
wNAF(n, affinePoint) {
|
|
351
|
-
if (!affinePoint && this.equals(ProjectivePoint.BASE))
|
|
352
|
-
affinePoint = Point.BASE;
|
|
353
|
-
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
|
354
|
-
// Calculate precomputes on a first run, reuse them after
|
|
355
|
-
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
|
356
|
-
if (!precomputes) {
|
|
357
|
-
precomputes = wnaf.precomputeWindow(this, W);
|
|
358
|
-
if (affinePoint && W !== 1) {
|
|
359
|
-
precomputes = ProjectivePoint.normalizeZ(precomputes);
|
|
360
|
-
pointPrecomputes.set(affinePoint, precomputes);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return wnaf.wNAF(W, precomputes, n);
|
|
364
|
-
}
|
|
365
405
|
/**
|
|
366
406
|
* Constant time multiplication.
|
|
367
407
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|
@@ -370,55 +410,65 @@ export function weierstrassPoints(opts) {
|
|
|
370
410
|
* @param affinePoint optional point ot save cached precompute windows on it
|
|
371
411
|
* @returns New point
|
|
372
412
|
*/
|
|
373
|
-
multiply(scalar
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
let point;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
let { p:
|
|
382
|
-
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
|
|
413
|
+
multiply(scalar) {
|
|
414
|
+
assertGE(scalar);
|
|
415
|
+
let n = scalar;
|
|
416
|
+
let point, fake; // Fake point is used to const-time mult
|
|
417
|
+
const { endo } = CURVE;
|
|
418
|
+
if (endo) {
|
|
419
|
+
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
|
|
420
|
+
let { p: k1p, f: f1p } = this.wNAF(k1);
|
|
421
|
+
let { p: k2p, f: f2p } = this.wNAF(k2);
|
|
383
422
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
384
423
|
k2p = wnaf.constTimeNegate(k2neg, k2p);
|
|
385
|
-
k2p = new
|
|
424
|
+
k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
|
|
386
425
|
point = k1p.add(k2p);
|
|
387
426
|
fake = f1p.add(f2p);
|
|
388
427
|
}
|
|
389
428
|
else {
|
|
390
|
-
const { p, f } = this.wNAF(n
|
|
429
|
+
const { p, f } = this.wNAF(n);
|
|
391
430
|
point = p;
|
|
392
431
|
fake = f;
|
|
393
432
|
}
|
|
394
433
|
// Normalize `z` for both points, but return only real one
|
|
395
|
-
return
|
|
434
|
+
return Point.normalizeZ([point, fake])[0];
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
|
|
438
|
+
* @returns non-zero affine point
|
|
439
|
+
*/
|
|
440
|
+
multiplyAndAddUnsafe(Q, a, b) {
|
|
441
|
+
const G = Point.BASE; // No Strauss-Shamir trick: we have 10% faster G precomputes
|
|
442
|
+
const mul = (P, a // Select faster multiply() method
|
|
443
|
+
) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
|
|
444
|
+
const sum = mul(this, a).add(mul(Q, b));
|
|
445
|
+
return sum.is0() ? undefined : sum;
|
|
396
446
|
}
|
|
397
447
|
// Converts Projective point to affine (x, y) coordinates.
|
|
398
448
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
399
449
|
// (x, y, z) ∋ (x=x/z, y=y/z)
|
|
400
|
-
toAffine(
|
|
401
|
-
const { x, y, z } = this;
|
|
402
|
-
const is0 = this.
|
|
450
|
+
toAffine(iz) {
|
|
451
|
+
const { px: x, py: y, pz: z } = this;
|
|
452
|
+
const is0 = this.is0();
|
|
403
453
|
// If invZ was 0, we return zero point. However we still want to execute
|
|
404
454
|
// all operations, so we replace invZ with a random number, 1.
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
const ax = Fp.mul(x,
|
|
408
|
-
const ay = Fp.mul(y,
|
|
409
|
-
const zz = Fp.mul(z,
|
|
455
|
+
if (iz == null)
|
|
456
|
+
iz = is0 ? Fp.ONE : Fp.inv(z);
|
|
457
|
+
const ax = Fp.mul(x, iz);
|
|
458
|
+
const ay = Fp.mul(y, iz);
|
|
459
|
+
const zz = Fp.mul(z, iz);
|
|
410
460
|
if (is0)
|
|
411
|
-
return
|
|
412
|
-
if (!Fp.
|
|
461
|
+
return { x: Fp.ZERO, y: Fp.ZERO };
|
|
462
|
+
if (!Fp.eql(zz, Fp.ONE))
|
|
413
463
|
throw new Error('invZ was invalid');
|
|
414
|
-
return
|
|
464
|
+
return { x: ax, y: ay };
|
|
415
465
|
}
|
|
416
466
|
isTorsionFree() {
|
|
417
467
|
const { h: cofactor, isTorsionFree } = CURVE;
|
|
418
468
|
if (cofactor === _1n)
|
|
419
469
|
return true; // No subgroups, always torsion-free
|
|
420
470
|
if (isTorsionFree)
|
|
421
|
-
return isTorsionFree(
|
|
471
|
+
return isTorsionFree(Point, this);
|
|
422
472
|
throw new Error('isTorsionFree() has not been declared for the elliptic curve');
|
|
423
473
|
}
|
|
424
474
|
clearCofactor() {
|
|
@@ -426,170 +476,31 @@ export function weierstrassPoints(opts) {
|
|
|
426
476
|
if (cofactor === _1n)
|
|
427
477
|
return this; // Fast-path
|
|
428
478
|
if (clearCofactor)
|
|
429
|
-
return clearCofactor(
|
|
479
|
+
return clearCofactor(Point, this);
|
|
430
480
|
return this.multiplyUnsafe(CURVE.h);
|
|
431
481
|
}
|
|
432
|
-
|
|
433
|
-
ProjectivePoint.BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
434
|
-
ProjectivePoint.ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
435
|
-
const _bits = CURVE.nBitLength;
|
|
436
|
-
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
437
|
-
function assertPrjPoint(other) {
|
|
438
|
-
if (!(other instanceof ProjectivePoint))
|
|
439
|
-
throw new TypeError('ProjectivePoint expected');
|
|
440
|
-
}
|
|
441
|
-
// Stores precomputed values for points.
|
|
442
|
-
const pointPrecomputes = new WeakMap();
|
|
443
|
-
/**
|
|
444
|
-
* Default Point works in default aka affine coordinates: (x, y)
|
|
445
|
-
*/
|
|
446
|
-
class Point {
|
|
447
|
-
constructor(x, y) {
|
|
448
|
-
this.x = x;
|
|
449
|
-
this.y = y;
|
|
450
|
-
}
|
|
451
|
-
// "Private method", don't use it directly
|
|
452
|
-
_setWindowSize(windowSize) {
|
|
453
|
-
this._WINDOW_SIZE = windowSize;
|
|
454
|
-
pointPrecomputes.delete(this);
|
|
455
|
-
}
|
|
456
|
-
// Checks for y % 2 == 0
|
|
457
|
-
hasEvenY() {
|
|
458
|
-
if (Fp.isOdd)
|
|
459
|
-
return !Fp.isOdd(this.y);
|
|
460
|
-
throw new Error("Field doesn't support isOdd");
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Converts hash string or Uint8Array to Point.
|
|
464
|
-
* @param hex short/long ECDSA hex
|
|
465
|
-
*/
|
|
466
|
-
static fromHex(hex) {
|
|
467
|
-
const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex));
|
|
468
|
-
const point = new Point(x, y);
|
|
469
|
-
point.assertValidity();
|
|
470
|
-
return point;
|
|
471
|
-
}
|
|
472
|
-
// Multiplies generator point by privateKey.
|
|
473
|
-
static fromPrivateKey(privateKey) {
|
|
474
|
-
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
475
|
-
}
|
|
476
|
-
toRawBytes(isCompressed = false) {
|
|
482
|
+
toRawBytes(isCompressed = true) {
|
|
477
483
|
this.assertValidity();
|
|
478
484
|
return CURVE.toBytes(Point, this, isCompressed);
|
|
479
485
|
}
|
|
480
|
-
toHex(isCompressed =
|
|
481
|
-
return bytesToHex(this.toRawBytes(isCompressed));
|
|
482
|
-
}
|
|
483
|
-
// A point on curve is valid if it conforms to equation.
|
|
484
|
-
assertValidity() {
|
|
485
|
-
// Zero is valid point too!
|
|
486
|
-
if (this.equals(Point.ZERO)) {
|
|
487
|
-
if (CURVE.allowInfinityPoint)
|
|
488
|
-
return;
|
|
489
|
-
throw new Error('Point at infinity');
|
|
490
|
-
}
|
|
491
|
-
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
492
|
-
const msg = 'Point is not on elliptic curve';
|
|
493
|
-
const { x, y } = this;
|
|
494
|
-
// Check if x, y are valid field elements
|
|
495
|
-
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
496
|
-
throw new Error(msg);
|
|
497
|
-
const left = Fp.square(y); // y²
|
|
498
|
-
const right = weierstrassEquation(x); // x³ + ax + b
|
|
499
|
-
if (!Fp.equals(left, right))
|
|
500
|
-
throw new Error(msg);
|
|
501
|
-
if (!this.isTorsionFree())
|
|
502
|
-
throw new Error('Point must be of prime-order subgroup');
|
|
503
|
-
}
|
|
504
|
-
equals(other) {
|
|
505
|
-
if (!(other instanceof Point))
|
|
506
|
-
throw new TypeError('Point#equals: expected Point');
|
|
507
|
-
return Fp.equals(this.x, other.x) && Fp.equals(this.y, other.y);
|
|
508
|
-
}
|
|
509
|
-
// Returns the same point with inverted `y`
|
|
510
|
-
negate() {
|
|
511
|
-
return new Point(this.x, Fp.negate(this.y));
|
|
512
|
-
}
|
|
513
|
-
toProj() {
|
|
514
|
-
return ProjectivePoint.fromAffine(this);
|
|
515
|
-
}
|
|
516
|
-
// Adds point to itself
|
|
517
|
-
double() {
|
|
518
|
-
return this.toProj().double().toAffine();
|
|
519
|
-
}
|
|
520
|
-
add(other) {
|
|
521
|
-
return this.toProj().add(ProjectivePoint.fromAffine(other)).toAffine();
|
|
522
|
-
}
|
|
523
|
-
subtract(other) {
|
|
524
|
-
return this.add(other.negate());
|
|
525
|
-
}
|
|
526
|
-
multiply(scalar) {
|
|
527
|
-
return this.toProj().multiply(scalar, this).toAffine();
|
|
528
|
-
}
|
|
529
|
-
multiplyUnsafe(scalar) {
|
|
530
|
-
return this.toProj().multiplyUnsafe(scalar).toAffine();
|
|
531
|
-
}
|
|
532
|
-
clearCofactor() {
|
|
533
|
-
return this.toProj().clearCofactor().toAffine();
|
|
534
|
-
}
|
|
535
|
-
isTorsionFree() {
|
|
536
|
-
return this.toProj().isTorsionFree();
|
|
537
|
-
}
|
|
538
|
-
/**
|
|
539
|
-
* Efficiently calculate `aP + bQ`.
|
|
540
|
-
* Unsafe, can expose private key, if used incorrectly.
|
|
541
|
-
* TODO: Utilize Shamir's trick
|
|
542
|
-
* @returns non-zero affine point
|
|
543
|
-
*/
|
|
544
|
-
multiplyAndAddUnsafe(Q, a, b) {
|
|
545
|
-
const P = this.toProj();
|
|
546
|
-
const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
|
|
547
|
-
const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
|
|
548
|
-
const sum = aP.add(bQ);
|
|
549
|
-
return sum.equals(ProjectivePoint.ZERO) ? undefined : sum.toAffine();
|
|
550
|
-
}
|
|
551
|
-
// Encodes byte string to elliptic curve
|
|
552
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
553
|
-
static hashToCurve(msg, options) {
|
|
554
|
-
const { mapToCurve } = CURVE;
|
|
555
|
-
if (!mapToCurve)
|
|
556
|
-
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
557
|
-
msg = ut.ensureBytes(msg);
|
|
558
|
-
const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options });
|
|
559
|
-
const { x: x0, y: y0 } = mapToCurve(u[0]);
|
|
560
|
-
const { x: x1, y: y1 } = mapToCurve(u[1]);
|
|
561
|
-
return new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
|
562
|
-
}
|
|
563
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
564
|
-
static encodeToCurve(msg, options) {
|
|
565
|
-
const { mapToCurve } = CURVE;
|
|
566
|
-
if (!mapToCurve)
|
|
567
|
-
throw new Error('CURVE.mapToCurve() has not been defined');
|
|
568
|
-
msg = ut.ensureBytes(msg);
|
|
569
|
-
const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options });
|
|
570
|
-
const { x, y } = mapToCurve(u[0]);
|
|
571
|
-
return new Point(x, y).clearCofactor();
|
|
486
|
+
toHex(isCompressed = true) {
|
|
487
|
+
return ut.bytesToHex(this.toRawBytes(isCompressed));
|
|
572
488
|
}
|
|
573
489
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Identity point aka point at infinity. p - p = zero_p; p + zero_p = p
|
|
580
|
-
*/
|
|
581
|
-
Point.ZERO = new Point(Fp.ZERO, Fp.ZERO);
|
|
490
|
+
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
491
|
+
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
492
|
+
const _bits = CURVE.nBitLength;
|
|
493
|
+
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
582
494
|
return {
|
|
583
|
-
|
|
584
|
-
ProjectivePoint: ProjectivePoint,
|
|
495
|
+
ProjectivePoint: Point,
|
|
585
496
|
normalizePrivateKey,
|
|
586
497
|
weierstrassEquation,
|
|
587
498
|
isWithinCurveOrder,
|
|
588
499
|
};
|
|
589
500
|
}
|
|
590
501
|
function validateOpts(curve) {
|
|
591
|
-
const opts =
|
|
592
|
-
if (typeof opts.hash !== 'function' || !
|
|
502
|
+
const opts = validateAbsOpts(curve);
|
|
503
|
+
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
593
504
|
throw new Error('Invalid hash function');
|
|
594
505
|
if (typeof opts.hmac !== 'function')
|
|
595
506
|
throw new Error('Invalid hmac function');
|
|
@@ -598,55 +509,58 @@ function validateOpts(curve) {
|
|
|
598
509
|
// Set defaults
|
|
599
510
|
return Object.freeze({ lowS: true, ...opts });
|
|
600
511
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
incr() {
|
|
625
|
-
if (this.counter >= 1000)
|
|
626
|
-
throw new Error('Tried 1,000 k values for sign(), all were invalid');
|
|
627
|
-
this.counter += 1;
|
|
628
|
-
}
|
|
629
|
-
reseedSync(seed = new Uint8Array()) {
|
|
630
|
-
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed);
|
|
631
|
-
this.v = this.hmacSync(this.v);
|
|
512
|
+
const u8n = (data) => new Uint8Array(data); // creates Uint8Array
|
|
513
|
+
const u8fr = (arr) => Uint8Array.from(arr); // another shortcut
|
|
514
|
+
function hmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
515
|
+
if (typeof hashLen !== 'number' || hashLen < 2)
|
|
516
|
+
throw new Error('hashLen must be a number');
|
|
517
|
+
if (typeof qByteLen !== 'number' || qByteLen < 2)
|
|
518
|
+
throw new Error('qByteLen must be a number');
|
|
519
|
+
if (typeof hmacFn !== 'function')
|
|
520
|
+
throw new Error('hmacFn must be a function');
|
|
521
|
+
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
522
|
+
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
523
|
+
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
|
524
|
+
let i = 0; // Iterations counter, will throw when over 1000
|
|
525
|
+
const reset = () => {
|
|
526
|
+
v.fill(1);
|
|
527
|
+
k.fill(0);
|
|
528
|
+
i = 0;
|
|
529
|
+
};
|
|
530
|
+
const h = (...b) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
|
|
531
|
+
const reseed = (seed = u8n()) => {
|
|
532
|
+
// HMAC-DRBG reseed() function. Steps D-G
|
|
533
|
+
k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
|
|
534
|
+
v = h(); // v = hmac(k || v)
|
|
632
535
|
if (seed.length === 0)
|
|
633
536
|
return;
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
537
|
+
k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
|
|
538
|
+
v = h(); // v = hmac(k || v)
|
|
539
|
+
};
|
|
540
|
+
const gen = () => {
|
|
541
|
+
// HMAC-DRBG generate() function
|
|
542
|
+
if (i++ >= 1000)
|
|
543
|
+
throw new Error('drbg: tried 1000 values');
|
|
640
544
|
let len = 0;
|
|
641
545
|
const out = [];
|
|
642
|
-
while (len <
|
|
643
|
-
|
|
644
|
-
const sl =
|
|
546
|
+
while (len < qByteLen) {
|
|
547
|
+
v = h();
|
|
548
|
+
const sl = v.slice();
|
|
645
549
|
out.push(sl);
|
|
646
|
-
len +=
|
|
550
|
+
len += v.length;
|
|
647
551
|
}
|
|
648
552
|
return ut.concatBytes(...out);
|
|
649
|
-
}
|
|
553
|
+
};
|
|
554
|
+
const genUntil = (seed, pred) => {
|
|
555
|
+
reset();
|
|
556
|
+
reseed(seed); // Steps D-G
|
|
557
|
+
let res = undefined; // Step H: grind until k is in [1..n-1]
|
|
558
|
+
while (!(res = pred(gen())))
|
|
559
|
+
reseed();
|
|
560
|
+
reset();
|
|
561
|
+
return res;
|
|
562
|
+
};
|
|
563
|
+
return genUntil;
|
|
650
564
|
}
|
|
651
565
|
export function weierstrass(curveDef) {
|
|
652
566
|
const CURVE = validateOpts(curveDef);
|
|
@@ -655,41 +569,49 @@ export function weierstrass(curveDef) {
|
|
|
655
569
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
|
656
570
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
|
657
571
|
function isValidFieldElement(num) {
|
|
658
|
-
// 0 is
|
|
659
|
-
|
|
572
|
+
return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
|
|
573
|
+
}
|
|
574
|
+
function modN(a) {
|
|
575
|
+
return mod.mod(a, CURVE_ORDER);
|
|
576
|
+
}
|
|
577
|
+
function invN(a) {
|
|
578
|
+
return mod.invert(a, CURVE_ORDER);
|
|
660
579
|
}
|
|
661
|
-
const { Point,
|
|
580
|
+
const { ProjectivePoint: Point, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
|
|
662
581
|
...CURVE,
|
|
663
582
|
toBytes(c, point, isCompressed) {
|
|
664
|
-
const
|
|
583
|
+
const a = point.toAffine();
|
|
584
|
+
const x = Fp.toBytes(a.x);
|
|
665
585
|
const cat = ut.concatBytes;
|
|
666
586
|
if (isCompressed) {
|
|
587
|
+
// TODO: hasEvenY
|
|
667
588
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
|
668
589
|
}
|
|
669
590
|
else {
|
|
670
|
-
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(
|
|
591
|
+
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
|
671
592
|
}
|
|
672
593
|
},
|
|
673
594
|
fromBytes(bytes) {
|
|
674
595
|
const len = bytes.length;
|
|
675
|
-
const
|
|
596
|
+
const head = bytes[0];
|
|
597
|
+
const tail = bytes.subarray(1);
|
|
676
598
|
// this.assertValidity() is done inside of fromHex
|
|
677
|
-
if (len === compressedLen && (
|
|
678
|
-
const x = ut.bytesToNumberBE(
|
|
599
|
+
if (len === compressedLen && (head === 0x02 || head === 0x03)) {
|
|
600
|
+
const x = ut.bytesToNumberBE(tail);
|
|
679
601
|
if (!isValidFieldElement(x))
|
|
680
602
|
throw new Error('Point is not on curve');
|
|
681
603
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
682
604
|
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
683
605
|
const isYOdd = (y & _1n) === _1n;
|
|
684
606
|
// ECDSA
|
|
685
|
-
const
|
|
686
|
-
if (
|
|
687
|
-
y = Fp.
|
|
607
|
+
const isHeadOdd = (head & 1) === 1;
|
|
608
|
+
if (isHeadOdd !== isYOdd)
|
|
609
|
+
y = Fp.neg(y);
|
|
688
610
|
return { x, y };
|
|
689
611
|
}
|
|
690
|
-
else if (len === uncompressedLen &&
|
|
691
|
-
const x = Fp.fromBytes(
|
|
692
|
-
const y = Fp.fromBytes(
|
|
612
|
+
else if (len === uncompressedLen && head === 0x04) {
|
|
613
|
+
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
|
614
|
+
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
|
693
615
|
return { x, y };
|
|
694
616
|
}
|
|
695
617
|
else {
|
|
@@ -697,51 +619,16 @@ export function weierstrass(curveDef) {
|
|
|
697
619
|
}
|
|
698
620
|
},
|
|
699
621
|
});
|
|
700
|
-
|
|
701
|
-
function numToField(num) {
|
|
702
|
-
if (typeof num !== 'bigint')
|
|
703
|
-
throw new Error('Expected bigint');
|
|
704
|
-
if (!(_0n <= num && num < Fp.MASK))
|
|
705
|
-
throw new Error(`Expected number < 2^${Fp.BYTES * 8}`);
|
|
706
|
-
return Fp.toBytes(num);
|
|
707
|
-
}
|
|
708
|
-
const numToFieldStr = (num) => bytesToHex(numToField(num));
|
|
709
|
-
/**
|
|
710
|
-
* Normalizes hex, bytes, Point to Point. Checks for curve equation.
|
|
711
|
-
*/
|
|
712
|
-
function normalizePublicKey(publicKey) {
|
|
713
|
-
if (publicKey instanceof Point) {
|
|
714
|
-
publicKey.assertValidity();
|
|
715
|
-
return publicKey;
|
|
716
|
-
}
|
|
717
|
-
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
|
718
|
-
return Point.fromHex(publicKey);
|
|
719
|
-
// This can happen because PointType can be instance of different class
|
|
720
|
-
}
|
|
721
|
-
else
|
|
722
|
-
throw new Error(`Unknown type of public key: ${publicKey}`);
|
|
723
|
-
}
|
|
622
|
+
const numToNByteStr = (num) => ut.bytesToHex(ut.numberToBytesBE(num, CURVE.nByteLength));
|
|
724
623
|
function isBiggerThanHalfOrder(number) {
|
|
725
624
|
const HALF = CURVE_ORDER >> _1n;
|
|
726
625
|
return number > HALF;
|
|
727
626
|
}
|
|
728
627
|
function normalizeS(s) {
|
|
729
|
-
return isBiggerThanHalfOrder(s) ?
|
|
730
|
-
}
|
|
731
|
-
function bits2int_2(bytes) {
|
|
732
|
-
const delta = bytes.length * 8 - CURVE.nBitLength;
|
|
733
|
-
const num = ut.bytesToNumberBE(bytes);
|
|
734
|
-
return delta > 0 ? num >> BigInt(delta) : num;
|
|
735
|
-
}
|
|
736
|
-
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
|
737
|
-
function _truncateHash(hash, truncateOnly = false) {
|
|
738
|
-
const h = bits2int_2(hash);
|
|
739
|
-
if (truncateOnly)
|
|
740
|
-
return h;
|
|
741
|
-
const { n } = CURVE;
|
|
742
|
-
return h >= n ? h - n : h;
|
|
628
|
+
return isBiggerThanHalfOrder(s) ? modN(-s) : s;
|
|
743
629
|
}
|
|
744
|
-
|
|
630
|
+
// slice bytes num
|
|
631
|
+
const slcNum = (b, from, to) => ut.bytesToNumberBE(b.slice(from, to));
|
|
745
632
|
/**
|
|
746
633
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
747
634
|
*/
|
|
@@ -754,108 +641,68 @@ export function weierstrass(curveDef) {
|
|
|
754
641
|
}
|
|
755
642
|
// pair (bytes of r, bytes of s)
|
|
756
643
|
static fromCompact(hex) {
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
throw new TypeError(`${name}: Expected string or Uint8Array`);
|
|
761
|
-
const str = arr ? bytesToHex(hex) : hex;
|
|
762
|
-
const gl = CURVE.nByteLength * 2; // group length in hex, not ui8a
|
|
763
|
-
if (str.length !== 2 * gl)
|
|
764
|
-
throw new Error(`${name}: Expected ${gl / 2}-byte hex`);
|
|
765
|
-
const slice = (from, to) => ut.hexToNumber(str.slice(from, to));
|
|
766
|
-
return new Signature(slice(0, gl), slice(gl, 2 * gl));
|
|
644
|
+
const gl = CURVE.nByteLength;
|
|
645
|
+
hex = ensureBytes(hex, gl * 2);
|
|
646
|
+
return new Signature(slcNum(hex, 0, gl), slcNum(hex, gl, 2 * gl));
|
|
767
647
|
}
|
|
768
648
|
// DER encoded ECDSA signature
|
|
769
649
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
|
770
650
|
static fromDER(hex) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
const { r, s } = DER.parseSig(arr ? hex : ut.hexToBytes(hex));
|
|
651
|
+
if (typeof hex !== 'string' && !(hex instanceof Uint8Array))
|
|
652
|
+
throw new Error(`Signature.fromDER: Expected string or Uint8Array`);
|
|
653
|
+
const { r, s } = DER.toSig(ensureBytes(hex));
|
|
775
654
|
return new Signature(r, s);
|
|
776
655
|
}
|
|
777
656
|
assertValidity() {
|
|
778
|
-
|
|
779
|
-
if (!isWithinCurveOrder(r))
|
|
780
|
-
throw new Error('
|
|
781
|
-
if (!isWithinCurveOrder(s))
|
|
782
|
-
throw new Error('
|
|
657
|
+
// can use assertGE here
|
|
658
|
+
if (!isWithinCurveOrder(this.r))
|
|
659
|
+
throw new Error('r must be 0 < r < n');
|
|
660
|
+
if (!isWithinCurveOrder(this.s))
|
|
661
|
+
throw new Error('s must be 0 < s < n');
|
|
783
662
|
}
|
|
784
|
-
|
|
663
|
+
addRecoveryBit(recovery) {
|
|
785
664
|
return new Signature(this.r, this.s, recovery);
|
|
786
665
|
}
|
|
787
|
-
/**
|
|
788
|
-
* Recovers public key from signature with recovery bit. Throws on invalid hash.
|
|
789
|
-
* https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery
|
|
790
|
-
* It's also possible to recover key without bit: try all 4 bit values and check for sig match.
|
|
791
|
-
*
|
|
792
|
-
* ```
|
|
793
|
-
* recover(r, s, h) where
|
|
794
|
-
* u1 = hs^-1 mod n
|
|
795
|
-
* u2 = sr^-1 mod n
|
|
796
|
-
* Q = u1⋅G + u2⋅R
|
|
797
|
-
* ```
|
|
798
|
-
*
|
|
799
|
-
* @param msgHash message hash
|
|
800
|
-
* @returns Point corresponding to public key
|
|
801
|
-
*/
|
|
802
666
|
recoverPublicKey(msgHash) {
|
|
803
|
-
const {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
if (![0, 1, 2, 3].includes(
|
|
807
|
-
throw new Error('
|
|
808
|
-
const
|
|
809
|
-
const { n } = CURVE;
|
|
810
|
-
const radj = recovery === 2 || recovery === 3 ? r + n : r;
|
|
667
|
+
const { n: N } = CURVE; // ECDSA public key recovery secg.org/sec1-v2.pdf 4.1.6
|
|
668
|
+
const { r, s, recovery: rec } = this;
|
|
669
|
+
const h = bits2int_modN(ensureBytes(msgHash)); // Truncate hash
|
|
670
|
+
if (rec == null || ![0, 1, 2, 3].includes(rec))
|
|
671
|
+
throw new Error('recovery id invalid');
|
|
672
|
+
const radj = rec === 2 || rec === 3 ? r + N : r;
|
|
811
673
|
if (radj >= Fp.ORDER)
|
|
812
|
-
throw new Error('
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
const
|
|
816
|
-
const
|
|
817
|
-
const
|
|
818
|
-
const
|
|
819
|
-
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked
|
|
674
|
+
throw new Error('recovery id 2 or 3 invalid');
|
|
675
|
+
const prefix = (rec & 1) === 0 ? '02' : '03';
|
|
676
|
+
const R = Point.fromHex(prefix + numToNByteStr(radj));
|
|
677
|
+
const ir = invN(radj); // r^-1
|
|
678
|
+
const u1 = modN(-h * ir); // -hr^-1
|
|
679
|
+
const u2 = modN(s * ir); // sr^-1
|
|
680
|
+
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
|
|
820
681
|
if (!Q)
|
|
821
|
-
throw new Error('
|
|
682
|
+
throw new Error('point at infinify'); // unsafe is fine: no priv data leaked
|
|
822
683
|
Q.assertValidity();
|
|
823
684
|
return Q;
|
|
824
685
|
}
|
|
825
|
-
|
|
826
|
-
* Default signatures are always low-s, to prevent malleability.
|
|
827
|
-
* `sign(lowS: true)` always produces low-s sigs.
|
|
828
|
-
* `verify(lowS: true)` always fails for high-s.
|
|
829
|
-
*/
|
|
686
|
+
// Signatures should be low-s, to prevent malleability.
|
|
830
687
|
hasHighS() {
|
|
831
688
|
return isBiggerThanHalfOrder(this.s);
|
|
832
689
|
}
|
|
833
690
|
normalizeS() {
|
|
834
|
-
return this.hasHighS()
|
|
835
|
-
? new Signature(this.r, mod.mod(-this.s, CURVE_ORDER), this.recovery)
|
|
836
|
-
: this;
|
|
691
|
+
return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this;
|
|
837
692
|
}
|
|
838
693
|
// DER-encoded
|
|
839
694
|
toDERRawBytes() {
|
|
840
695
|
return ut.hexToBytes(this.toDERHex());
|
|
841
696
|
}
|
|
842
697
|
toDERHex() {
|
|
843
|
-
|
|
844
|
-
const sHex = DER.slice(toHex(this.s));
|
|
845
|
-
const rHex = DER.slice(toHex(this.r));
|
|
846
|
-
const sHexL = sHex.length / 2;
|
|
847
|
-
const rHexL = rHex.length / 2;
|
|
848
|
-
const sLen = toHex(sHexL);
|
|
849
|
-
const rLen = toHex(rHexL);
|
|
850
|
-
const length = toHex(rHexL + sHexL + 4);
|
|
851
|
-
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
|
|
698
|
+
return DER.hexFromSig({ r: this.r, s: this.s });
|
|
852
699
|
}
|
|
853
700
|
// padded bytes of r, then padded bytes of s
|
|
854
701
|
toCompactRawBytes() {
|
|
855
702
|
return ut.hexToBytes(this.toCompactHex());
|
|
856
703
|
}
|
|
857
704
|
toCompactHex() {
|
|
858
|
-
return
|
|
705
|
+
return numToNByteStr(this.r) + numToNByteStr(this.s);
|
|
859
706
|
}
|
|
860
707
|
}
|
|
861
708
|
const utils = {
|
|
@@ -868,17 +715,11 @@ export function weierstrass(curveDef) {
|
|
|
868
715
|
return false;
|
|
869
716
|
}
|
|
870
717
|
},
|
|
871
|
-
_bigintToBytes: numToField,
|
|
872
|
-
_bigintToString: numToFieldStr,
|
|
873
718
|
_normalizePrivateKey: normalizePrivateKey,
|
|
874
|
-
_normalizePublicKey: normalizePublicKey,
|
|
875
|
-
_isWithinCurveOrder: isWithinCurveOrder,
|
|
876
|
-
_isValidFieldElement: isValidFieldElement,
|
|
877
|
-
_weierstrassEquation: weierstrassEquation,
|
|
878
719
|
/**
|
|
879
720
|
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
|
880
721
|
*/
|
|
881
|
-
hashToPrivateKey: (hash) =>
|
|
722
|
+
hashToPrivateKey: (hash) => ut.numberToBytesBE(mod.hashToPrivateScalar(hash, CURVE_ORDER), CURVE.nByteLength),
|
|
882
723
|
/**
|
|
883
724
|
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
|
884
725
|
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
|
@@ -893,10 +734,9 @@ export function weierstrass(curveDef) {
|
|
|
893
734
|
* @returns cached point
|
|
894
735
|
*/
|
|
895
736
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
return cached;
|
|
737
|
+
point._setWindowSize(windowSize);
|
|
738
|
+
point.multiply(BigInt(3));
|
|
739
|
+
return point;
|
|
900
740
|
},
|
|
901
741
|
};
|
|
902
742
|
/**
|
|
@@ -905,7 +745,7 @@ export function weierstrass(curveDef) {
|
|
|
905
745
|
* @param isCompressed whether to return compact (default), or full key
|
|
906
746
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
907
747
|
*/
|
|
908
|
-
function getPublicKey(privateKey, isCompressed =
|
|
748
|
+
function getPublicKey(privateKey, isCompressed = true) {
|
|
909
749
|
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
|
910
750
|
}
|
|
911
751
|
/**
|
|
@@ -932,101 +772,104 @@ export function weierstrass(curveDef) {
|
|
|
932
772
|
* @param isCompressed whether to return compact (default), or full key
|
|
933
773
|
* @returns shared public key
|
|
934
774
|
*/
|
|
935
|
-
function getSharedSecret(privateA, publicB, isCompressed =
|
|
775
|
+
function getSharedSecret(privateA, publicB, isCompressed = true) {
|
|
936
776
|
if (isProbPub(privateA))
|
|
937
|
-
throw new
|
|
777
|
+
throw new Error('first arg must be private key');
|
|
938
778
|
if (!isProbPub(publicB))
|
|
939
|
-
throw new
|
|
940
|
-
const b =
|
|
941
|
-
b.assertValidity();
|
|
779
|
+
throw new Error('second arg must be public key');
|
|
780
|
+
const b = Point.fromHex(publicB); // check for being on-curve
|
|
942
781
|
return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed);
|
|
943
782
|
}
|
|
944
|
-
// RFC6979
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
function bits2octets(bytes) {
|
|
963
|
-
const z1 = bits2int(bytes);
|
|
964
|
-
const z2 = mod.mod(z1, CURVE_ORDER);
|
|
965
|
-
return int2octets(z2 < _0n ? z1 : z2);
|
|
966
|
-
}
|
|
783
|
+
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
784
|
+
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
785
|
+
// bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
|
|
786
|
+
// int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
|
|
787
|
+
const bits2int = CURVE.bits2int ||
|
|
788
|
+
function (bytes) {
|
|
789
|
+
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
|
790
|
+
// for some cases, since bytes.length * 8 is not actual bitLength.
|
|
791
|
+
const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
|
|
792
|
+
const num = ut.bytesToNumberBE(bytes); // check for == u8 done here
|
|
793
|
+
return delta > 0 ? num >> BigInt(delta) : num;
|
|
794
|
+
};
|
|
795
|
+
const bits2int_modN = CURVE.bits2int_modN ||
|
|
796
|
+
function (bytes) {
|
|
797
|
+
return modN(bits2int(bytes)); // can't use bytesToNumberBE here
|
|
798
|
+
};
|
|
799
|
+
// NOTE: pads output with zero as per spec
|
|
800
|
+
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
|
967
801
|
function int2octets(num) {
|
|
968
|
-
|
|
802
|
+
if (typeof num !== 'bigint')
|
|
803
|
+
throw new Error('Expected bigint');
|
|
804
|
+
if (!(_0n <= num && num < ORDER_MASK))
|
|
805
|
+
throw new Error(`Expected number < 2^${CURVE.nBitLength}`);
|
|
806
|
+
// works with order, can have different size than numToField!
|
|
807
|
+
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
|
969
808
|
}
|
|
970
809
|
// Steps A, D of RFC6979 3.2
|
|
971
810
|
// Creates RFC6979 seed; converts msg/privKey to numbers.
|
|
972
|
-
|
|
811
|
+
// Used only in sign, not in verify.
|
|
812
|
+
// NOTE: we cannot assume here that msgHash has same amount of bytes as curve order, this will be wrong at least for P521.
|
|
813
|
+
// Also it can be bigger for P224 + SHA256
|
|
814
|
+
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
973
815
|
if (msgHash == null)
|
|
974
816
|
throw new Error(`sign: expected valid message hash, not "${msgHash}"`);
|
|
817
|
+
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
818
|
+
// Ban legacy options
|
|
819
|
+
throw new Error('sign() legacy options not supported');
|
|
820
|
+
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
|
821
|
+
if (prehash)
|
|
822
|
+
msgHash = CURVE.hash(ensureBytes(msgHash));
|
|
823
|
+
if (lowS == null)
|
|
824
|
+
lowS = true; // RFC6979 3.2: we skip step A, because
|
|
975
825
|
// Step A is ignored, since we already provide hash instead of msg
|
|
976
|
-
|
|
826
|
+
// NOTE: instead of bits2int, we calling here truncateHash, since we need
|
|
827
|
+
// custom truncation for stark. For other curves it is essentially same as calling bits2int + mod
|
|
828
|
+
// However, we cannot later call bits2octets (which is truncateHash + int2octets), since nested bits2int is broken
|
|
829
|
+
// for curves where nBitLength % 8 !== 0, so we unwrap it here as int2octets call.
|
|
830
|
+
// const bits2octets = (bits)=>int2octets(bytesToNumberBE(truncateHash(bits)))
|
|
831
|
+
const h1int = bits2int_modN(ensureBytes(msgHash));
|
|
832
|
+
const h1octets = int2octets(h1int);
|
|
977
833
|
const d = normalizePrivateKey(privateKey);
|
|
978
834
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
979
|
-
const seedArgs = [int2octets(d),
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
const e =
|
|
835
|
+
const seedArgs = [int2octets(d), h1octets];
|
|
836
|
+
if (ent != null) {
|
|
837
|
+
// RFC6979 3.6: additional k' (optional)
|
|
838
|
+
if (ent === true)
|
|
839
|
+
ent = CURVE.randomBytes(Fp.BYTES);
|
|
840
|
+
const e = ensureBytes(ent);
|
|
985
841
|
if (e.length !== Fp.BYTES)
|
|
986
842
|
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
|
|
987
843
|
seedArgs.push(e);
|
|
988
844
|
}
|
|
989
|
-
|
|
990
|
-
//
|
|
991
|
-
//
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
// r = x mod n
|
|
1013
|
-
const r = mod.mod(q.x, n);
|
|
1014
|
-
if (r === _0n)
|
|
1015
|
-
return;
|
|
1016
|
-
// s = (m + dr)/k mod n where x/k == x*inv(k)
|
|
1017
|
-
const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n);
|
|
1018
|
-
if (s === _0n)
|
|
1019
|
-
return;
|
|
1020
|
-
// recovery bit is usually 0 or 1; rarely it's 2 or 3, when q.x > n
|
|
1021
|
-
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n);
|
|
1022
|
-
let normS = s;
|
|
1023
|
-
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
1024
|
-
normS = normalizeS(s);
|
|
1025
|
-
recovery ^= 1;
|
|
845
|
+
const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
846
|
+
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
|
847
|
+
// Converts signature params into point w r/s, checks result for validity.
|
|
848
|
+
function k2sig(kBytes) {
|
|
849
|
+
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
850
|
+
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
851
|
+
if (!isWithinCurveOrder(k))
|
|
852
|
+
return; // Important: all mod() calls here must be done over N
|
|
853
|
+
const ik = invN(k); // k^-1 mod n
|
|
854
|
+
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
|
855
|
+
const r = modN(q.x); // r = q.x mod n
|
|
856
|
+
if (r === _0n)
|
|
857
|
+
return;
|
|
858
|
+
const s = modN(ik * modN(m + modN(d * r))); // s = k^-1(m + rd) mod n
|
|
859
|
+
if (s === _0n)
|
|
860
|
+
return;
|
|
861
|
+
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
|
862
|
+
let normS = s;
|
|
863
|
+
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
864
|
+
normS = normalizeS(s); // if lowS was passed, ensure s is always
|
|
865
|
+
recovery ^= 1; // // in the bottom half of N
|
|
866
|
+
}
|
|
867
|
+
return new Signature(r, normS, recovery); // use normS, not s
|
|
1026
868
|
}
|
|
1027
|
-
return
|
|
869
|
+
return { seed, k2sig };
|
|
1028
870
|
}
|
|
1029
|
-
const defaultSigOpts = { lowS: CURVE.lowS };
|
|
871
|
+
const defaultSigOpts = { lowS: CURVE.lowS, prehash: false };
|
|
872
|
+
const defaultVerOpts = { lowS: CURVE.lowS, prehash: false };
|
|
1030
873
|
/**
|
|
1031
874
|
* Signs message hash (not message: you need to hash it by yourself).
|
|
1032
875
|
* ```
|
|
@@ -1035,28 +878,16 @@ export function weierstrass(curveDef) {
|
|
|
1035
878
|
* r = x mod n
|
|
1036
879
|
* s = (m + dr)/k mod n
|
|
1037
880
|
* ```
|
|
1038
|
-
* @param opts `lowS, extraEntropy`
|
|
881
|
+
* @param opts `lowS, extraEntropy, prehash`
|
|
1039
882
|
*/
|
|
1040
883
|
function sign(msgHash, privKey, opts = defaultSigOpts) {
|
|
1041
|
-
// Steps A, D of RFC6979 3.2.
|
|
1042
|
-
const
|
|
1043
|
-
// Steps B, C, D, E, F, G
|
|
1044
|
-
const drbg = new HmacDrbg(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
|
|
1045
|
-
drbg.reseedSync(seed);
|
|
1046
|
-
// Step H3, repeat until k is in range [1, n-1]
|
|
1047
|
-
let sig;
|
|
1048
|
-
while (!(sig = kmdToSig(drbg.generateSync(), m, d, opts.lowS)))
|
|
1049
|
-
drbg.reseedSync();
|
|
1050
|
-
return sig;
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* Signs a message (not message hash).
|
|
1054
|
-
*/
|
|
1055
|
-
function signUnhashed(msg, privKey, opts = defaultSigOpts) {
|
|
1056
|
-
return sign(CURVE.hash(ut.ensureBytes(msg)), privKey, opts);
|
|
884
|
+
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
|
885
|
+
const genUntil = hmacDrbg(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
|
|
886
|
+
return genUntil(seed, k2sig); // Steps B, C, D, E, F, G
|
|
1057
887
|
}
|
|
1058
888
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
1059
889
|
Point.BASE._setWindowSize(8);
|
|
890
|
+
// utils.precompute(8, ProjectivePoint.BASE)
|
|
1060
891
|
/**
|
|
1061
892
|
* Verifies a signature against message hash and public key.
|
|
1062
893
|
* Rejects lowS signatures by default: to override,
|
|
@@ -1070,50 +901,47 @@ export function weierstrass(curveDef) {
|
|
|
1070
901
|
* mod(R.x, n) == r
|
|
1071
902
|
* ```
|
|
1072
903
|
*/
|
|
1073
|
-
function verify(signature, msgHash, publicKey, opts =
|
|
904
|
+
function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
|
|
905
|
+
let P;
|
|
906
|
+
let _sig = undefined;
|
|
907
|
+
if (publicKey instanceof Point)
|
|
908
|
+
throw new Error('publicKey must be hex');
|
|
1074
909
|
try {
|
|
1075
|
-
if (signature instanceof
|
|
1076
|
-
signature
|
|
910
|
+
if (signature && typeof signature === 'object' && !(signature instanceof Uint8Array)) {
|
|
911
|
+
const { r, s } = signature;
|
|
912
|
+
_sig = new Signature(r, s); // assertValidity() is executed on creation
|
|
1077
913
|
}
|
|
1078
914
|
else {
|
|
1079
|
-
// Signature can be represented in 2 ways: compact (
|
|
1080
|
-
// Since DER can also be
|
|
915
|
+
// Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
|
|
916
|
+
// Since DER can also be 2*nByteLength bytes, we check for it first.
|
|
1081
917
|
try {
|
|
1082
|
-
|
|
918
|
+
_sig = Signature.fromDER(signature);
|
|
1083
919
|
}
|
|
1084
920
|
catch (derError) {
|
|
1085
|
-
if (!(derError instanceof
|
|
921
|
+
if (!(derError instanceof DER.Err))
|
|
1086
922
|
throw derError;
|
|
1087
|
-
|
|
923
|
+
_sig = Signature.fromCompact(signature);
|
|
1088
924
|
}
|
|
1089
925
|
}
|
|
1090
|
-
msgHash =
|
|
926
|
+
msgHash = ensureBytes(msgHash);
|
|
927
|
+
P = Point.fromHex(publicKey);
|
|
1091
928
|
}
|
|
1092
929
|
catch (error) {
|
|
1093
930
|
return false;
|
|
1094
931
|
}
|
|
1095
|
-
if (opts.lowS &&
|
|
932
|
+
if (opts.lowS && _sig.hasHighS())
|
|
1096
933
|
return false;
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
const
|
|
1105
|
-
const { r, s } = signature;
|
|
1106
|
-
const h = truncateHash(msgHash);
|
|
1107
|
-
const sinv = mod.invert(s, n); // s^-1
|
|
1108
|
-
// R = u1⋅G - u2⋅P
|
|
1109
|
-
const u1 = mod.mod(h * sinv, n);
|
|
1110
|
-
const u2 = mod.mod(r * sinv, n);
|
|
1111
|
-
// Some implementations compare R.x in projective, without inversion.
|
|
1112
|
-
// The speed-up is <5%, so we don't complicate the code.
|
|
1113
|
-
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2);
|
|
934
|
+
if (opts.prehash)
|
|
935
|
+
msgHash = CURVE.hash(msgHash);
|
|
936
|
+
const { r, s } = _sig;
|
|
937
|
+
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
|
938
|
+
const is = invN(s); // s^-1
|
|
939
|
+
const u1 = modN(h * is); // u1 = hs^-1 mod n
|
|
940
|
+
const u2 = modN(r * is); // u2 = rs^-1 mod n
|
|
941
|
+
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine(); // R = u1⋅G + u2⋅P
|
|
1114
942
|
if (!R)
|
|
1115
943
|
return false;
|
|
1116
|
-
const v =
|
|
944
|
+
const v = modN(R.x);
|
|
1117
945
|
return v === r;
|
|
1118
946
|
}
|
|
1119
947
|
return {
|
|
@@ -1121,16 +949,15 @@ export function weierstrass(curveDef) {
|
|
|
1121
949
|
getPublicKey,
|
|
1122
950
|
getSharedSecret,
|
|
1123
951
|
sign,
|
|
1124
|
-
signUnhashed,
|
|
1125
952
|
verify,
|
|
1126
|
-
Point,
|
|
1127
|
-
ProjectivePoint,
|
|
953
|
+
// Point,
|
|
954
|
+
ProjectivePoint: Point,
|
|
1128
955
|
Signature,
|
|
1129
956
|
utils,
|
|
1130
957
|
};
|
|
1131
958
|
}
|
|
1132
959
|
// Implementation of the Shallue and van de Woestijne method for any Weierstrass curve
|
|
1133
|
-
// TODO: check if there is a way to merge this with
|
|
960
|
+
// TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular?
|
|
1134
961
|
// b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
|
1135
962
|
// b = False and y = sqrt(Z * (u / v)) otherwise.
|
|
1136
963
|
export function SWUFpSqrtRatio(Fp, Z) {
|
|
@@ -1149,7 +976,7 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1149
976
|
let sqrtRatio = (u, v) => {
|
|
1150
977
|
let tv1 = c6; // 1. tv1 = c6
|
|
1151
978
|
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
1152
|
-
let tv3 = Fp.
|
|
979
|
+
let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
|
|
1153
980
|
tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
|
|
1154
981
|
let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
|
|
1155
982
|
tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
|
|
@@ -1158,7 +985,7 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1158
985
|
tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
|
|
1159
986
|
let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
|
|
1160
987
|
tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
|
|
1161
|
-
let isQR = Fp.
|
|
988
|
+
let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
|
|
1162
989
|
tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
|
|
1163
990
|
tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
|
|
1164
991
|
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
@@ -1167,7 +994,7 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1167
994
|
for (let i = c1; i > 1; i--) {
|
|
1168
995
|
let tv5 = 2n ** (i - 2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
|
|
1169
996
|
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1170
|
-
const e1 = Fp.
|
|
997
|
+
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1171
998
|
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
1172
999
|
tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
|
|
1173
1000
|
tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
|
|
@@ -1179,16 +1006,16 @@ export function SWUFpSqrtRatio(Fp, Z) {
|
|
|
1179
1006
|
if (Fp.ORDER % 4n === 3n) {
|
|
1180
1007
|
// sqrt_ratio_3mod4(u, v)
|
|
1181
1008
|
const c1 = (Fp.ORDER - 3n) / 4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1182
|
-
const c2 = Fp.sqrt(Fp.
|
|
1009
|
+
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1183
1010
|
sqrtRatio = (u, v) => {
|
|
1184
|
-
let tv1 = Fp.
|
|
1011
|
+
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
1185
1012
|
const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
|
|
1186
1013
|
tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
|
|
1187
1014
|
let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
|
|
1188
1015
|
y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
|
|
1189
1016
|
const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
|
|
1190
|
-
const tv3 = Fp.mul(Fp.
|
|
1191
|
-
const isQR = Fp.
|
|
1017
|
+
const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
|
|
1018
|
+
const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
|
|
1192
1019
|
let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
|
|
1193
1020
|
return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
|
|
1194
1021
|
};
|
|
@@ -1210,16 +1037,16 @@ export function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
1210
1037
|
return (u) => {
|
|
1211
1038
|
// prettier-ignore
|
|
1212
1039
|
let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
|
|
1213
|
-
tv1 = Fp.
|
|
1040
|
+
tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
|
1214
1041
|
tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
|
|
1215
|
-
tv2 = Fp.
|
|
1042
|
+
tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
|
|
1216
1043
|
tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
|
|
1217
1044
|
tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
|
|
1218
1045
|
tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
|
|
1219
|
-
tv4 = Fp.cmov(opts.Z, Fp.
|
|
1046
|
+
tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
|
|
1220
1047
|
tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
|
|
1221
|
-
tv2 = Fp.
|
|
1222
|
-
tv6 = Fp.
|
|
1048
|
+
tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
|
|
1049
|
+
tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
|
|
1223
1050
|
tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
|
|
1224
1051
|
tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
|
|
1225
1052
|
tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
|
|
@@ -1233,7 +1060,7 @@ export function mapToCurveSimpleSWU(Fp, opts) {
|
|
|
1233
1060
|
x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
|
|
1234
1061
|
y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
|
|
1235
1062
|
const e1 = Fp.isOdd(u) === Fp.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
|
|
1236
|
-
y = Fp.cmov(Fp.
|
|
1063
|
+
y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
|
|
1237
1064
|
x = Fp.div(x, tv4); // 25. x = x / tv4
|
|
1238
1065
|
return { x, y };
|
|
1239
1066
|
};
|