@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,25 +1,16 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 (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 * as ut from './utils.js';
|
|
12
|
-
import { ensureBytes } from './utils.js';
|
|
13
|
-
import { wNAF } from './group.js';
|
|
14
|
-
import { hash_to_field as hashToField, validateHTFOpts } from './hash-to-curve.js';
|
|
3
|
+
import { mod } from './modular.js';
|
|
4
|
+
import { bytesToHex, bytesToNumberLE, concatBytes, ensureBytes, numberToBytesLE, } from './utils.js';
|
|
5
|
+
import { wNAF, validateAbsOpts, } from './curve.js';
|
|
15
6
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
|
16
7
|
const _0n = BigInt(0);
|
|
17
8
|
const _1n = BigInt(1);
|
|
18
9
|
const _2n = BigInt(2);
|
|
19
10
|
const _8n = BigInt(8);
|
|
20
11
|
function validateOpts(curve) {
|
|
21
|
-
const opts =
|
|
22
|
-
if (typeof opts.hash !== 'function'
|
|
12
|
+
const opts = validateAbsOpts(curve);
|
|
13
|
+
if (typeof opts.hash !== 'function')
|
|
23
14
|
throw new Error('Invalid hash function');
|
|
24
15
|
for (const i of ['a', 'd']) {
|
|
25
16
|
const val = opts[i];
|
|
@@ -36,25 +27,20 @@ function validateOpts(curve) {
|
|
|
36
27
|
if (typeof opts[fn] !== 'function')
|
|
37
28
|
throw new Error(`Invalid ${fn} function`);
|
|
38
29
|
}
|
|
39
|
-
if (opts.htfDefaults !== undefined)
|
|
40
|
-
validateHTFOpts(opts.htfDefaults);
|
|
41
30
|
// Set defaults
|
|
42
31
|
return Object.freeze({ ...opts });
|
|
43
32
|
}
|
|
44
|
-
//
|
|
33
|
+
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
|
45
34
|
export function twistedEdwards(curveDef) {
|
|
46
35
|
const CURVE = validateOpts(curveDef);
|
|
47
|
-
const Fp = CURVE
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
// Function overrides
|
|
51
|
-
const { randomBytes } = CURVE;
|
|
52
|
-
const modP = Fp.create;
|
|
36
|
+
const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
|
|
37
|
+
const MASK = _2n ** BigInt(nByteLength * 8);
|
|
38
|
+
const modP = Fp.create; // Function overrides
|
|
53
39
|
// sqrt(u/v)
|
|
54
40
|
const uvRatio = CURVE.uvRatio ||
|
|
55
41
|
((u, v) => {
|
|
56
42
|
try {
|
|
57
|
-
return { isValid: true, value: Fp.sqrt(u * Fp.
|
|
43
|
+
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
|
|
58
44
|
}
|
|
59
45
|
catch (e) {
|
|
60
46
|
return { isValid: false, value: _0n };
|
|
@@ -67,57 +53,89 @@ export function twistedEdwards(curveDef) {
|
|
|
67
53
|
throw new Error('Contexts/pre-hash are not supported');
|
|
68
54
|
return data;
|
|
69
55
|
}); // NOOP
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
56
|
+
const inBig = (n) => typeof n === 'bigint' && 0n < n; // n in [1..]
|
|
57
|
+
const inRange = (n, max) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
|
58
|
+
const in0MaskRange = (n) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
|
59
|
+
function assertInRange(n, max) {
|
|
60
|
+
// n in [1..max-1]
|
|
61
|
+
if (inRange(n, max))
|
|
62
|
+
return n;
|
|
63
|
+
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
|
|
64
|
+
}
|
|
65
|
+
function assertGE0(n) {
|
|
66
|
+
// n in [0..CURVE_ORDER-1]
|
|
67
|
+
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
|
|
68
|
+
}
|
|
69
|
+
const pointPrecomputes = new Map();
|
|
70
|
+
function isPoint(other) {
|
|
71
|
+
if (!(other instanceof Point))
|
|
72
|
+
throw new Error('ExtendedPoint expected');
|
|
73
|
+
}
|
|
74
|
+
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
|
75
|
+
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
|
76
|
+
class Point {
|
|
77
|
+
constructor(ex, ey, ez, et) {
|
|
78
|
+
this.ex = ex;
|
|
79
|
+
this.ey = ey;
|
|
80
|
+
this.ez = ez;
|
|
81
|
+
this.et = et;
|
|
82
|
+
if (!in0MaskRange(ex))
|
|
83
|
+
throw new Error('x required');
|
|
84
|
+
if (!in0MaskRange(ey))
|
|
85
|
+
throw new Error('y required');
|
|
86
|
+
if (!in0MaskRange(ez))
|
|
87
|
+
throw new Error('z required');
|
|
88
|
+
if (!in0MaskRange(et))
|
|
89
|
+
throw new Error('t required');
|
|
90
|
+
}
|
|
91
|
+
get x() {
|
|
92
|
+
return this.toAffine().x;
|
|
93
|
+
}
|
|
94
|
+
get y() {
|
|
95
|
+
return this.toAffine().y;
|
|
81
96
|
}
|
|
82
97
|
static fromAffine(p) {
|
|
83
|
-
if (
|
|
84
|
-
throw new
|
|
85
|
-
}
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
return new
|
|
89
|
-
}
|
|
90
|
-
// Takes a bunch of Jacobian Points but executes only one
|
|
91
|
-
// invert on all of them. invert is very slow operation,
|
|
92
|
-
// so this improves performance massively.
|
|
93
|
-
static toAffineBatch(points) {
|
|
94
|
-
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
|
95
|
-
return points.map((p, i) => p.toAffine(toInv[i]));
|
|
98
|
+
if (p instanceof Point)
|
|
99
|
+
throw new Error('extended point not allowed');
|
|
100
|
+
const { x, y } = p || {};
|
|
101
|
+
if (!in0MaskRange(x) || !in0MaskRange(y))
|
|
102
|
+
throw new Error('invalid affine point');
|
|
103
|
+
return new Point(x, y, _1n, modP(x * y));
|
|
96
104
|
}
|
|
97
105
|
static normalizeZ(points) {
|
|
98
|
-
|
|
106
|
+
const toInv = Fp.invertBatch(points.map((p) => p.ez));
|
|
107
|
+
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
|
99
108
|
}
|
|
109
|
+
// "Private method", don't use it directly
|
|
110
|
+
_setWindowSize(windowSize) {
|
|
111
|
+
this._WINDOW_SIZE = windowSize;
|
|
112
|
+
pointPrecomputes.delete(this);
|
|
113
|
+
}
|
|
114
|
+
assertValidity() { }
|
|
100
115
|
// Compare one point to another.
|
|
101
116
|
equals(other) {
|
|
102
|
-
|
|
103
|
-
const {
|
|
104
|
-
const {
|
|
117
|
+
isPoint(other);
|
|
118
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
119
|
+
const { ex: X2, ey: Y2, ez: Z2 } = other;
|
|
105
120
|
const X1Z2 = modP(X1 * Z2);
|
|
106
121
|
const X2Z1 = modP(X2 * Z1);
|
|
107
122
|
const Y1Z2 = modP(Y1 * Z2);
|
|
108
123
|
const Y2Z1 = modP(Y2 * Z1);
|
|
109
124
|
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
110
125
|
}
|
|
111
|
-
|
|
126
|
+
is0() {
|
|
127
|
+
return this.equals(Point.ZERO);
|
|
128
|
+
}
|
|
112
129
|
negate() {
|
|
113
|
-
|
|
130
|
+
// Flips point sign to a negative one (-x, y in affine coords)
|
|
131
|
+
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
|
|
114
132
|
}
|
|
115
133
|
// Fast algo for doubling Extended Point.
|
|
116
134
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
|
117
135
|
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
|
118
136
|
double() {
|
|
119
137
|
const { a } = CURVE;
|
|
120
|
-
const {
|
|
138
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
121
139
|
const A = modP(X1 * X1); // A = X12
|
|
122
140
|
const B = modP(Y1 * Y1); // B = Y12
|
|
123
141
|
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
|
|
@@ -131,16 +149,16 @@ export function twistedEdwards(curveDef) {
|
|
|
131
149
|
const Y3 = modP(G * H); // Y3 = G*H
|
|
132
150
|
const T3 = modP(E * H); // T3 = E*H
|
|
133
151
|
const Z3 = modP(F * G); // Z3 = F*G
|
|
134
|
-
return new
|
|
152
|
+
return new Point(X3, Y3, Z3, T3);
|
|
135
153
|
}
|
|
136
154
|
// Fast algo for adding 2 Extended Points.
|
|
137
155
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
|
138
156
|
// Cost: 9M + 1*a + 1*d + 7add.
|
|
139
157
|
add(other) {
|
|
140
|
-
|
|
158
|
+
isPoint(other);
|
|
141
159
|
const { a, d } = CURVE;
|
|
142
|
-
const {
|
|
143
|
-
const {
|
|
160
|
+
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
|
161
|
+
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
|
|
144
162
|
// Faster algo for adding 2 Extended Points when curve's a=-1.
|
|
145
163
|
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
|
|
146
164
|
// Cost: 8M + 8add + 2*2.
|
|
@@ -160,7 +178,7 @@ export function twistedEdwards(curveDef) {
|
|
|
160
178
|
const Y3 = modP(G * H);
|
|
161
179
|
const T3 = modP(E * H);
|
|
162
180
|
const Z3 = modP(F * G);
|
|
163
|
-
return new
|
|
181
|
+
return new Point(X3, Y3, Z3, T3);
|
|
164
182
|
}
|
|
165
183
|
const A = modP(X1 * X2); // A = X1*X2
|
|
166
184
|
const B = modP(Y1 * Y2); // B = Y1*Y2
|
|
@@ -174,44 +192,30 @@ export function twistedEdwards(curveDef) {
|
|
|
174
192
|
const Y3 = modP(G * H); // Y3 = G*H
|
|
175
193
|
const T3 = modP(E * H); // T3 = E*H
|
|
176
194
|
const Z3 = modP(F * G); // Z3 = F*G
|
|
177
|
-
return new
|
|
195
|
+
return new Point(X3, Y3, Z3, T3);
|
|
178
196
|
}
|
|
179
197
|
subtract(other) {
|
|
180
198
|
return this.add(other.negate());
|
|
181
199
|
}
|
|
182
|
-
wNAF(n
|
|
183
|
-
|
|
184
|
-
affinePoint = Point.BASE;
|
|
185
|
-
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
|
186
|
-
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
|
187
|
-
if (!precomputes) {
|
|
188
|
-
precomputes = wnaf.precomputeWindow(this, W);
|
|
189
|
-
if (affinePoint && W !== 1) {
|
|
190
|
-
precomputes = ExtendedPoint.normalizeZ(precomputes);
|
|
191
|
-
pointPrecomputes.set(affinePoint, precomputes);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
const { p, f } = wnaf.wNAF(W, precomputes, n);
|
|
195
|
-
return ExtendedPoint.normalizeZ([p, f])[0];
|
|
200
|
+
wNAF(n) {
|
|
201
|
+
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
|
|
196
202
|
}
|
|
197
|
-
// Constant
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return this.wNAF(normalizeScalar(scalar, CURVE_ORDER), affinePoint);
|
|
203
|
+
// Constant-time multiplication.
|
|
204
|
+
multiply(scalar) {
|
|
205
|
+
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
|
|
206
|
+
return Point.normalizeZ([p, f])[0];
|
|
202
207
|
}
|
|
203
208
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
204
209
|
// It's faster, but should only be used when you don't care about
|
|
205
210
|
// an exposed private key e.g. sig verification.
|
|
206
211
|
multiplyUnsafe(scalar) {
|
|
207
|
-
let n =
|
|
208
|
-
const P0 = ExtendedPoint.ZERO;
|
|
212
|
+
let n = assertGE0(scalar);
|
|
209
213
|
if (n === _0n)
|
|
210
|
-
return
|
|
211
|
-
if (this.equals(
|
|
214
|
+
return I;
|
|
215
|
+
if (this.equals(I) || n === _1n)
|
|
212
216
|
return this;
|
|
213
|
-
if (this.equals(
|
|
214
|
-
return this.wNAF(n);
|
|
217
|
+
if (this.equals(G))
|
|
218
|
+
return this.wNAF(n).p;
|
|
215
219
|
return wnaf.unsafeLadder(this, n);
|
|
216
220
|
}
|
|
217
221
|
// Checks if point is of small order.
|
|
@@ -219,28 +223,28 @@ export function twistedEdwards(curveDef) {
|
|
|
219
223
|
// point with torsion component.
|
|
220
224
|
// Multiplies point by cofactor and checks if the result is 0.
|
|
221
225
|
isSmallOrder() {
|
|
222
|
-
return this.multiplyUnsafe(
|
|
226
|
+
return this.multiplyUnsafe(cofactor).is0();
|
|
223
227
|
}
|
|
224
|
-
// Multiplies point by curve order
|
|
228
|
+
// Multiplies point by curve order and checks if the result is 0.
|
|
225
229
|
// Returns `false` is the point is dirty.
|
|
226
230
|
isTorsionFree() {
|
|
227
|
-
return wnaf.unsafeLadder(this, CURVE_ORDER).
|
|
231
|
+
return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
|
|
228
232
|
}
|
|
229
233
|
// Converts Extended point to default (x, y) coordinates.
|
|
230
234
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
|
231
|
-
toAffine(
|
|
232
|
-
const { x, y, z } = this;
|
|
233
|
-
const is0 = this.
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
const ax = modP(x *
|
|
237
|
-
const ay = modP(y *
|
|
238
|
-
const zz = modP(z *
|
|
235
|
+
toAffine(iz) {
|
|
236
|
+
const { ex: x, ey: y, ez: z } = this;
|
|
237
|
+
const is0 = this.is0();
|
|
238
|
+
if (iz == null)
|
|
239
|
+
iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
|
|
240
|
+
const ax = modP(x * iz);
|
|
241
|
+
const ay = modP(y * iz);
|
|
242
|
+
const zz = modP(z * iz);
|
|
239
243
|
if (is0)
|
|
240
|
-
return
|
|
244
|
+
return { x: _0n, y: _1n };
|
|
241
245
|
if (zz !== _1n)
|
|
242
246
|
throw new Error('invZ was invalid');
|
|
243
|
-
return
|
|
247
|
+
return { x: ax, y: ay };
|
|
244
248
|
}
|
|
245
249
|
clearCofactor() {
|
|
246
250
|
const { h: cofactor } = CURVE;
|
|
@@ -248,306 +252,127 @@ export function twistedEdwards(curveDef) {
|
|
|
248
252
|
return this;
|
|
249
253
|
return this.multiplyUnsafe(cofactor);
|
|
250
254
|
}
|
|
251
|
-
}
|
|
252
|
-
ExtendedPoint.BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
253
|
-
ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
|
|
254
|
-
const wnaf = wNAF(ExtendedPoint, CURVE.nByteLength * 8);
|
|
255
|
-
function assertExtPoint(other) {
|
|
256
|
-
if (!(other instanceof ExtendedPoint))
|
|
257
|
-
throw new TypeError('ExtendedPoint expected');
|
|
258
|
-
}
|
|
259
|
-
// Stores precomputed values for points.
|
|
260
|
-
const pointPrecomputes = new WeakMap();
|
|
261
|
-
/**
|
|
262
|
-
* Default Point works in affine coordinates: (x, y)
|
|
263
|
-
*/
|
|
264
|
-
class Point {
|
|
265
|
-
constructor(x, y) {
|
|
266
|
-
this.x = x;
|
|
267
|
-
this.y = y;
|
|
268
|
-
}
|
|
269
|
-
// "Private method", don't use it directly.
|
|
270
|
-
_setWindowSize(windowSize) {
|
|
271
|
-
this._WINDOW_SIZE = windowSize;
|
|
272
|
-
pointPrecomputes.delete(this);
|
|
273
|
-
}
|
|
274
255
|
// Converts hash string or Uint8Array to Point.
|
|
275
256
|
// Uses algo from RFC8032 5.1.3.
|
|
276
257
|
static fromHex(hex, strict = true) {
|
|
277
258
|
const { d, a } = CURVE;
|
|
278
259
|
const len = Fp.BYTES;
|
|
279
|
-
hex = ensureBytes(hex, len);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
// Ed25519: x² = (y
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
// -> x² = (y²-1) / (d*y²-a)
|
|
301
|
-
// The denominator is always non-zero mod p. Let u = y² - 1 and v = d y² + 1.
|
|
302
|
-
const y2 = modP(y * y);
|
|
303
|
-
const u = modP(y2 - _1n);
|
|
304
|
-
const v = modP(d * y2 - a);
|
|
305
|
-
let { isValid, value: x } = uvRatio(u, v);
|
|
260
|
+
hex = ensureBytes(hex, len); // copy hex to a new array
|
|
261
|
+
const normed = hex.slice(); // copy again, we'll manipulate it
|
|
262
|
+
const lastByte = hex[len - 1]; // select last byte
|
|
263
|
+
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
|
264
|
+
const y = bytesToNumberLE(normed);
|
|
265
|
+
if (y === _0n) {
|
|
266
|
+
// y=0 is allowed
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
|
270
|
+
if (strict)
|
|
271
|
+
assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
|
|
272
|
+
else
|
|
273
|
+
assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
|
|
274
|
+
}
|
|
275
|
+
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
|
276
|
+
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
|
277
|
+
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
|
278
|
+
const u = modP(y2 - _1n); // u = y² - 1
|
|
279
|
+
const v = modP(d * y2 - a); // v = d y² + 1.
|
|
280
|
+
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
|
306
281
|
if (!isValid)
|
|
307
282
|
throw new Error('Point.fromHex: invalid y coordinate');
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
// 2, set x <-- p - x. Return the decoded point (x,y).
|
|
311
|
-
const isXOdd = (x & _1n) === _1n;
|
|
312
|
-
const isLastByteOdd = (lastByte & 0x80) !== 0;
|
|
283
|
+
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
|
|
284
|
+
const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
|
|
313
285
|
if (isLastByteOdd !== isXOdd)
|
|
314
|
-
x = modP(-x);
|
|
315
|
-
return
|
|
286
|
+
x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
|
287
|
+
return Point.fromAffine({ x, y });
|
|
316
288
|
}
|
|
317
|
-
static fromPrivateKey(
|
|
318
|
-
return getExtendedPublicKey(
|
|
289
|
+
static fromPrivateKey(privKey) {
|
|
290
|
+
return getExtendedPublicKey(privKey).point;
|
|
319
291
|
}
|
|
320
|
-
// There can always be only two x values (x, -x) for any y
|
|
321
|
-
// When compressing point, it's enough to only store its y coordinate
|
|
322
|
-
// and use the last byte to encode sign of x.
|
|
323
292
|
toRawBytes() {
|
|
324
|
-
const
|
|
325
|
-
bytes
|
|
326
|
-
|
|
293
|
+
const { x, y } = this.toAffine();
|
|
294
|
+
const bytes = numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
|
295
|
+
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
|
296
|
+
return bytes; // and use the last byte to encode sign of x
|
|
327
297
|
}
|
|
328
|
-
// Same as toRawBytes, but returns string.
|
|
329
298
|
toHex() {
|
|
330
|
-
return
|
|
331
|
-
}
|
|
332
|
-
// Determines if point is in prime-order subgroup.
|
|
333
|
-
// Returns `false` is the point is dirty.
|
|
334
|
-
isTorsionFree() {
|
|
335
|
-
return ExtendedPoint.fromAffine(this).isTorsionFree();
|
|
336
|
-
}
|
|
337
|
-
equals(other) {
|
|
338
|
-
if (!(other instanceof Point))
|
|
339
|
-
throw new TypeError('Point#equals: expected Point');
|
|
340
|
-
return this.x === other.x && this.y === other.y;
|
|
341
|
-
}
|
|
342
|
-
negate() {
|
|
343
|
-
return new Point(modP(-this.x), this.y);
|
|
344
|
-
}
|
|
345
|
-
double() {
|
|
346
|
-
return ExtendedPoint.fromAffine(this).double().toAffine();
|
|
347
|
-
}
|
|
348
|
-
add(other) {
|
|
349
|
-
return ExtendedPoint.fromAffine(this).add(ExtendedPoint.fromAffine(other)).toAffine();
|
|
350
|
-
}
|
|
351
|
-
subtract(other) {
|
|
352
|
-
return this.add(other.negate());
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Constant time multiplication.
|
|
356
|
-
* @param scalar Big-Endian number
|
|
357
|
-
* @returns new point
|
|
358
|
-
*/
|
|
359
|
-
multiply(scalar) {
|
|
360
|
-
return ExtendedPoint.fromAffine(this).multiply(scalar, this).toAffine();
|
|
361
|
-
}
|
|
362
|
-
clearCofactor() {
|
|
363
|
-
return ExtendedPoint.fromAffine(this).clearCofactor().toAffine();
|
|
364
|
-
}
|
|
365
|
-
// Encodes byte string to elliptic curve
|
|
366
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
|
367
|
-
static hashToCurve(msg, options) {
|
|
368
|
-
const { mapToCurve, htfDefaults } = CURVE;
|
|
369
|
-
if (!mapToCurve)
|
|
370
|
-
throw new Error('No mapToCurve defined for curve');
|
|
371
|
-
const u = hashToField(ensureBytes(msg), 2, { ...htfDefaults, ...options });
|
|
372
|
-
const { x: x0, y: y0 } = mapToCurve(u[0]);
|
|
373
|
-
const { x: x1, y: y1 } = mapToCurve(u[1]);
|
|
374
|
-
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
|
375
|
-
return p;
|
|
376
|
-
}
|
|
377
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
|
378
|
-
static encodeToCurve(msg, options) {
|
|
379
|
-
const { mapToCurve, htfDefaults } = CURVE;
|
|
380
|
-
if (!mapToCurve)
|
|
381
|
-
throw new Error('No mapToCurve defined for curve');
|
|
382
|
-
const u = hashToField(ensureBytes(msg), 1, { ...htfDefaults, ...options });
|
|
383
|
-
const { x, y } = mapToCurve(u[0]);
|
|
384
|
-
return new Point(x, y).clearCofactor();
|
|
299
|
+
return bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
|
385
300
|
}
|
|
386
301
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* EDDSA signature.
|
|
395
|
-
*/
|
|
396
|
-
class Signature {
|
|
397
|
-
constructor(r, s) {
|
|
398
|
-
this.r = r;
|
|
399
|
-
this.s = s;
|
|
400
|
-
this.assertValidity();
|
|
401
|
-
}
|
|
402
|
-
static fromHex(hex) {
|
|
403
|
-
const len = Fp.BYTES;
|
|
404
|
-
const bytes = ensureBytes(hex, 2 * len);
|
|
405
|
-
const r = Point.fromHex(bytes.slice(0, len), false);
|
|
406
|
-
const s = ut.bytesToNumberLE(bytes.slice(len, 2 * len));
|
|
407
|
-
return new Signature(r, s);
|
|
408
|
-
}
|
|
409
|
-
assertValidity() {
|
|
410
|
-
const { r, s } = this;
|
|
411
|
-
if (!(r instanceof Point))
|
|
412
|
-
throw new Error('Expected Point instance');
|
|
413
|
-
// 0 <= s < l
|
|
414
|
-
normalizeScalar(s, CURVE_ORDER, false);
|
|
415
|
-
return this;
|
|
416
|
-
}
|
|
417
|
-
toRawBytes() {
|
|
418
|
-
return ut.concatBytes(this.r.toRawBytes(), ut.numberToBytesLE(this.s, Fp.BYTES));
|
|
419
|
-
}
|
|
420
|
-
toHex() {
|
|
421
|
-
return ut.bytesToHex(this.toRawBytes());
|
|
422
|
-
}
|
|
302
|
+
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
|
303
|
+
Point.ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
|
|
304
|
+
const { BASE: G, ZERO: I } = Point;
|
|
305
|
+
const wnaf = wNAF(Point, nByteLength * 8);
|
|
306
|
+
function modN(a) {
|
|
307
|
+
return mod(a, CURVE_ORDER);
|
|
423
308
|
}
|
|
424
309
|
// Little-endian SHA512 with modulo n
|
|
425
|
-
function
|
|
426
|
-
return
|
|
310
|
+
function modN_LE(hash) {
|
|
311
|
+
return modN(bytesToNumberLE(hash));
|
|
427
312
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
* For strict == false: `0 <= num < max`.
|
|
432
|
-
* Converts non-float safe numbers to bigints.
|
|
433
|
-
*/
|
|
434
|
-
function normalizeScalar(num, max, strict = true) {
|
|
435
|
-
if (!max)
|
|
436
|
-
throw new TypeError('Specify max value');
|
|
437
|
-
if (ut.isPositiveInt(num))
|
|
438
|
-
num = BigInt(num);
|
|
439
|
-
if (typeof num === 'bigint' && num < max) {
|
|
440
|
-
if (strict) {
|
|
441
|
-
if (_0n < num)
|
|
442
|
-
return num;
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
if (_0n <= num)
|
|
446
|
-
return num;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`);
|
|
313
|
+
function isHex(item, err) {
|
|
314
|
+
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
|
315
|
+
throw new Error(`${err} must be hex string or Uint8Array`);
|
|
450
316
|
}
|
|
451
317
|
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
|
452
318
|
function getExtendedPublicKey(key) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const keyb = typeof key === 'bigint' || typeof key === 'number'
|
|
456
|
-
? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
|
|
457
|
-
: key;
|
|
319
|
+
isHex(key, 'private key');
|
|
320
|
+
const len = nByteLength;
|
|
458
321
|
// Hash private key with curve's hash function to produce uniformingly random input
|
|
459
|
-
//
|
|
460
|
-
const hashed = ensureBytes(
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
const scalar = modnLE(head);
|
|
467
|
-
// Point on Edwards curve aka public key
|
|
468
|
-
const point = Point.BASE.multiply(scalar);
|
|
469
|
-
// Uint8Array representation
|
|
470
|
-
const pointBytes = point.toRawBytes();
|
|
322
|
+
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
|
323
|
+
const hashed = ensureBytes(cHash(ensureBytes(key, len)), 2 * len);
|
|
324
|
+
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
|
325
|
+
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
|
326
|
+
const scalar = modN_LE(head); // The actual private scalar
|
|
327
|
+
const point = G.multiply(scalar); // Point on Edwards curve aka public key
|
|
328
|
+
const pointBytes = point.toRawBytes(); // Uint8Array representation
|
|
471
329
|
return { head, prefix, scalar, point, pointBytes };
|
|
472
330
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
* 2. 3 least significant bits of the first byte are cleared
|
|
477
|
-
*/
|
|
478
|
-
function getPublicKey(privateKey) {
|
|
479
|
-
return getExtendedPublicKey(privateKey).pointBytes;
|
|
331
|
+
// Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
|
|
332
|
+
function getPublicKey(privKey) {
|
|
333
|
+
return getExtendedPublicKey(privKey).pointBytes;
|
|
480
334
|
}
|
|
481
|
-
|
|
482
|
-
function hashDomainToScalar(
|
|
483
|
-
|
|
484
|
-
return
|
|
335
|
+
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
|
336
|
+
function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
|
|
337
|
+
const msg = concatBytes(...msgs);
|
|
338
|
+
return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash)));
|
|
485
339
|
}
|
|
486
340
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
487
|
-
function sign(
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
const
|
|
496
|
-
|
|
341
|
+
function sign(msg, privKey, context) {
|
|
342
|
+
isHex(msg, 'message');
|
|
343
|
+
msg = ensureBytes(msg);
|
|
344
|
+
if (preHash)
|
|
345
|
+
msg = preHash(msg); // for ed25519ph etc.
|
|
346
|
+
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
|
347
|
+
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
|
348
|
+
const R = G.multiply(r).toRawBytes(); // R = rG
|
|
349
|
+
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
|
350
|
+
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
|
351
|
+
assertGE0(s); // 0 <= s < l
|
|
352
|
+
const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES));
|
|
353
|
+
return ensureBytes(res, nByteLength * 2); // 64-byte signature
|
|
497
354
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
// When Point instance is passed, we assume it has already been checked, for performance.
|
|
513
|
-
// If user passes Point/Sig instance, we assume it has been already verified.
|
|
514
|
-
// We don't check its equations for performance. We do check for valid bounds for s though
|
|
515
|
-
// We always check for: a) s bounds. b) hex validity
|
|
516
|
-
if (publicKey instanceof Point) {
|
|
517
|
-
// ignore
|
|
518
|
-
}
|
|
519
|
-
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
|
520
|
-
publicKey = Point.fromHex(publicKey, false);
|
|
521
|
-
}
|
|
522
|
-
else {
|
|
523
|
-
throw new Error(`Invalid publicKey: ${publicKey}`);
|
|
524
|
-
}
|
|
525
|
-
if (sig instanceof Signature)
|
|
526
|
-
sig.assertValidity();
|
|
527
|
-
else if (sig instanceof Uint8Array || typeof sig === 'string')
|
|
528
|
-
sig = Signature.fromHex(sig);
|
|
529
|
-
else
|
|
530
|
-
throw new Error(`Wrong signature: ${sig}`);
|
|
531
|
-
const { r, s } = sig;
|
|
532
|
-
const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
|
|
533
|
-
const k = hashDomainToScalar(ut.concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context);
|
|
534
|
-
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
|
|
535
|
-
const RkA = ExtendedPoint.fromAffine(r).add(kA);
|
|
355
|
+
function verify(sig, msg, publicKey, context) {
|
|
356
|
+
isHex(sig, 'sig');
|
|
357
|
+
isHex(msg, 'message');
|
|
358
|
+
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
|
359
|
+
sig = ensureBytes(sig, 2 * len); // An extended group equation is checked.
|
|
360
|
+
msg = ensureBytes(msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
|
|
361
|
+
if (preHash)
|
|
362
|
+
msg = preHash(msg); // for ed25519ph, etc
|
|
363
|
+
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
|
364
|
+
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
|
365
|
+
const s = bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
|
366
|
+
const SB = G.multiplyUnsafe(s);
|
|
367
|
+
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
|
368
|
+
const RkA = R.add(A.multiplyUnsafe(k));
|
|
536
369
|
// [8][S]B = [8]R + [8][k]A'
|
|
537
|
-
return RkA.subtract(SB).clearCofactor().equals(
|
|
370
|
+
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
|
|
538
371
|
}
|
|
539
|
-
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
540
|
-
Point.BASE._setWindowSize(8);
|
|
372
|
+
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
541
373
|
const utils = {
|
|
542
374
|
getExtendedPublicKey,
|
|
543
|
-
|
|
544
|
-
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
|
|
545
|
-
*/
|
|
546
|
-
hashToPrivateScalar: (hash) => ut.hashToPrivateScalar(hash, CURVE_ORDER, true),
|
|
547
|
-
/**
|
|
548
|
-
* ed25519 private keys are uniform 32-bit strings. We do not need to check for
|
|
549
|
-
* modulo bias like we do in secp256k1 randomPrivateKey()
|
|
550
|
-
*/
|
|
375
|
+
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
|
|
551
376
|
randomPrivateKey: () => randomBytes(Fp.BYTES),
|
|
552
377
|
/**
|
|
553
378
|
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
|
@@ -556,10 +381,9 @@ export function twistedEdwards(curveDef) {
|
|
|
556
381
|
* @param windowSize 2, 4, 8, 16
|
|
557
382
|
*/
|
|
558
383
|
precompute(windowSize = 8, point = Point.BASE) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
return cached;
|
|
384
|
+
point._setWindowSize(windowSize);
|
|
385
|
+
point.multiply(BigInt(3));
|
|
386
|
+
return point;
|
|
563
387
|
},
|
|
564
388
|
};
|
|
565
389
|
return {
|
|
@@ -567,9 +391,7 @@ export function twistedEdwards(curveDef) {
|
|
|
567
391
|
getPublicKey,
|
|
568
392
|
sign,
|
|
569
393
|
verify,
|
|
570
|
-
ExtendedPoint,
|
|
571
|
-
Point,
|
|
572
|
-
Signature,
|
|
394
|
+
ExtendedPoint: Point,
|
|
573
395
|
utils,
|
|
574
396
|
};
|
|
575
397
|
}
|