@noble/curves 0.1.0 → 0.2.1
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 +339 -20
- package/lib/edwards.d.ts +108 -0
- package/lib/edwards.js +554 -0
- package/lib/esm/edwards.js +550 -0
- package/lib/esm/group.js +107 -0
- package/lib/esm/modular.js +19 -1
- package/lib/esm/montgomery.js +189 -0
- package/lib/esm/utils.js +62 -5
- package/lib/esm/{shortw.js → weierstrass.js} +81 -159
- package/lib/group.d.ts +33 -0
- package/lib/group.js +111 -0
- package/lib/modular.d.ts +7 -1
- package/lib/modular.js +23 -3
- package/lib/montgomery.d.ts +20 -0
- package/lib/montgomery.js +191 -0
- package/lib/utils.d.ts +26 -2
- package/lib/utils.js +71 -7
- package/lib/{shortw.d.ts → weierstrass.d.ts} +22 -35
- package/lib/{shortw.js → weierstrass.js} +80 -158
- package/package.json +23 -11
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
+
// Implementation of Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
|
3
|
+
// Differences from @noble/ed25519 1.7:
|
|
4
|
+
// 1. Different field element lengths in ed448:
|
|
5
|
+
// EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
|
|
6
|
+
// 2. Different addition formula (doubling is same)
|
|
7
|
+
// 3. uvRatio differs between curves (half-expected, not only pow fn changes)
|
|
8
|
+
// 4. Point decompression code is different too (unexpected), now using generalized formula
|
|
9
|
+
// 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
|
|
10
|
+
import * as mod from './modular.js';
|
|
11
|
+
import { bytesToHex, concatBytes, ensureBytes, numberToBytesLE, bytesToNumberLE, hashToPrivateScalar, validateOpts as utilOpts, } from './utils.js'; // TODO: import * as u from './utils.js'?
|
|
12
|
+
import { wNAF } from './group.js';
|
|
13
|
+
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
14
|
+
const _0n = BigInt(0);
|
|
15
|
+
const _1n = BigInt(1);
|
|
16
|
+
const _2n = BigInt(2);
|
|
17
|
+
const _8n = BigInt(8);
|
|
18
|
+
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
|
|
19
|
+
function validateOpts(curve) {
|
|
20
|
+
const opts = utilOpts(curve);
|
|
21
|
+
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
22
|
+
throw new Error('Invalid hash function');
|
|
23
|
+
for (const i of ['a', 'd']) {
|
|
24
|
+
if (typeof opts[i] !== 'bigint')
|
|
25
|
+
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
|
26
|
+
}
|
|
27
|
+
for (const fn of ['randomBytes']) {
|
|
28
|
+
if (typeof opts[fn] !== 'function')
|
|
29
|
+
throw new Error(`Invalid ${fn} function`);
|
|
30
|
+
}
|
|
31
|
+
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio']) {
|
|
32
|
+
if (opts[fn] === undefined)
|
|
33
|
+
continue; // Optional
|
|
34
|
+
if (typeof opts[fn] !== 'function')
|
|
35
|
+
throw new Error(`Invalid ${fn} function`);
|
|
36
|
+
}
|
|
37
|
+
// Set defaults
|
|
38
|
+
return Object.freeze({ ...opts });
|
|
39
|
+
}
|
|
40
|
+
// NOTE: it is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
|
41
|
+
export function twistedEdwards(curveDef) {
|
|
42
|
+
const CURVE = validateOpts(curveDef);
|
|
43
|
+
const CURVE_ORDER = CURVE.n;
|
|
44
|
+
const fieldLen = CURVE.nByteLength; // 32 (length of one field element)
|
|
45
|
+
if (fieldLen > 2048)
|
|
46
|
+
throw new Error('Field lengths over 2048 are not supported');
|
|
47
|
+
const groupLen = CURVE.nByteLength;
|
|
48
|
+
// (2n ** 256n).toString(16);
|
|
49
|
+
const maxGroupElement = _2n ** BigInt(groupLen * 8); // previous POW_2_256
|
|
50
|
+
// Function overrides
|
|
51
|
+
const { P, randomBytes } = CURVE;
|
|
52
|
+
const modP = (a) => mod.mod(a, P);
|
|
53
|
+
// sqrt(u/v)
|
|
54
|
+
function _uvRatio(u, v) {
|
|
55
|
+
try {
|
|
56
|
+
const value = mod.sqrt(u * mod.invert(v, P), P);
|
|
57
|
+
return { isValid: true, value };
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return { isValid: false, value: _0n };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const uvRatio = CURVE.uvRatio || _uvRatio;
|
|
64
|
+
const _adjustScalarBytes = (bytes) => bytes; // NOOP
|
|
65
|
+
const adjustScalarBytes = CURVE.adjustScalarBytes || _adjustScalarBytes;
|
|
66
|
+
function _domain(data, ctx, phflag) {
|
|
67
|
+
if (ctx.length || phflag)
|
|
68
|
+
throw new Error('Contexts/pre-hash are not supported');
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
const domain = CURVE.domain || _domain; // NOOP
|
|
72
|
+
/**
|
|
73
|
+
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
|
74
|
+
* Default Point works in affine coordinates: (x, y)
|
|
75
|
+
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
|
76
|
+
*/
|
|
77
|
+
class ExtendedPoint {
|
|
78
|
+
constructor(x, y, z, t) {
|
|
79
|
+
this.x = x;
|
|
80
|
+
this.y = y;
|
|
81
|
+
this.z = z;
|
|
82
|
+
this.t = t;
|
|
83
|
+
}
|
|
84
|
+
static fromAffine(p) {
|
|
85
|
+
if (!(p instanceof Point)) {
|
|
86
|
+
throw new TypeError('ExtendedPoint#fromAffine: expected Point');
|
|
87
|
+
}
|
|
88
|
+
if (p.equals(Point.ZERO))
|
|
89
|
+
return ExtendedPoint.ZERO;
|
|
90
|
+
return new ExtendedPoint(p.x, p.y, _1n, modP(p.x * p.y));
|
|
91
|
+
}
|
|
92
|
+
// Takes a bunch of Jacobian Points but executes only one
|
|
93
|
+
// invert on all of them. invert is very slow operation,
|
|
94
|
+
// so this improves performance massively.
|
|
95
|
+
static toAffineBatch(points) {
|
|
96
|
+
const toInv = mod.invertBatch(points.map((p) => p.z), P);
|
|
97
|
+
return points.map((p, i) => p.toAffine(toInv[i]));
|
|
98
|
+
}
|
|
99
|
+
static normalizeZ(points) {
|
|
100
|
+
return this.toAffineBatch(points).map(this.fromAffine);
|
|
101
|
+
}
|
|
102
|
+
// Compare one point to another.
|
|
103
|
+
equals(other) {
|
|
104
|
+
assertExtPoint(other);
|
|
105
|
+
const { x: X1, y: Y1, z: Z1 } = this;
|
|
106
|
+
const { x: X2, y: Y2, z: Z2 } = other;
|
|
107
|
+
const X1Z2 = modP(X1 * Z2);
|
|
108
|
+
const X2Z1 = modP(X2 * Z1);
|
|
109
|
+
const Y1Z2 = modP(Y1 * Z2);
|
|
110
|
+
const Y2Z1 = modP(Y2 * Z1);
|
|
111
|
+
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
112
|
+
}
|
|
113
|
+
// Inverses point to one corresponding to (x, -y) in Affine coordinates.
|
|
114
|
+
negate() {
|
|
115
|
+
return new ExtendedPoint(modP(-this.x), this.y, this.z, modP(-this.t));
|
|
116
|
+
}
|
|
117
|
+
// Fast algo for doubling Extended Point.
|
|
118
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
|
119
|
+
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
|
120
|
+
double() {
|
|
121
|
+
const { a } = CURVE;
|
|
122
|
+
const { x: X1, y: Y1, z: Z1 } = this;
|
|
123
|
+
const A = modP(X1 * X1); // A = X12
|
|
124
|
+
const B = modP(Y1 * Y1); // B = Y12
|
|
125
|
+
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
|
|
126
|
+
const D = modP(a * A); // D = a*A
|
|
127
|
+
const x1y1 = X1 + Y1;
|
|
128
|
+
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
|
|
129
|
+
const G = D + B; // G = D+B
|
|
130
|
+
const F = G - C; // F = G-C
|
|
131
|
+
const H = D - B; // H = D-B
|
|
132
|
+
const X3 = modP(E * F); // X3 = E*F
|
|
133
|
+
const Y3 = modP(G * H); // Y3 = G*H
|
|
134
|
+
const T3 = modP(E * H); // T3 = E*H
|
|
135
|
+
const Z3 = modP(F * G); // Z3 = F*G
|
|
136
|
+
return new ExtendedPoint(X3, Y3, Z3, T3);
|
|
137
|
+
}
|
|
138
|
+
// Fast algo for adding 2 Extended Points.
|
|
139
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
|
140
|
+
// Cost: 9M + 1*a + 1*d + 7add.
|
|
141
|
+
add(other) {
|
|
142
|
+
assertExtPoint(other);
|
|
143
|
+
const { a, d } = CURVE;
|
|
144
|
+
const { x: X1, y: Y1, z: Z1, t: T1 } = this;
|
|
145
|
+
const { x: X2, y: Y2, z: Z2, t: T2 } = other;
|
|
146
|
+
// Faster algo for adding 2 Extended Points when curve's a=-1.
|
|
147
|
+
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
|
|
148
|
+
// Cost: 8M + 8add + 2*2.
|
|
149
|
+
// Note: It does not check whether the `other` point is valid.
|
|
150
|
+
if (a === BigInt(-1)) {
|
|
151
|
+
const A = modP((Y1 - X1) * (Y2 + X2));
|
|
152
|
+
const B = modP((Y1 + X1) * (Y2 - X2));
|
|
153
|
+
const F = modP(B - A);
|
|
154
|
+
if (F === _0n)
|
|
155
|
+
return this.double(); // Same point.
|
|
156
|
+
const C = modP(Z1 * _2n * T2);
|
|
157
|
+
const D = modP(T1 * _2n * Z2);
|
|
158
|
+
const E = D + C;
|
|
159
|
+
const G = B + A;
|
|
160
|
+
const H = D - C;
|
|
161
|
+
const X3 = modP(E * F);
|
|
162
|
+
const Y3 = modP(G * H);
|
|
163
|
+
const T3 = modP(E * H);
|
|
164
|
+
const Z3 = modP(F * G);
|
|
165
|
+
return new ExtendedPoint(X3, Y3, Z3, T3);
|
|
166
|
+
}
|
|
167
|
+
const A = modP(X1 * X2); // A = X1*X2
|
|
168
|
+
const B = modP(Y1 * Y2); // B = Y1*Y2
|
|
169
|
+
const C = modP(T1 * d * T2); // C = T1*d*T2
|
|
170
|
+
const D = modP(Z1 * Z2); // D = Z1*Z2
|
|
171
|
+
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
|
|
172
|
+
// TODO: do we need to check for same point here? Looks like working without it
|
|
173
|
+
const F = D - C; // F = D-C
|
|
174
|
+
const G = D + C; // G = D+C
|
|
175
|
+
const H = modP(B - a * A); // H = B-a*A
|
|
176
|
+
const X3 = modP(E * F); // X3 = E*F
|
|
177
|
+
const Y3 = modP(G * H); // Y3 = G*H
|
|
178
|
+
const T3 = modP(E * H); // T3 = E*H
|
|
179
|
+
const Z3 = modP(F * G); // Z3 = F*G
|
|
180
|
+
return new ExtendedPoint(X3, Y3, Z3, T3);
|
|
181
|
+
}
|
|
182
|
+
subtract(other) {
|
|
183
|
+
return this.add(other.negate());
|
|
184
|
+
}
|
|
185
|
+
wNAF(n, affinePoint) {
|
|
186
|
+
if (!affinePoint && this.equals(ExtendedPoint.BASE))
|
|
187
|
+
affinePoint = Point.BASE;
|
|
188
|
+
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
|
189
|
+
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
|
190
|
+
if (!precomputes) {
|
|
191
|
+
precomputes = wnaf.precomputeWindow(this, W);
|
|
192
|
+
if (affinePoint && W !== 1) {
|
|
193
|
+
precomputes = ExtendedPoint.normalizeZ(precomputes);
|
|
194
|
+
pointPrecomputes.set(affinePoint, precomputes);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const { p, f } = wnaf.wNAF(W, precomputes, n);
|
|
198
|
+
return ExtendedPoint.normalizeZ([p, f])[0];
|
|
199
|
+
}
|
|
200
|
+
// Constant time multiplication.
|
|
201
|
+
// Uses wNAF method. Windowed method may be 10% faster,
|
|
202
|
+
// but takes 2x longer to generate and consumes 2x memory.
|
|
203
|
+
multiply(scalar, affinePoint) {
|
|
204
|
+
return this.wNAF(normalizeScalar(scalar, CURVE_ORDER), affinePoint);
|
|
205
|
+
}
|
|
206
|
+
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
207
|
+
// It's faster, but should only be used when you don't care about
|
|
208
|
+
// an exposed private key e.g. sig verification.
|
|
209
|
+
// Allows scalar bigger than curve order, but less than 2^256
|
|
210
|
+
multiplyUnsafe(scalar) {
|
|
211
|
+
let n = normalizeScalar(scalar, CURVE_ORDER, false);
|
|
212
|
+
const G = ExtendedPoint.BASE;
|
|
213
|
+
const P0 = ExtendedPoint.ZERO;
|
|
214
|
+
if (n === _0n)
|
|
215
|
+
return P0;
|
|
216
|
+
if (this.equals(P0) || n === _1n)
|
|
217
|
+
return this;
|
|
218
|
+
if (this.equals(G))
|
|
219
|
+
return this.wNAF(n);
|
|
220
|
+
return wnaf.unsafeLadder(this, n);
|
|
221
|
+
}
|
|
222
|
+
// Multiplies point by cofactor and checks if the result is 0.
|
|
223
|
+
isSmallOrder() {
|
|
224
|
+
return this.multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
|
|
225
|
+
}
|
|
226
|
+
// Multiplies point by a very big scalar n and checks if the result is 0.
|
|
227
|
+
isTorsionFree() {
|
|
228
|
+
return this.multiplyUnsafe(CURVE_ORDER).equals(ExtendedPoint.ZERO);
|
|
229
|
+
}
|
|
230
|
+
// Converts Extended point to default (x, y) coordinates.
|
|
231
|
+
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
232
|
+
toAffine(invZ) {
|
|
233
|
+
const { x, y, z } = this;
|
|
234
|
+
const is0 = this.equals(ExtendedPoint.ZERO);
|
|
235
|
+
if (invZ == null)
|
|
236
|
+
invZ = is0 ? _8n : mod.invert(z, P); // 8 was chosen arbitrarily
|
|
237
|
+
const ax = modP(x * invZ);
|
|
238
|
+
const ay = modP(y * invZ);
|
|
239
|
+
const zz = modP(z * invZ);
|
|
240
|
+
if (is0)
|
|
241
|
+
return Point.ZERO;
|
|
242
|
+
if (zz !== _1n)
|
|
243
|
+
throw new Error('invZ was invalid');
|
|
244
|
+
return new Point(ax, ay);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
ExtendedPoint.BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
248
|
+
ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
|
|
249
|
+
const wnaf = wNAF(ExtendedPoint, groupLen * 8);
|
|
250
|
+
function assertExtPoint(other) {
|
|
251
|
+
if (!(other instanceof ExtendedPoint))
|
|
252
|
+
throw new TypeError('ExtendedPoint expected');
|
|
253
|
+
}
|
|
254
|
+
// Stores precomputed values for points.
|
|
255
|
+
const pointPrecomputes = new WeakMap();
|
|
256
|
+
/**
|
|
257
|
+
* Default Point works in affine coordinates: (x, y)
|
|
258
|
+
*/
|
|
259
|
+
class Point {
|
|
260
|
+
constructor(x, y) {
|
|
261
|
+
this.x = x;
|
|
262
|
+
this.y = y;
|
|
263
|
+
}
|
|
264
|
+
// "Private method", don't use it directly.
|
|
265
|
+
_setWindowSize(windowSize) {
|
|
266
|
+
this._WINDOW_SIZE = windowSize;
|
|
267
|
+
pointPrecomputes.delete(this);
|
|
268
|
+
}
|
|
269
|
+
// Converts hash string or Uint8Array to Point.
|
|
270
|
+
// Uses algo from RFC8032 5.1.3.
|
|
271
|
+
static fromHex(hex, strict = true) {
|
|
272
|
+
const { d, P, a } = CURVE;
|
|
273
|
+
hex = ensureBytes(hex, fieldLen);
|
|
274
|
+
// 1. First, interpret the string as an integer in little-endian
|
|
275
|
+
// representation. Bit 255 of this number is the least significant
|
|
276
|
+
// bit of the x-coordinate and denote this value x_0. The
|
|
277
|
+
// y-coordinate is recovered simply by clearing this bit. If the
|
|
278
|
+
// resulting value is >= p, decoding fails.
|
|
279
|
+
const normed = hex.slice();
|
|
280
|
+
const lastByte = hex[fieldLen - 1];
|
|
281
|
+
normed[fieldLen - 1] = lastByte & ~0x80;
|
|
282
|
+
const y = bytesToNumberLE(normed);
|
|
283
|
+
if (strict && y >= P)
|
|
284
|
+
throw new Error('Expected 0 < hex < P');
|
|
285
|
+
if (!strict && y >= maxGroupElement)
|
|
286
|
+
throw new Error('Expected 0 < hex < 2**256');
|
|
287
|
+
// 2. To recover the x-coordinate, the curve equation implies
|
|
288
|
+
// Ed25519: x² = (y² - 1) / (d y² + 1) (mod p).
|
|
289
|
+
// Ed448: x² = (y² - 1) / (d y² - 1) (mod p).
|
|
290
|
+
// For generic case:
|
|
291
|
+
// a*x²+y²=1+d*x²*y²
|
|
292
|
+
// -> y²-1 = d*x²*y²-a*x²
|
|
293
|
+
// -> y²-1 = x² (d*y²-a)
|
|
294
|
+
// -> x² = (y²-1) / (d*y²-a)
|
|
295
|
+
// The denominator is always non-zero mod p. Let u = y² - 1 and v = d y² + 1.
|
|
296
|
+
const y2 = modP(y * y);
|
|
297
|
+
const u = modP(y2 - _1n);
|
|
298
|
+
const v = modP(d * y2 - a);
|
|
299
|
+
let { isValid, value: x } = uvRatio(u, v);
|
|
300
|
+
if (!isValid)
|
|
301
|
+
throw new Error('Point.fromHex: invalid y coordinate');
|
|
302
|
+
// 4. Finally, use the x_0 bit to select the right square root. If
|
|
303
|
+
// x = 0, and x_0 = 1, decoding fails. Otherwise, if x_0 != x mod
|
|
304
|
+
// 2, set x <-- p - x. Return the decoded point (x,y).
|
|
305
|
+
const isXOdd = (x & _1n) === _1n;
|
|
306
|
+
const isLastByteOdd = (lastByte & 0x80) !== 0;
|
|
307
|
+
if (isLastByteOdd !== isXOdd)
|
|
308
|
+
x = modP(-x);
|
|
309
|
+
return new Point(x, y);
|
|
310
|
+
}
|
|
311
|
+
static fromPrivateKey(privateKey) {
|
|
312
|
+
return getExtendedPublicKey(privateKey).point;
|
|
313
|
+
}
|
|
314
|
+
// There can always be only two x values (x, -x) for any y
|
|
315
|
+
// When compressing point, it's enough to only store its y coordinate
|
|
316
|
+
// and use the last byte to encode sign of x.
|
|
317
|
+
toRawBytes() {
|
|
318
|
+
const bytes = numberToBytesLE(this.y, fieldLen);
|
|
319
|
+
bytes[fieldLen - 1] |= this.x & _1n ? 0x80 : 0;
|
|
320
|
+
return bytes;
|
|
321
|
+
}
|
|
322
|
+
// Same as toRawBytes, but returns string.
|
|
323
|
+
toHex() {
|
|
324
|
+
return bytesToHex(this.toRawBytes());
|
|
325
|
+
}
|
|
326
|
+
isTorsionFree() {
|
|
327
|
+
return ExtendedPoint.fromAffine(this).isTorsionFree();
|
|
328
|
+
}
|
|
329
|
+
equals(other) {
|
|
330
|
+
if (!(other instanceof Point))
|
|
331
|
+
throw new TypeError('Point#equals: expected Point');
|
|
332
|
+
return this.x === other.x && this.y === other.y;
|
|
333
|
+
}
|
|
334
|
+
negate() {
|
|
335
|
+
return new Point(modP(-this.x), this.y);
|
|
336
|
+
}
|
|
337
|
+
double() {
|
|
338
|
+
return ExtendedPoint.fromAffine(this).double().toAffine();
|
|
339
|
+
}
|
|
340
|
+
add(other) {
|
|
341
|
+
return ExtendedPoint.fromAffine(this).add(ExtendedPoint.fromAffine(other)).toAffine();
|
|
342
|
+
}
|
|
343
|
+
subtract(other) {
|
|
344
|
+
return this.add(other.negate());
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Constant time multiplication.
|
|
348
|
+
* @param scalar Big-Endian number
|
|
349
|
+
* @returns new point
|
|
350
|
+
*/
|
|
351
|
+
multiply(scalar) {
|
|
352
|
+
return ExtendedPoint.fromAffine(this).multiply(scalar, this).toAffine();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Base point aka generator
|
|
356
|
+
// public_key = Point.BASE * private_key
|
|
357
|
+
Point.BASE = new Point(CURVE.Gx, CURVE.Gy);
|
|
358
|
+
// Identity point aka point at infinity
|
|
359
|
+
// point = point + zero_point
|
|
360
|
+
Point.ZERO = new Point(_0n, _1n);
|
|
361
|
+
/**
|
|
362
|
+
* EDDSA signature.
|
|
363
|
+
*/
|
|
364
|
+
class Signature {
|
|
365
|
+
constructor(r, s) {
|
|
366
|
+
this.r = r;
|
|
367
|
+
this.s = s;
|
|
368
|
+
this.assertValidity();
|
|
369
|
+
}
|
|
370
|
+
static fromHex(hex) {
|
|
371
|
+
const bytes = ensureBytes(hex, 2 * fieldLen);
|
|
372
|
+
const r = Point.fromHex(bytes.slice(0, fieldLen), false);
|
|
373
|
+
const s = bytesToNumberLE(bytes.slice(fieldLen, 2 * fieldLen));
|
|
374
|
+
return new Signature(r, s);
|
|
375
|
+
}
|
|
376
|
+
assertValidity() {
|
|
377
|
+
const { r, s } = this;
|
|
378
|
+
if (!(r instanceof Point))
|
|
379
|
+
throw new Error('Expected Point instance');
|
|
380
|
+
// 0 <= s < l
|
|
381
|
+
normalizeScalar(s, CURVE_ORDER, false);
|
|
382
|
+
return this;
|
|
383
|
+
}
|
|
384
|
+
toRawBytes() {
|
|
385
|
+
return concatBytes(this.r.toRawBytes(), numberToBytesLE(this.s, fieldLen));
|
|
386
|
+
}
|
|
387
|
+
toHex() {
|
|
388
|
+
return bytesToHex(this.toRawBytes());
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// Little-endian SHA512 with modulo n
|
|
392
|
+
function modlLE(hash) {
|
|
393
|
+
return mod.mod(bytesToNumberLE(hash), CURVE_ORDER);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Checks for num to be in range:
|
|
397
|
+
* For strict == true: `0 < num < max`.
|
|
398
|
+
* For strict == false: `0 <= num < max`.
|
|
399
|
+
* Converts non-float safe numbers to bigints.
|
|
400
|
+
*/
|
|
401
|
+
function normalizeScalar(num, max, strict = true) {
|
|
402
|
+
if (!max)
|
|
403
|
+
throw new TypeError('Specify max value');
|
|
404
|
+
if (typeof num === 'number' && Number.isSafeInteger(num))
|
|
405
|
+
num = BigInt(num);
|
|
406
|
+
if (typeof num === 'bigint' && num < max) {
|
|
407
|
+
if (strict) {
|
|
408
|
+
if (_0n < num)
|
|
409
|
+
return num;
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
if (_0n <= num)
|
|
413
|
+
return num;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
|
417
|
+
}
|
|
418
|
+
function checkPrivateKey(key) {
|
|
419
|
+
// Normalize bigint / number / string to Uint8Array
|
|
420
|
+
key =
|
|
421
|
+
typeof key === 'bigint' || typeof key === 'number'
|
|
422
|
+
? numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
|
|
423
|
+
: ensureBytes(key);
|
|
424
|
+
if (key.length !== groupLen)
|
|
425
|
+
throw new Error(`Expected ${groupLen} bytes, got ${key.length}`);
|
|
426
|
+
return key;
|
|
427
|
+
}
|
|
428
|
+
// Takes 64 bytes
|
|
429
|
+
function getKeyFromHash(hashed) {
|
|
430
|
+
// First 32 bytes of 64b uniformingly random input are taken,
|
|
431
|
+
// clears 3 bits of it to produce a random field element.
|
|
432
|
+
const head = adjustScalarBytes(hashed.slice(0, groupLen));
|
|
433
|
+
// Second 32 bytes is called key prefix (5.1.6)
|
|
434
|
+
const prefix = hashed.slice(groupLen, 2 * groupLen);
|
|
435
|
+
// The actual private scalar
|
|
436
|
+
const scalar = modlLE(head);
|
|
437
|
+
// Point on Edwards curve aka public key
|
|
438
|
+
const point = Point.BASE.multiply(scalar);
|
|
439
|
+
const pointBytes = point.toRawBytes();
|
|
440
|
+
return { head, prefix, scalar, point, pointBytes };
|
|
441
|
+
}
|
|
442
|
+
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
|
443
|
+
function getExtendedPublicKey(key) {
|
|
444
|
+
return getKeyFromHash(CURVE.hash(checkPrivateKey(key)));
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Calculates ed25519 public key. RFC8032 5.1.5
|
|
448
|
+
* 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
|
|
449
|
+
* 2. 3 least significant bits of the first byte are cleared
|
|
450
|
+
*/
|
|
451
|
+
function getPublicKey(privateKey) {
|
|
452
|
+
return getExtendedPublicKey(privateKey).pointBytes;
|
|
453
|
+
}
|
|
454
|
+
const EMPTY = new Uint8Array();
|
|
455
|
+
function hashDomainToScalar(message, context = EMPTY) {
|
|
456
|
+
context = ensureBytes(context);
|
|
457
|
+
return modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
|
|
458
|
+
}
|
|
459
|
+
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
460
|
+
function sign(message, privateKey, context) {
|
|
461
|
+
message = ensureBytes(message);
|
|
462
|
+
if (CURVE.preHash)
|
|
463
|
+
message = CURVE.preHash(message);
|
|
464
|
+
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
|
|
465
|
+
const r = hashDomainToScalar(concatBytes(prefix, message), context);
|
|
466
|
+
const R = Point.BASE.multiply(r); // R = rG
|
|
467
|
+
const k = hashDomainToScalar(concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
|
|
468
|
+
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
|
|
469
|
+
return new Signature(R, s).toRawBytes();
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Verifies EdDSA signature against message and public key.
|
|
473
|
+
* An extended group equation is checked.
|
|
474
|
+
* RFC8032 5.1.7
|
|
475
|
+
* Compliant with ZIP215:
|
|
476
|
+
* 0 <= sig.R/publicKey < 2**256 (can be >= curve.P)
|
|
477
|
+
* 0 <= sig.s < l
|
|
478
|
+
* Not compliant with RFC8032: it's not possible to comply to both ZIP & RFC at the same time.
|
|
479
|
+
*/
|
|
480
|
+
function verify(sig, message, publicKey, context) {
|
|
481
|
+
message = ensureBytes(message);
|
|
482
|
+
if (CURVE.preHash)
|
|
483
|
+
message = CURVE.preHash(message);
|
|
484
|
+
// When hex is passed, we check public key fully.
|
|
485
|
+
// When Point instance is passed, we assume it has already been checked, for performance.
|
|
486
|
+
// If user passes Point/Sig instance, we assume it has been already verified.
|
|
487
|
+
// We don't check its equations for performance. We do check for valid bounds for s though
|
|
488
|
+
// We always check for: a) s bounds. b) hex validity
|
|
489
|
+
if (publicKey instanceof Point) {
|
|
490
|
+
// ignore
|
|
491
|
+
}
|
|
492
|
+
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
|
493
|
+
publicKey = Point.fromHex(publicKey, false);
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
throw new Error(`Invalid publicKey: ${publicKey}`);
|
|
497
|
+
}
|
|
498
|
+
if (sig instanceof Signature)
|
|
499
|
+
sig.assertValidity();
|
|
500
|
+
else if (sig instanceof Uint8Array || typeof sig === 'string')
|
|
501
|
+
sig = Signature.fromHex(sig);
|
|
502
|
+
else
|
|
503
|
+
throw new Error(`Wrong signature: ${sig}`);
|
|
504
|
+
const { r, s } = sig;
|
|
505
|
+
const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
|
|
506
|
+
const k = hashDomainToScalar(concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context);
|
|
507
|
+
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
|
|
508
|
+
const RkA = ExtendedPoint.fromAffine(r).add(kA);
|
|
509
|
+
// [8][S]B = [8]R + [8][k]A'
|
|
510
|
+
return RkA.subtract(SB).multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
|
|
511
|
+
}
|
|
512
|
+
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
513
|
+
Point.BASE._setWindowSize(8);
|
|
514
|
+
const utils = {
|
|
515
|
+
getExtendedPublicKey,
|
|
516
|
+
mod: modP,
|
|
517
|
+
invert: (a, m = CURVE.P) => mod.invert(a, m),
|
|
518
|
+
/**
|
|
519
|
+
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
|
|
520
|
+
*/
|
|
521
|
+
hashToPrivateScalar: (hash) => hashToPrivateScalar(hash, CURVE_ORDER, true),
|
|
522
|
+
/**
|
|
523
|
+
* ed25519 private keys are uniform 32-bit strings. We do not need to check for
|
|
524
|
+
* modulo bias like we do in secp256k1 randomPrivateKey()
|
|
525
|
+
*/
|
|
526
|
+
randomPrivateKey: () => randomBytes(fieldLen),
|
|
527
|
+
/**
|
|
528
|
+
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
|
529
|
+
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
|
|
530
|
+
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
|
531
|
+
* @param windowSize 2, 4, 8, 16
|
|
532
|
+
*/
|
|
533
|
+
precompute(windowSize = 8, point = Point.BASE) {
|
|
534
|
+
const cached = point.equals(Point.BASE) ? point : new Point(point.x, point.y);
|
|
535
|
+
cached._setWindowSize(windowSize);
|
|
536
|
+
cached.multiply(_2n);
|
|
537
|
+
return cached;
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
return {
|
|
541
|
+
CURVE,
|
|
542
|
+
getPublicKey,
|
|
543
|
+
sign,
|
|
544
|
+
verify,
|
|
545
|
+
ExtendedPoint,
|
|
546
|
+
Point,
|
|
547
|
+
Signature,
|
|
548
|
+
utils,
|
|
549
|
+
};
|
|
550
|
+
}
|
package/lib/esm/group.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
+
// Default group related functions
|
|
3
|
+
const _0n = BigInt(0);
|
|
4
|
+
const _1n = BigInt(1);
|
|
5
|
+
// Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste
|
|
6
|
+
export function wNAF(c, bits) {
|
|
7
|
+
const constTimeNegate = (condition, item) => {
|
|
8
|
+
const neg = item.negate();
|
|
9
|
+
return condition ? neg : item;
|
|
10
|
+
};
|
|
11
|
+
const opts = (W) => {
|
|
12
|
+
if (256 % W)
|
|
13
|
+
throw new Error('Invalid precomputation window, must be power of 2');
|
|
14
|
+
const windows = Math.ceil(bits / W) + 1; // +1, because
|
|
15
|
+
const windowSize = 2 ** (W - 1); // -1 because we skip zero
|
|
16
|
+
return { windows, windowSize };
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
constTimeNegate,
|
|
20
|
+
// non-const time multiplication ladder
|
|
21
|
+
unsafeLadder(elm, n) {
|
|
22
|
+
let p = c.ZERO;
|
|
23
|
+
let d = elm;
|
|
24
|
+
while (n > _0n) {
|
|
25
|
+
if (n & _1n)
|
|
26
|
+
p = p.add(d);
|
|
27
|
+
d = d.double();
|
|
28
|
+
n >>= _1n;
|
|
29
|
+
}
|
|
30
|
+
return p;
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Creates a wNAF precomputation window. Used for caching.
|
|
34
|
+
* Default window size is set by `utils.precompute()` and is equal to 8.
|
|
35
|
+
* Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
|
|
36
|
+
* @returns 65K precomputed points, depending on W
|
|
37
|
+
*/
|
|
38
|
+
precomputeWindow(elm, W) {
|
|
39
|
+
const { windows, windowSize } = opts(W);
|
|
40
|
+
const points = [];
|
|
41
|
+
let p = elm;
|
|
42
|
+
let base = p;
|
|
43
|
+
for (let window = 0; window < windows; window++) {
|
|
44
|
+
base = p;
|
|
45
|
+
points.push(base);
|
|
46
|
+
// =1, because we skip zero
|
|
47
|
+
for (let i = 1; i < windowSize; i++) {
|
|
48
|
+
base = base.add(p);
|
|
49
|
+
points.push(base);
|
|
50
|
+
}
|
|
51
|
+
p = base.double();
|
|
52
|
+
}
|
|
53
|
+
return points;
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
57
|
+
* @param n
|
|
58
|
+
* @param affinePoint optional 2d point to save cached precompute windows on it.
|
|
59
|
+
* @returns real and fake (for const-time) points
|
|
60
|
+
*/
|
|
61
|
+
wNAF(W, precomputes, n) {
|
|
62
|
+
const { windows, windowSize } = opts(W);
|
|
63
|
+
let p = c.ZERO;
|
|
64
|
+
let f = c.BASE;
|
|
65
|
+
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
|
|
66
|
+
const maxNumber = 2 ** W;
|
|
67
|
+
const shiftBy = BigInt(W);
|
|
68
|
+
for (let window = 0; window < windows; window++) {
|
|
69
|
+
const offset = window * windowSize;
|
|
70
|
+
// Extract W bits.
|
|
71
|
+
let wbits = Number(n & mask);
|
|
72
|
+
// Shift number by W bits.
|
|
73
|
+
n >>= shiftBy;
|
|
74
|
+
// If the bits are bigger than max size, we'll split those.
|
|
75
|
+
// +224 => 256 - 32
|
|
76
|
+
if (wbits > windowSize) {
|
|
77
|
+
wbits -= maxNumber;
|
|
78
|
+
n += _1n;
|
|
79
|
+
}
|
|
80
|
+
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
|
|
81
|
+
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
|
|
82
|
+
// there is negate now: it is possible that negated element from low value
|
|
83
|
+
// would be the same as high element, which will create carry into next window.
|
|
84
|
+
// It's not obvious how this can fail, but still worth investigating later.
|
|
85
|
+
// Check if we're onto Zero point.
|
|
86
|
+
// Add random point inside current window to f.
|
|
87
|
+
const offset1 = offset;
|
|
88
|
+
const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
|
|
89
|
+
const cond1 = window % 2 !== 0;
|
|
90
|
+
const cond2 = wbits < 0;
|
|
91
|
+
if (wbits === 0) {
|
|
92
|
+
// The most important part for const-time getPublicKey
|
|
93
|
+
f = f.add(constTimeNegate(cond1, precomputes[offset1]));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
p = p.add(constTimeNegate(cond2, precomputes[offset2]));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
|
|
100
|
+
// Even if the variable is still unused, there are some checks which will
|
|
101
|
+
// throw an exception, so compiler needs to prove they won't happen, which is hard.
|
|
102
|
+
// At this point there is a way to F be infinity-point even if p is not,
|
|
103
|
+
// which makes it less const-time: around 1 bigint multiply.
|
|
104
|
+
return { p, f };
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|