@noble/curves 0.3.1 → 0.4.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 +2 -1
- package/lib/bls.d.ts +79 -0
- package/lib/bls.js +304 -0
- package/lib/edwards.d.ts +10 -6
- package/lib/edwards.js +16 -11
- package/lib/esm/bls.js +300 -0
- package/lib/esm/edwards.js +17 -12
- package/lib/esm/group.js +2 -2
- package/lib/esm/hashToCurve.js +105 -0
- package/lib/esm/modular.js +131 -50
- package/lib/esm/utils.js +25 -19
- package/lib/esm/weierstrass.js +351 -272
- package/lib/group.js +2 -2
- package/lib/hashToCurve.d.ts +13 -0
- package/lib/hashToCurve.js +112 -0
- package/lib/modular.d.ts +37 -17
- package/lib/modular.js +138 -54
- package/lib/utils.d.ts +28 -10
- package/lib/utils.js +31 -22
- package/lib/weierstrass.d.ts +106 -69
- package/lib/weierstrass.js +352 -272
- package/package.json +23 -44
- package/lib/crypto.d.ts +0 -4
- package/lib/crypto.js +0 -8
- package/lib/cryptoBrowser.d.ts +0 -4
- package/lib/cryptoBrowser.js +0 -7
- package/lib/definitions/_shortw_utils.d.ts +0 -63
- package/lib/definitions/_shortw_utils.js +0 -18
- package/lib/definitions/bn.d.ts +0 -7
- package/lib/definitions/bn.js +0 -23
- package/lib/definitions/ed25519.d.ts +0 -49
- package/lib/definitions/ed25519.js +0 -308
- package/lib/definitions/ed448.d.ts +0 -3
- package/lib/definitions/ed448.js +0 -127
- package/lib/definitions/index.d.ts +0 -0
- package/lib/definitions/index.js +0 -2
- package/lib/definitions/jubjub.d.ts +0 -7
- package/lib/definitions/jubjub.js +0 -55
- package/lib/definitions/p192.d.ts +0 -112
- package/lib/definitions/p192.js +0 -23
- package/lib/definitions/p224.d.ts +0 -112
- package/lib/definitions/p224.js +0 -24
- package/lib/definitions/p256.d.ts +0 -112
- package/lib/definitions/p256.js +0 -23
- package/lib/definitions/p384.d.ts +0 -112
- package/lib/definitions/p384.js +0 -24
- package/lib/definitions/p521.d.ts +0 -113
- package/lib/definitions/p521.js +0 -36
- package/lib/definitions/pasta.d.ts +0 -2
- package/lib/definitions/pasta.js +0 -32
- package/lib/definitions/secp256k1.d.ts +0 -87
- package/lib/definitions/secp256k1.js +0 -245
- package/lib/definitions/stark.d.ts +0 -62
- package/lib/definitions/stark.js +0 -248
- package/lib/esm/crypto.js +0 -5
- package/lib/esm/cryptoBrowser.js +0 -4
- package/lib/esm/definitions/_shortw_utils.js +0 -13
- package/lib/esm/definitions/bn.js +0 -20
- package/lib/esm/definitions/ed25519.js +0 -304
- package/lib/esm/definitions/ed448.js +0 -124
- package/lib/esm/definitions/index.js +0 -2
- package/lib/esm/definitions/jubjub.js +0 -50
- package/lib/esm/definitions/p192.js +0 -20
- package/lib/esm/definitions/p224.js +0 -21
- package/lib/esm/definitions/p256.js +0 -20
- package/lib/esm/definitions/p384.js +0 -21
- package/lib/esm/definitions/p521.js +0 -33
- package/lib/esm/definitions/pasta.js +0 -29
- package/lib/esm/definitions/secp256k1.js +0 -241
- package/lib/esm/definitions/stark.js +0 -227
package/lib/esm/weierstrass.js
CHANGED
|
@@ -8,42 +8,10 @@
|
|
|
8
8
|
// 3. truncateHash() truncateOnly mode
|
|
9
9
|
// 4. DRBG supports outputLen bigger than outputLen of hmac
|
|
10
10
|
import * as mod from './modular.js';
|
|
11
|
-
import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar,
|
|
11
|
+
import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, } from './utils.js';
|
|
12
|
+
import * as utils from './utils.js';
|
|
13
|
+
import { hash_to_field, validateHTFOpts } from './hashToCurve.js';
|
|
12
14
|
import { wNAF } from './group.js';
|
|
13
|
-
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
|
|
14
|
-
function validateOpts(curve) {
|
|
15
|
-
const opts = utilOpts(curve);
|
|
16
|
-
for (const i of ['a', 'b']) {
|
|
17
|
-
if (typeof opts[i] !== 'bigint')
|
|
18
|
-
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
19
|
-
}
|
|
20
|
-
for (const fn of ['hash', 'hmac']) {
|
|
21
|
-
if (typeof opts[fn] !== 'function')
|
|
22
|
-
throw new Error(`Invalid ${fn} function`);
|
|
23
|
-
}
|
|
24
|
-
for (const fn of ['randomBytes']) {
|
|
25
|
-
if (opts[fn] === undefined)
|
|
26
|
-
continue; // Optional
|
|
27
|
-
if (typeof opts[fn] !== 'function')
|
|
28
|
-
throw new Error(`Invalid ${fn} function`);
|
|
29
|
-
}
|
|
30
|
-
if (!Number.isSafeInteger(opts.hash.outputLen))
|
|
31
|
-
throw new Error('Invalid hash function');
|
|
32
|
-
const endo = opts.endo;
|
|
33
|
-
if (endo) {
|
|
34
|
-
if (opts.a !== _0n) {
|
|
35
|
-
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
|
|
36
|
-
}
|
|
37
|
-
if (typeof endo !== 'object' ||
|
|
38
|
-
typeof endo.beta !== 'bigint' ||
|
|
39
|
-
typeof endo.splitScalar !== 'function') {
|
|
40
|
-
throw new Error('Expected endomorphism with beta: bigint and splitScalar: function');
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// Set defaults
|
|
44
|
-
return Object.freeze({ lowS: true, randomBytes: utilRandomBytes, ...opts });
|
|
45
|
-
}
|
|
46
|
-
// TODO: convert bits to bytes aligned to 32 bits? (224 for example)
|
|
47
15
|
// DER encoding utilities
|
|
48
16
|
class DERError extends Error {
|
|
49
17
|
constructor(message) {
|
|
@@ -90,108 +58,73 @@ const _1n = BigInt(1);
|
|
|
90
58
|
const _2n = BigInt(2);
|
|
91
59
|
const _3n = BigInt(3);
|
|
92
60
|
const _8n = BigInt(8);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.hashLen = hashLen;
|
|
100
|
-
this.qByteLen = qByteLen;
|
|
101
|
-
this.hmacFn = hmacFn;
|
|
102
|
-
if (typeof hashLen !== 'number' || hashLen < 2)
|
|
103
|
-
throw new Error('hashLen must be a number');
|
|
104
|
-
if (typeof qByteLen !== 'number' || qByteLen < 2)
|
|
105
|
-
throw new Error('qByteLen must be a number');
|
|
106
|
-
if (typeof hmacFn !== 'function')
|
|
107
|
-
throw new Error('hmacFn must be a function');
|
|
108
|
-
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
109
|
-
this.v = new Uint8Array(hashLen).fill(1);
|
|
110
|
-
this.k = new Uint8Array(hashLen).fill(0);
|
|
111
|
-
this.counter = 0;
|
|
112
|
-
}
|
|
113
|
-
hmacSync(...values) {
|
|
114
|
-
return this.hmacFn(this.k, ...values);
|
|
115
|
-
}
|
|
116
|
-
incr() {
|
|
117
|
-
if (this.counter >= 1000)
|
|
118
|
-
throw new Error('Tried 1,000 k values for sign(), all were invalid');
|
|
119
|
-
this.counter += 1;
|
|
61
|
+
function validatePointOpts(curve) {
|
|
62
|
+
const opts = utils.validateOpts(curve);
|
|
63
|
+
const Fp = opts.Fp;
|
|
64
|
+
for (const i of ['a', 'b']) {
|
|
65
|
+
if (!Fp.isValid(curve[i]))
|
|
66
|
+
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
120
67
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed);
|
|
127
|
-
this.v = this.hmacSync(this.v);
|
|
68
|
+
for (const i of ['isTorsionFree', 'clearCofactor', 'mapToCurve']) {
|
|
69
|
+
if (curve[i] === undefined)
|
|
70
|
+
continue; // Optional
|
|
71
|
+
if (typeof curve[i] !== 'function')
|
|
72
|
+
throw new Error(`Invalid ${i} function`);
|
|
128
73
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
len += this.v.length;
|
|
74
|
+
const endo = opts.endo;
|
|
75
|
+
if (endo) {
|
|
76
|
+
if (!Fp.equals(opts.a, Fp.ZERO)) {
|
|
77
|
+
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
|
|
78
|
+
}
|
|
79
|
+
if (typeof endo !== 'object' ||
|
|
80
|
+
typeof endo.beta !== 'bigint' ||
|
|
81
|
+
typeof endo.splitScalar !== 'function') {
|
|
82
|
+
throw new Error('Expected endomorphism with beta: bigint and splitScalar: function');
|
|
139
83
|
}
|
|
140
|
-
return concatBytes(...out);
|
|
141
84
|
}
|
|
85
|
+
if (typeof opts.fromBytes !== 'function')
|
|
86
|
+
throw new Error('Invalid fromBytes function');
|
|
87
|
+
if (typeof opts.toBytes !== 'function')
|
|
88
|
+
throw new Error('Invalid fromBytes function');
|
|
89
|
+
// Requires including hashToCurve file
|
|
90
|
+
if (opts.htfDefaults !== undefined)
|
|
91
|
+
validateHTFOpts(opts.htfDefaults);
|
|
92
|
+
// Set defaults
|
|
93
|
+
return Object.freeze({ ...opts });
|
|
142
94
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
const CURVE_ORDER = CURVE.n;
|
|
95
|
+
export function weierstrassPoints(opts) {
|
|
96
|
+
const CURVE = validatePointOpts(opts);
|
|
97
|
+
const Fp = CURVE.Fp;
|
|
147
98
|
// Lengths
|
|
148
99
|
// All curves has same field / group length as for now, but it can be different for other curves
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
if (fieldLen > 2048)
|
|
152
|
-
throw new Error('Field lengths over 2048 are not supported');
|
|
153
|
-
const compressedLen = fieldLen + 1; // 33
|
|
154
|
-
const uncompressedLen = 2 * fieldLen + 1; // 65
|
|
100
|
+
const { nByteLength, nBitLength } = CURVE;
|
|
101
|
+
const groupLen = nByteLength;
|
|
155
102
|
// Not using ** operator with bigints for old engines.
|
|
156
103
|
// 2n ** (8n * 32n) == 2n << (8n * 32n - 1n)
|
|
157
|
-
const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n);
|
|
158
|
-
function numToFieldStr(num) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return num.toString(16).padStart(2 * fieldLen, '0');
|
|
164
|
-
}
|
|
165
|
-
function numToField(num) {
|
|
166
|
-
const b = hexToBytes(numToFieldStr(num));
|
|
167
|
-
if (b.length !== fieldLen)
|
|
168
|
-
throw new Error(`Error: expected ${fieldLen} bytes`);
|
|
169
|
-
return b;
|
|
170
|
-
}
|
|
171
|
-
function modP(n, m = CURVE.P) {
|
|
172
|
-
return mod.mod(n, m);
|
|
173
|
-
}
|
|
104
|
+
//const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n);
|
|
105
|
+
// function numToFieldStr(num: bigint): string {
|
|
106
|
+
// if (typeof num !== 'bigint') throw new Error('Expected bigint');
|
|
107
|
+
// if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`);
|
|
108
|
+
// return num.toString(16).padStart(2 * fieldLen, '0');
|
|
109
|
+
// }
|
|
174
110
|
/**
|
|
175
111
|
* y² = x³ + ax + b: Short weierstrass curve formula
|
|
176
112
|
* @returns y²
|
|
177
113
|
*/
|
|
178
114
|
function weierstrassEquation(x) {
|
|
179
115
|
const { a, b } = CURVE;
|
|
180
|
-
const x2 =
|
|
181
|
-
const x3 =
|
|
182
|
-
return
|
|
116
|
+
const x2 = Fp.square(x); // x * x
|
|
117
|
+
const x3 = Fp.multiply(x2, x); // x2 * x
|
|
118
|
+
return Fp.add(Fp.add(x3, Fp.multiply(x, a)), b); // x3 + a * x + b
|
|
183
119
|
}
|
|
184
120
|
function isWithinCurveOrder(num) {
|
|
185
121
|
return _0n < num && num < CURVE.n;
|
|
186
122
|
}
|
|
187
|
-
function isValidFieldElement(num) {
|
|
188
|
-
return _0n < num && num < CURVE.P;
|
|
189
|
-
}
|
|
190
123
|
function normalizePrivateKey(key) {
|
|
191
|
-
let num;
|
|
192
124
|
if (typeof CURVE.normalizePrivateKey === 'function') {
|
|
193
125
|
key = CURVE.normalizePrivateKey(key);
|
|
194
126
|
}
|
|
127
|
+
let num;
|
|
195
128
|
if (typeof key === 'bigint') {
|
|
196
129
|
num = key;
|
|
197
130
|
}
|
|
@@ -211,32 +144,12 @@ export function weierstrass(curveDef) {
|
|
|
211
144
|
else {
|
|
212
145
|
throw new TypeError('Expected valid private key');
|
|
213
146
|
}
|
|
147
|
+
if (CURVE.wrapPrivateKey)
|
|
148
|
+
num = mod.mod(num, CURVE.n);
|
|
214
149
|
if (!isWithinCurveOrder(num))
|
|
215
150
|
throw new Error('Expected private key: 0 < key < n');
|
|
216
151
|
return num;
|
|
217
152
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Normalizes hex, bytes, Point to Point. Checks for curve equation.
|
|
220
|
-
*/
|
|
221
|
-
function normalizePublicKey(publicKey) {
|
|
222
|
-
if (publicKey instanceof Point) {
|
|
223
|
-
publicKey.assertValidity();
|
|
224
|
-
return publicKey;
|
|
225
|
-
}
|
|
226
|
-
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
|
227
|
-
return Point.fromHex(publicKey);
|
|
228
|
-
// This can happen because PointType can be instance of different class
|
|
229
|
-
}
|
|
230
|
-
else
|
|
231
|
-
throw new Error(`Unknown type of public key: ${publicKey}`);
|
|
232
|
-
}
|
|
233
|
-
function isBiggerThanHalfOrder(number) {
|
|
234
|
-
const HALF = CURVE_ORDER >> _1n;
|
|
235
|
-
return number > HALF;
|
|
236
|
-
}
|
|
237
|
-
function normalizeS(s) {
|
|
238
|
-
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
|
239
|
-
}
|
|
240
153
|
function normalizeScalar(num) {
|
|
241
154
|
if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
|
|
242
155
|
return BigInt(num);
|
|
@@ -244,20 +157,6 @@ export function weierstrass(curveDef) {
|
|
|
244
157
|
return num;
|
|
245
158
|
throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n');
|
|
246
159
|
}
|
|
247
|
-
const sqrtModCurve = CURVE.sqrtMod || mod.sqrt;
|
|
248
|
-
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
|
249
|
-
function _truncateHash(hash, truncateOnly = false) {
|
|
250
|
-
const { n, nBitLength } = CURVE;
|
|
251
|
-
const byteLength = hash.length;
|
|
252
|
-
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
|
253
|
-
let h = bytesToNumberBE(hash);
|
|
254
|
-
if (delta > 0)
|
|
255
|
-
h = h >> BigInt(delta);
|
|
256
|
-
if (!truncateOnly && h >= n)
|
|
257
|
-
h -= n;
|
|
258
|
-
return h;
|
|
259
|
-
}
|
|
260
|
-
const truncateHash = CURVE.truncateHash || _truncateHash;
|
|
261
160
|
/**
|
|
262
161
|
* Jacobian Point works in 3d / jacobi coordinates: (x, y, z) ∋ (x=x/z², y=y/z³)
|
|
263
162
|
* Default Point works in 2d / affine coordinates: (x, y)
|
|
@@ -276,7 +175,7 @@ export function weierstrass(curveDef) {
|
|
|
276
175
|
// fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
|
|
277
176
|
if (p.equals(Point.ZERO))
|
|
278
177
|
return JacobianPoint.ZERO;
|
|
279
|
-
return new JacobianPoint(p.x, p.y,
|
|
178
|
+
return new JacobianPoint(p.x, p.y, Fp.ONE);
|
|
280
179
|
}
|
|
281
180
|
/**
|
|
282
181
|
* Takes a bunch of Jacobian Points but executes only one
|
|
@@ -284,7 +183,7 @@ export function weierstrass(curveDef) {
|
|
|
284
183
|
* so this improves performance massively.
|
|
285
184
|
*/
|
|
286
185
|
static toAffineBatch(points) {
|
|
287
|
-
const toInv =
|
|
186
|
+
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
|
288
187
|
return points.map((p, i) => p.toAffine(toInv[i]));
|
|
289
188
|
}
|
|
290
189
|
static normalizeZ(points) {
|
|
@@ -298,19 +197,19 @@ export function weierstrass(curveDef) {
|
|
|
298
197
|
throw new TypeError('JacobianPoint expected');
|
|
299
198
|
const { x: X1, y: Y1, z: Z1 } = this;
|
|
300
199
|
const { x: X2, y: Y2, z: Z2 } = other;
|
|
301
|
-
const Z1Z1 =
|
|
302
|
-
const Z2Z2 =
|
|
303
|
-
const U1 =
|
|
304
|
-
const U2 =
|
|
305
|
-
const S1 =
|
|
306
|
-
const S2 =
|
|
307
|
-
return U1
|
|
200
|
+
const Z1Z1 = Fp.square(Z1); // Z1 * Z1
|
|
201
|
+
const Z2Z2 = Fp.square(Z2); // Z2 * Z2
|
|
202
|
+
const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2
|
|
203
|
+
const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1
|
|
204
|
+
const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2
|
|
205
|
+
const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1
|
|
206
|
+
return Fp.equals(U1, U2) && Fp.equals(S1, S2);
|
|
308
207
|
}
|
|
309
208
|
/**
|
|
310
209
|
* Flips point to one corresponding to (x, -y) in Affine coordinates.
|
|
311
210
|
*/
|
|
312
211
|
negate() {
|
|
313
|
-
return new JacobianPoint(this.x,
|
|
212
|
+
return new JacobianPoint(this.x, Fp.negate(this.y), this.z);
|
|
314
213
|
}
|
|
315
214
|
// Fast algo for doubling 2 Jacobian Points.
|
|
316
215
|
// From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
|
|
@@ -321,31 +220,31 @@ export function weierstrass(curveDef) {
|
|
|
321
220
|
// Faster algorithm: when a=0
|
|
322
221
|
// From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
|
323
222
|
// Cost: 2M + 5S + 6add + 3*2 + 1*3 + 1*8.
|
|
324
|
-
if (a
|
|
325
|
-
const A =
|
|
326
|
-
const B =
|
|
327
|
-
const C =
|
|
328
|
-
const x1b = X1 + B
|
|
329
|
-
const D =
|
|
330
|
-
const E =
|
|
331
|
-
const F =
|
|
332
|
-
const X3 =
|
|
333
|
-
const Y3 =
|
|
334
|
-
const Z3 =
|
|
223
|
+
if (Fp.isZero(a)) {
|
|
224
|
+
const A = Fp.square(X1); // X1 * X1
|
|
225
|
+
const B = Fp.square(Y1); // Y1 * Y1
|
|
226
|
+
const C = Fp.square(B); // B * B
|
|
227
|
+
const x1b = Fp.addN(X1, B); // X1 + B
|
|
228
|
+
const D = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(x1b), A), C), _2n); // ((x1b * x1b) - A - C) * 2
|
|
229
|
+
const E = Fp.multiply(A, _3n); // A * 3
|
|
230
|
+
const F = Fp.square(E); // E * E
|
|
231
|
+
const X3 = Fp.subtract(F, Fp.multiplyN(D, _2n)); // F - 2 * D
|
|
232
|
+
const Y3 = Fp.subtract(Fp.multiplyN(E, Fp.subtractN(D, X3)), Fp.multiplyN(C, _8n)); // E * (D - X3) - 8 * C;
|
|
233
|
+
const Z3 = Fp.multiply(Fp.multiplyN(Y1, _2n), Z1); // 2 * Y1 * Z1
|
|
335
234
|
return new JacobianPoint(X3, Y3, Z3);
|
|
336
235
|
}
|
|
337
|
-
const XX =
|
|
338
|
-
const YY =
|
|
339
|
-
const YYYY =
|
|
340
|
-
const ZZ =
|
|
341
|
-
const tmp1 =
|
|
342
|
-
const S =
|
|
343
|
-
const M =
|
|
344
|
-
const T =
|
|
236
|
+
const XX = Fp.square(X1); // X1 * X1
|
|
237
|
+
const YY = Fp.square(Y1); // Y1 * Y1
|
|
238
|
+
const YYYY = Fp.square(YY); // YY * YY
|
|
239
|
+
const ZZ = Fp.square(Z1); // Z1 * Z1
|
|
240
|
+
const tmp1 = Fp.add(X1, YY); // X1 + YY
|
|
241
|
+
const S = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(tmp1), XX), YYYY), _2n); // 2*((X1+YY)^2-XX-YYYY)
|
|
242
|
+
const M = Fp.add(Fp.multiplyN(XX, _3n), Fp.multiplyN(Fp.square(ZZ), a)); // 3 * XX + a * ZZ^2
|
|
243
|
+
const T = Fp.subtract(Fp.square(M), Fp.multiplyN(S, _2n)); // M^2-2*S
|
|
345
244
|
const X3 = T;
|
|
346
|
-
const Y3 =
|
|
347
|
-
const y1az1 =
|
|
348
|
-
const Z3 =
|
|
245
|
+
const Y3 = Fp.subtract(Fp.multiplyN(M, Fp.subtractN(S, T)), Fp.multiplyN(YYYY, _8n)); // M*(S-T)-8*YYYY
|
|
246
|
+
const y1az1 = Fp.add(Y1, Z1); // (Y1+Z1)
|
|
247
|
+
const Z3 = Fp.subtract(Fp.subtractN(Fp.square(y1az1), YY), ZZ); // (Y1+Z1)^2-YY-ZZ
|
|
349
248
|
return new JacobianPoint(X3, Y3, Z3);
|
|
350
249
|
}
|
|
351
250
|
// Fast algo for adding 2 Jacobian Points.
|
|
@@ -357,28 +256,28 @@ export function weierstrass(curveDef) {
|
|
|
357
256
|
throw new TypeError('JacobianPoint expected');
|
|
358
257
|
const { x: X1, y: Y1, z: Z1 } = this;
|
|
359
258
|
const { x: X2, y: Y2, z: Z2 } = other;
|
|
360
|
-
if (X2
|
|
259
|
+
if (Fp.isZero(X2) || Fp.isZero(Y2))
|
|
361
260
|
return this;
|
|
362
|
-
if (X1
|
|
261
|
+
if (Fp.isZero(X1) || Fp.isZero(Y1))
|
|
363
262
|
return other;
|
|
364
263
|
// We're using same code in equals()
|
|
365
|
-
const Z1Z1 =
|
|
366
|
-
const Z2Z2 =
|
|
367
|
-
const U1 =
|
|
368
|
-
const U2 =
|
|
369
|
-
const S1 =
|
|
370
|
-
const S2 =
|
|
371
|
-
const H =
|
|
372
|
-
const r =
|
|
264
|
+
const Z1Z1 = Fp.square(Z1); // Z1Z1 = Z1^2
|
|
265
|
+
const Z2Z2 = Fp.square(Z2); // Z2Z2 = Z2^2;
|
|
266
|
+
const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2
|
|
267
|
+
const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1
|
|
268
|
+
const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2
|
|
269
|
+
const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1
|
|
270
|
+
const H = Fp.subtractN(U2, U1); // H = U2 - U1
|
|
271
|
+
const r = Fp.subtractN(S2, S1); // S2 - S1
|
|
373
272
|
// H = 0 meaning it's the same point.
|
|
374
|
-
if (H
|
|
375
|
-
return r
|
|
376
|
-
const HH =
|
|
377
|
-
const HHH =
|
|
378
|
-
const V =
|
|
379
|
-
const X3 =
|
|
380
|
-
const Y3 =
|
|
381
|
-
const Z3 =
|
|
273
|
+
if (Fp.isZero(H))
|
|
274
|
+
return Fp.isZero(r) ? this.double() : JacobianPoint.ZERO;
|
|
275
|
+
const HH = Fp.square(H); // HH = H2
|
|
276
|
+
const HHH = Fp.multiply(H, HH); // HHH = H * HH
|
|
277
|
+
const V = Fp.multiply(U1, HH); // V = U1 * HH
|
|
278
|
+
const X3 = Fp.subtract(Fp.subtractN(Fp.squareN(r), HHH), Fp.multiplyN(V, _2n)); // X3 = r^2 - HHH - 2 * V;
|
|
279
|
+
const Y3 = Fp.subtract(Fp.multiplyN(r, Fp.subtractN(V, X3)), Fp.multiplyN(S1, HHH)); // Y3 = r * (V - X3) - S1 * HHH;
|
|
280
|
+
const Z3 = Fp.multiply(Fp.multiply(Z1, Z2), H); // Z3 = Z1 * Z2 * H;
|
|
382
281
|
return new JacobianPoint(X3, Y3, Z3);
|
|
383
282
|
}
|
|
384
283
|
subtract(other) {
|
|
@@ -417,7 +316,7 @@ export function weierstrass(curveDef) {
|
|
|
417
316
|
k1p = k1p.negate();
|
|
418
317
|
if (k2neg)
|
|
419
318
|
k2p = k2p.negate();
|
|
420
|
-
k2p = new JacobianPoint(
|
|
319
|
+
k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
|
|
421
320
|
return k1p.add(k2p);
|
|
422
321
|
}
|
|
423
322
|
/**
|
|
@@ -458,7 +357,7 @@ export function weierstrass(curveDef) {
|
|
|
458
357
|
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
|
|
459
358
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
|
460
359
|
k2p = wnaf.constTimeNegate(k2neg, k2p);
|
|
461
|
-
k2p = new JacobianPoint(
|
|
360
|
+
k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
|
|
462
361
|
point = k1p.add(k2p);
|
|
463
362
|
fake = f1p.add(f2p);
|
|
464
363
|
}
|
|
@@ -478,25 +377,42 @@ export function weierstrass(curveDef) {
|
|
|
478
377
|
const { x, y, z } = this;
|
|
479
378
|
const is0 = this.equals(JacobianPoint.ZERO);
|
|
480
379
|
// 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,
|
|
380
|
+
// all operations, so we replace invZ with a random number, 1.
|
|
482
381
|
if (invZ == null)
|
|
483
|
-
invZ = is0 ?
|
|
382
|
+
invZ = is0 ? Fp.ONE : Fp.invert(z);
|
|
484
383
|
const iz1 = invZ;
|
|
485
|
-
const iz2 =
|
|
486
|
-
const iz3 =
|
|
487
|
-
const ax =
|
|
488
|
-
const ay =
|
|
489
|
-
const zz =
|
|
384
|
+
const iz2 = Fp.square(iz1); // iz1 * iz1
|
|
385
|
+
const iz3 = Fp.multiply(iz2, iz1); // iz2 * iz1
|
|
386
|
+
const ax = Fp.multiply(x, iz2); // x * iz2
|
|
387
|
+
const ay = Fp.multiply(y, iz3); // y * iz3
|
|
388
|
+
const zz = Fp.multiply(z, iz1); // z * iz1
|
|
490
389
|
if (is0)
|
|
491
390
|
return Point.ZERO;
|
|
492
|
-
if (zz
|
|
391
|
+
if (!Fp.equals(zz, Fp.ONE))
|
|
493
392
|
throw new Error('invZ was invalid');
|
|
494
393
|
return new Point(ax, ay);
|
|
495
394
|
}
|
|
395
|
+
isTorsionFree() {
|
|
396
|
+
if (CURVE.h === _1n)
|
|
397
|
+
return true; // No subgroups, always torsion fee
|
|
398
|
+
if (CURVE.isTorsionFree)
|
|
399
|
+
return CURVE.isTorsionFree(JacobianPoint, this);
|
|
400
|
+
// is multiplyUnsafe(CURVE.n) is always ok, same as for edwards?
|
|
401
|
+
throw new Error('Unsupported!');
|
|
402
|
+
}
|
|
403
|
+
// Clear cofactor of G1
|
|
404
|
+
// https://eprint.iacr.org/2019/403
|
|
405
|
+
clearCofactor() {
|
|
406
|
+
if (CURVE.h === _1n)
|
|
407
|
+
return this; // Fast-path
|
|
408
|
+
if (CURVE.clearCofactor)
|
|
409
|
+
return CURVE.clearCofactor(JacobianPoint, this);
|
|
410
|
+
return this.multiplyUnsafe(CURVE.h);
|
|
411
|
+
}
|
|
496
412
|
}
|
|
497
|
-
JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy,
|
|
498
|
-
JacobianPoint.ZERO = new JacobianPoint(
|
|
499
|
-
const wnaf = wNAF(JacobianPoint, CURVE.endo ?
|
|
413
|
+
JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
414
|
+
JacobianPoint.ZERO = new JacobianPoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|
415
|
+
const wnaf = wNAF(JacobianPoint, CURVE.endo ? nBitLength / 2 : nBitLength);
|
|
500
416
|
// Stores precomputed values for points.
|
|
501
417
|
const pointPrecomputes = new WeakMap();
|
|
502
418
|
/**
|
|
@@ -514,87 +430,60 @@ export function weierstrass(curveDef) {
|
|
|
514
430
|
}
|
|
515
431
|
// Checks for y % 2 == 0
|
|
516
432
|
hasEvenY() {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
* Supports compressed ECDSA points
|
|
521
|
-
* @returns Point instance
|
|
522
|
-
*/
|
|
523
|
-
static fromCompressedHex(bytes) {
|
|
524
|
-
const P = CURVE.P;
|
|
525
|
-
const x = bytesToNumberBE(bytes.subarray(1));
|
|
526
|
-
if (!isValidFieldElement(x))
|
|
527
|
-
throw new Error('Point is not on curve');
|
|
528
|
-
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
529
|
-
let y = sqrtModCurve(y2, P); // y = y² ^ (p+1)/4
|
|
530
|
-
const isYOdd = (y & _1n) === _1n;
|
|
531
|
-
// ECDSA
|
|
532
|
-
const isFirstByteOdd = (bytes[0] & 1) === 1;
|
|
533
|
-
if (isFirstByteOdd !== isYOdd)
|
|
534
|
-
y = modP(-y);
|
|
535
|
-
const point = new Point(x, y);
|
|
536
|
-
point.assertValidity();
|
|
537
|
-
return point;
|
|
538
|
-
}
|
|
539
|
-
static fromUncompressedHex(bytes) {
|
|
540
|
-
const x = bytesToNumberBE(bytes.subarray(1, fieldLen + 1));
|
|
541
|
-
const y = bytesToNumberBE(bytes.subarray(fieldLen + 1, 2 * fieldLen + 1));
|
|
542
|
-
const point = new Point(x, y);
|
|
543
|
-
point.assertValidity();
|
|
544
|
-
return point;
|
|
433
|
+
if (Fp.isOdd)
|
|
434
|
+
return !Fp.isOdd(this.y);
|
|
435
|
+
throw new Error("Field doesn't support isOdd");
|
|
545
436
|
}
|
|
546
437
|
/**
|
|
547
438
|
* Converts hash string or Uint8Array to Point.
|
|
548
439
|
* @param hex short/long ECDSA hex
|
|
549
440
|
*/
|
|
550
441
|
static fromHex(hex) {
|
|
551
|
-
const
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (len === compressedLen && (header === 0x02 || header === 0x03))
|
|
556
|
-
return this.fromCompressedHex(bytes);
|
|
557
|
-
if (len === uncompressedLen && header === 0x04)
|
|
558
|
-
return this.fromUncompressedHex(bytes);
|
|
559
|
-
throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`);
|
|
442
|
+
const { x, y } = CURVE.fromBytes(ensureBytes(hex));
|
|
443
|
+
const point = new Point(x, y);
|
|
444
|
+
point.assertValidity();
|
|
445
|
+
return point;
|
|
560
446
|
}
|
|
561
447
|
// Multiplies generator point by privateKey.
|
|
562
448
|
static fromPrivateKey(privateKey) {
|
|
563
449
|
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
564
450
|
}
|
|
565
451
|
toRawBytes(isCompressed = false) {
|
|
566
|
-
|
|
452
|
+
this.assertValidity();
|
|
453
|
+
return CURVE.toBytes(Point, this, isCompressed);
|
|
567
454
|
}
|
|
568
455
|
toHex(isCompressed = false) {
|
|
569
|
-
|
|
570
|
-
if (isCompressed) {
|
|
571
|
-
const prefix = this.hasEvenY() ? '02' : '03';
|
|
572
|
-
return `${prefix}${x}`;
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
return `04${x}${numToFieldStr(this.y)}`;
|
|
576
|
-
}
|
|
456
|
+
return bytesToHex(this.toRawBytes(isCompressed));
|
|
577
457
|
}
|
|
578
458
|
// A point on curve is valid if it conforms to equation.
|
|
579
459
|
assertValidity() {
|
|
460
|
+
// Zero is valid point too!
|
|
461
|
+
if (this.equals(Point.ZERO)) {
|
|
462
|
+
if (CURVE.allowInfinityPoint)
|
|
463
|
+
return;
|
|
464
|
+
throw new Error('Point is infinity');
|
|
465
|
+
}
|
|
580
466
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
|
581
467
|
const msg = 'Point is not on elliptic curve';
|
|
582
468
|
const { x, y } = this;
|
|
583
|
-
if (!
|
|
469
|
+
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|
584
470
|
throw new Error(msg);
|
|
585
|
-
const left =
|
|
471
|
+
const left = Fp.square(y);
|
|
586
472
|
const right = weierstrassEquation(x);
|
|
587
|
-
if (
|
|
473
|
+
if (!Fp.equals(left, right))
|
|
588
474
|
throw new Error(msg);
|
|
475
|
+
// TODO: flag to disable this?
|
|
476
|
+
if (!this.isTorsionFree())
|
|
477
|
+
throw new Error('Point must be of prime-order subgroup');
|
|
589
478
|
}
|
|
590
479
|
equals(other) {
|
|
591
480
|
if (!(other instanceof Point))
|
|
592
481
|
throw new TypeError('Point#equals: expected Point');
|
|
593
|
-
return this.x
|
|
482
|
+
return Fp.equals(this.x, other.x) && Fp.equals(this.y, other.y);
|
|
594
483
|
}
|
|
595
484
|
// Returns the same point with inverted `y`
|
|
596
485
|
negate() {
|
|
597
|
-
return new Point(this.x,
|
|
486
|
+
return new Point(this.x, Fp.negate(this.y));
|
|
598
487
|
}
|
|
599
488
|
// Adds point to itself
|
|
600
489
|
double() {
|
|
@@ -611,6 +500,15 @@ export function weierstrass(curveDef) {
|
|
|
611
500
|
multiply(scalar) {
|
|
612
501
|
return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine();
|
|
613
502
|
}
|
|
503
|
+
multiplyUnsafe(scalar) {
|
|
504
|
+
return JacobianPoint.fromAffine(this).multiplyUnsafe(scalar).toAffine();
|
|
505
|
+
}
|
|
506
|
+
clearCofactor() {
|
|
507
|
+
return JacobianPoint.fromAffine(this).clearCofactor().toAffine();
|
|
508
|
+
}
|
|
509
|
+
isTorsionFree() {
|
|
510
|
+
return JacobianPoint.fromAffine(this).isTorsionFree();
|
|
511
|
+
}
|
|
614
512
|
/**
|
|
615
513
|
* Efficiently calculate `aP + bQ`.
|
|
616
514
|
* Unsafe, can expose private key, if used incorrectly.
|
|
@@ -624,6 +522,27 @@ export function weierstrass(curveDef) {
|
|
|
624
522
|
const sum = aP.add(bQ);
|
|
625
523
|
return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine();
|
|
626
524
|
}
|
|
525
|
+
// Encodes byte string to elliptic curve
|
|
526
|
+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
527
|
+
static hashToCurve(msg, options) {
|
|
528
|
+
if (!CURVE.mapToCurve)
|
|
529
|
+
throw new Error('No mapToCurve defined for curve');
|
|
530
|
+
msg = ensureBytes(msg);
|
|
531
|
+
const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options });
|
|
532
|
+
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]);
|
|
533
|
+
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]);
|
|
534
|
+
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
|
535
|
+
return p;
|
|
536
|
+
}
|
|
537
|
+
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
538
|
+
static encodeToCurve(msg, options) {
|
|
539
|
+
if (!CURVE.mapToCurve)
|
|
540
|
+
throw new Error('No mapToCurve defined for curve');
|
|
541
|
+
msg = ensureBytes(msg);
|
|
542
|
+
const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options });
|
|
543
|
+
const { x, y } = CURVE.mapToCurve(u[0]);
|
|
544
|
+
return new Point(x, y).clearCofactor();
|
|
545
|
+
}
|
|
627
546
|
}
|
|
628
547
|
/**
|
|
629
548
|
* Base point aka generator. public_key = Point.BASE * private_key
|
|
@@ -632,7 +551,167 @@ export function weierstrass(curveDef) {
|
|
|
632
551
|
/**
|
|
633
552
|
* Identity point aka point at infinity. point = point + zero_point
|
|
634
553
|
*/
|
|
635
|
-
Point.ZERO = new Point(
|
|
554
|
+
Point.ZERO = new Point(Fp.ZERO, Fp.ZERO);
|
|
555
|
+
return {
|
|
556
|
+
Point: Point,
|
|
557
|
+
JacobianPoint: JacobianPoint,
|
|
558
|
+
normalizePrivateKey,
|
|
559
|
+
weierstrassEquation,
|
|
560
|
+
isWithinCurveOrder,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function validateOpts(curve) {
|
|
564
|
+
const opts = utils.validateOpts(curve);
|
|
565
|
+
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
566
|
+
throw new Error('Invalid hash function');
|
|
567
|
+
if (typeof opts.hmac !== 'function')
|
|
568
|
+
throw new Error('Invalid hmac function');
|
|
569
|
+
if (typeof opts.randomBytes !== 'function')
|
|
570
|
+
throw new Error('Invalid randomBytes function');
|
|
571
|
+
// Set defaults
|
|
572
|
+
return Object.freeze({ lowS: true, ...opts });
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Minimal HMAC-DRBG (NIST 800-90) for signatures.
|
|
576
|
+
* Used only for RFC6979, does not fully implement DRBG spec.
|
|
577
|
+
*/
|
|
578
|
+
class HmacDrbg {
|
|
579
|
+
constructor(hashLen, qByteLen, hmacFn) {
|
|
580
|
+
this.hashLen = hashLen;
|
|
581
|
+
this.qByteLen = qByteLen;
|
|
582
|
+
this.hmacFn = hmacFn;
|
|
583
|
+
if (typeof hashLen !== 'number' || hashLen < 2)
|
|
584
|
+
throw new Error('hashLen must be a number');
|
|
585
|
+
if (typeof qByteLen !== 'number' || qByteLen < 2)
|
|
586
|
+
throw new Error('qByteLen must be a number');
|
|
587
|
+
if (typeof hmacFn !== 'function')
|
|
588
|
+
throw new Error('hmacFn must be a function');
|
|
589
|
+
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
590
|
+
this.v = new Uint8Array(hashLen).fill(1);
|
|
591
|
+
this.k = new Uint8Array(hashLen).fill(0);
|
|
592
|
+
this.counter = 0;
|
|
593
|
+
}
|
|
594
|
+
hmacSync(...values) {
|
|
595
|
+
return this.hmacFn(this.k, ...values);
|
|
596
|
+
}
|
|
597
|
+
incr() {
|
|
598
|
+
if (this.counter >= 1000)
|
|
599
|
+
throw new Error('Tried 1,000 k values for sign(), all were invalid');
|
|
600
|
+
this.counter += 1;
|
|
601
|
+
}
|
|
602
|
+
reseedSync(seed = new Uint8Array()) {
|
|
603
|
+
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed);
|
|
604
|
+
this.v = this.hmacSync(this.v);
|
|
605
|
+
if (seed.length === 0)
|
|
606
|
+
return;
|
|
607
|
+
this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed);
|
|
608
|
+
this.v = this.hmacSync(this.v);
|
|
609
|
+
}
|
|
610
|
+
// TODO: review
|
|
611
|
+
generateSync() {
|
|
612
|
+
this.incr();
|
|
613
|
+
let len = 0;
|
|
614
|
+
const out = [];
|
|
615
|
+
while (len < this.qByteLen) {
|
|
616
|
+
this.v = this.hmacSync(this.v);
|
|
617
|
+
const sl = this.v.slice();
|
|
618
|
+
out.push(sl);
|
|
619
|
+
len += this.v.length;
|
|
620
|
+
}
|
|
621
|
+
return concatBytes(...out);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
export function weierstrass(curveDef) {
|
|
625
|
+
const CURVE = validateOpts(curveDef);
|
|
626
|
+
const CURVE_ORDER = CURVE.n;
|
|
627
|
+
const Fp = CURVE.Fp;
|
|
628
|
+
const compressedLen = Fp.BYTES + 1; // 33
|
|
629
|
+
const uncompressedLen = 2 * Fp.BYTES + 1; // 65
|
|
630
|
+
function isValidFieldElement(num) {
|
|
631
|
+
// 0 is disallowed by arbitrary reasons. Probably because infinity point?
|
|
632
|
+
return _0n < num && num < Fp.ORDER;
|
|
633
|
+
}
|
|
634
|
+
const { Point, JacobianPoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({
|
|
635
|
+
...CURVE,
|
|
636
|
+
toBytes(c, point, isCompressed) {
|
|
637
|
+
if (isCompressed) {
|
|
638
|
+
return concatBytes(new Uint8Array([point.hasEvenY() ? 0x02 : 0x03]), Fp.toBytes(point.x));
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
return concatBytes(new Uint8Array([0x04]), Fp.toBytes(point.x), Fp.toBytes(point.y));
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
fromBytes(bytes) {
|
|
645
|
+
const len = bytes.length;
|
|
646
|
+
const header = bytes[0];
|
|
647
|
+
// this.assertValidity() is done inside of fromHex
|
|
648
|
+
if (len === compressedLen && (header === 0x02 || header === 0x03)) {
|
|
649
|
+
const x = bytesToNumberBE(bytes.subarray(1));
|
|
650
|
+
if (!isValidFieldElement(x))
|
|
651
|
+
throw new Error('Point is not on curve');
|
|
652
|
+
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
|
653
|
+
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
|
654
|
+
const isYOdd = (y & _1n) === _1n;
|
|
655
|
+
// ECDSA
|
|
656
|
+
const isFirstByteOdd = (bytes[0] & 1) === 1;
|
|
657
|
+
if (isFirstByteOdd !== isYOdd)
|
|
658
|
+
y = Fp.negate(y);
|
|
659
|
+
return { x, y };
|
|
660
|
+
}
|
|
661
|
+
else if (len === uncompressedLen && header === 0x04) {
|
|
662
|
+
const x = Fp.fromBytes(bytes.subarray(1, Fp.BYTES + 1));
|
|
663
|
+
const y = Fp.fromBytes(bytes.subarray(Fp.BYTES + 1, 2 * Fp.BYTES + 1));
|
|
664
|
+
return { x, y };
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`);
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
});
|
|
671
|
+
// Do we need these functions at all?
|
|
672
|
+
function numToField(num) {
|
|
673
|
+
if (typeof num !== 'bigint')
|
|
674
|
+
throw new Error('Expected bigint');
|
|
675
|
+
if (!(_0n <= num && num < Fp.MASK))
|
|
676
|
+
throw new Error(`Expected number < 2^${Fp.BYTES * 8}`);
|
|
677
|
+
return Fp.toBytes(num);
|
|
678
|
+
}
|
|
679
|
+
const numToFieldStr = (num) => bytesToHex(numToField(num));
|
|
680
|
+
/**
|
|
681
|
+
* Normalizes hex, bytes, Point to Point. Checks for curve equation.
|
|
682
|
+
*/
|
|
683
|
+
function normalizePublicKey(publicKey) {
|
|
684
|
+
if (publicKey instanceof Point) {
|
|
685
|
+
publicKey.assertValidity();
|
|
686
|
+
return publicKey;
|
|
687
|
+
}
|
|
688
|
+
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
|
689
|
+
return Point.fromHex(publicKey);
|
|
690
|
+
// This can happen because PointType can be instance of different class
|
|
691
|
+
}
|
|
692
|
+
else
|
|
693
|
+
throw new Error(`Unknown type of public key: ${publicKey}`);
|
|
694
|
+
}
|
|
695
|
+
function isBiggerThanHalfOrder(number) {
|
|
696
|
+
const HALF = CURVE_ORDER >> _1n;
|
|
697
|
+
return number > HALF;
|
|
698
|
+
}
|
|
699
|
+
function normalizeS(s) {
|
|
700
|
+
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
|
|
701
|
+
}
|
|
702
|
+
// Ensures ECDSA message hashes are 32 bytes and < curve order
|
|
703
|
+
function _truncateHash(hash, truncateOnly = false) {
|
|
704
|
+
const { n, nBitLength } = CURVE;
|
|
705
|
+
const byteLength = hash.length;
|
|
706
|
+
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
|
707
|
+
let h = bytesToNumberBE(hash);
|
|
708
|
+
if (delta > 0)
|
|
709
|
+
h = h >> BigInt(delta);
|
|
710
|
+
if (!truncateOnly && h >= n)
|
|
711
|
+
h -= n;
|
|
712
|
+
return h;
|
|
713
|
+
}
|
|
714
|
+
const truncateHash = CURVE.truncateHash || _truncateHash;
|
|
636
715
|
/**
|
|
637
716
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
|
638
717
|
*/
|
|
@@ -743,8 +822,8 @@ export function weierstrass(curveDef) {
|
|
|
743
822
|
}
|
|
744
823
|
}
|
|
745
824
|
const utils = {
|
|
746
|
-
mod:
|
|
747
|
-
invert:
|
|
825
|
+
mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo),
|
|
826
|
+
invert: Fp.invert,
|
|
748
827
|
isValidPrivateKey(privateKey) {
|
|
749
828
|
try {
|
|
750
829
|
normalizePrivateKey(privateKey);
|
|
@@ -769,7 +848,7 @@ export function weierstrass(curveDef) {
|
|
|
769
848
|
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
|
770
849
|
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
|
771
850
|
*/
|
|
772
|
-
randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(
|
|
851
|
+
randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(Fp.BYTES + 8)),
|
|
773
852
|
/**
|
|
774
853
|
* 1. Returns cached point which you can use to pass to `getSharedSecret` or `#multiply` by it.
|
|
775
854
|
* 2. Precomputes point multiplication table. Is done by default on first `getPublicKey()` call.
|
|
@@ -829,7 +908,7 @@ export function weierstrass(curveDef) {
|
|
|
829
908
|
}
|
|
830
909
|
// RFC6979 methods
|
|
831
910
|
function bits2int(bytes) {
|
|
832
|
-
const slice = bytes.length >
|
|
911
|
+
const slice = bytes.length > Fp.BYTES ? bytes.slice(0, Fp.BYTES) : bytes;
|
|
833
912
|
return bytesToNumberBE(slice);
|
|
834
913
|
}
|
|
835
914
|
function bits2octets(bytes) {
|
|
@@ -853,10 +932,10 @@ export function weierstrass(curveDef) {
|
|
|
853
932
|
// RFC6979 3.6: additional k' could be provided
|
|
854
933
|
if (extraEntropy != null) {
|
|
855
934
|
if (extraEntropy === true)
|
|
856
|
-
extraEntropy = CURVE.randomBytes(
|
|
935
|
+
extraEntropy = CURVE.randomBytes(Fp.BYTES);
|
|
857
936
|
const e = ensureBytes(extraEntropy);
|
|
858
|
-
if (e.length !==
|
|
859
|
-
throw new Error(`sign: Expected ${
|
|
937
|
+
if (e.length !== Fp.BYTES)
|
|
938
|
+
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
|
|
860
939
|
seedArgs.push(e);
|
|
861
940
|
}
|
|
862
941
|
// seed is constructed from private key and message
|