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