@1shotapi/ows-signer 0.2.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 +106 -0
- package/html/index.html +54 -0
- package/package.json +37 -0
- package/src/constants.js +30 -0
- package/src/crypto/aes256.js +115 -0
- package/src/crypto/prf.js +119 -0
- package/src/crypto/recovery.js +81 -0
- package/src/crypto/sign.js +59 -0
- package/src/crypto/vendor/noble-ed25519.js +385 -0
- package/src/crypto/vendor/noble-secp256k1.js +602 -0
- package/src/debug.js +42 -0
- package/src/handlers.js +691 -0
- package/src/hex.js +68 -0
- package/src/main.js +40 -0
- package/src/rpc.js +48 -0
- package/src/state.js +96 -0
- package/src/ui.js +68 -0
- package/src/webauthn.js +150 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
|
|
2
|
+
const P = 2n ** 255n - 19n; // ed25519 is twisted edwards curve
|
|
3
|
+
const N = 2n ** 252n + 27742317777372353535851937790883648493n; // curve's (group) order
|
|
4
|
+
const Gx = 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an; // base point x
|
|
5
|
+
const Gy = 0x6666666666666666666666666666666666666666666666666666666666666658n; // base point y
|
|
6
|
+
const CURVE = {
|
|
7
|
+
a: -1n, // where a=-1, d = -(121665/121666) == -(121665 * inv(121666)) mod P
|
|
8
|
+
d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n,
|
|
9
|
+
p: P, n: N, h: 8, Gx, Gy // field prime, curve (group) order, cofactor
|
|
10
|
+
};
|
|
11
|
+
const err = (m = '') => { throw new Error(m); }; // error helper, messes-up stack trace
|
|
12
|
+
const str = (s) => typeof s === 'string'; // is string
|
|
13
|
+
const isu8 = (a) => (a instanceof Uint8Array ||
|
|
14
|
+
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array'));
|
|
15
|
+
const au8 = (a, l) => // is Uint8Array (of specific length)
|
|
16
|
+
!isu8(a) || (typeof l === 'number' && l > 0 && a.length !== l) ?
|
|
17
|
+
err('Uint8Array of valid length expected') : a;
|
|
18
|
+
const u8n = (data) => new Uint8Array(data); // creates Uint8Array
|
|
19
|
+
const toU8 = (a, len) => au8(str(a) ? h2b(a) : u8n(au8(a)), len); // norm(hex/u8a) to u8a
|
|
20
|
+
const mod = (a, b = P) => { let r = a % b; return r >= 0n ? r : b + r; }; // mod division
|
|
21
|
+
const isPoint = (p) => (p instanceof Point ? p : err('Point expected')); // is xyzt point
|
|
22
|
+
class Point {
|
|
23
|
+
constructor(ex, ey, ez, et) {
|
|
24
|
+
this.ex = ex;
|
|
25
|
+
this.ey = ey;
|
|
26
|
+
this.ez = ez;
|
|
27
|
+
this.et = et;
|
|
28
|
+
}
|
|
29
|
+
static fromAffine(p) { return new Point(p.x, p.y, 1n, mod(p.x * p.y)); }
|
|
30
|
+
static fromHex(hex, zip215 = false) {
|
|
31
|
+
const { d } = CURVE;
|
|
32
|
+
hex = toU8(hex, 32);
|
|
33
|
+
const normed = hex.slice(); // copy the array to not mess it up
|
|
34
|
+
const lastByte = hex[31];
|
|
35
|
+
normed[31] = lastByte & ~0x80; // adjust first LE byte = last BE byte
|
|
36
|
+
const y = b2n_LE(normed); // decode as little-endian, convert to num
|
|
37
|
+
if (zip215 && !(0n <= y && y < 2n ** 256n))
|
|
38
|
+
err('bad y coord 1'); // zip215=true [1..2^256-1]
|
|
39
|
+
if (!zip215 && !(0n <= y && y < P))
|
|
40
|
+
err('bad y coord 2'); // zip215=false [1..P-1]
|
|
41
|
+
const y2 = mod(y * y); // y²
|
|
42
|
+
const u = mod(y2 - 1n); // u=y²-1
|
|
43
|
+
const v = mod(d * y2 + 1n); // v=dy²+1
|
|
44
|
+
let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root
|
|
45
|
+
if (!isValid)
|
|
46
|
+
err('bad y coordinate 3'); // not square root: bad point
|
|
47
|
+
const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate
|
|
48
|
+
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
|
|
49
|
+
if (!zip215 && x === 0n && isLastByteOdd)
|
|
50
|
+
err('bad y coord 3'); // x=0 and x_0 = 1
|
|
51
|
+
if (isLastByteOdd !== isXOdd)
|
|
52
|
+
x = mod(-x);
|
|
53
|
+
return new Point(x, y, 1n, mod(x * y)); // Z=1, T=xy
|
|
54
|
+
}
|
|
55
|
+
get x() { return this.toAffine().x; } // .x, .y will call expensive toAffine.
|
|
56
|
+
get y() { return this.toAffine().y; } // Should be used with care.
|
|
57
|
+
equals(other) {
|
|
58
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
59
|
+
const { ex: X2, ey: Y2, ez: Z2 } = isPoint(other); // isPoint() checks class equality
|
|
60
|
+
const X1Z2 = mod(X1 * Z2), X2Z1 = mod(X2 * Z1);
|
|
61
|
+
const Y1Z2 = mod(Y1 * Z2), Y2Z1 = mod(Y2 * Z1);
|
|
62
|
+
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
63
|
+
}
|
|
64
|
+
is0() { return this.equals(I); }
|
|
65
|
+
negate() {
|
|
66
|
+
return new Point(mod(-this.ex), this.ey, this.ez, mod(-this.et));
|
|
67
|
+
}
|
|
68
|
+
double() {
|
|
69
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this; // Cost: 4M + 4S + 1*a + 6add + 1*2
|
|
70
|
+
const { a } = CURVE; // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
|
71
|
+
const A = mod(X1 * X1);
|
|
72
|
+
const B = mod(Y1 * Y1);
|
|
73
|
+
const C = mod(2n * mod(Z1 * Z1));
|
|
74
|
+
const D = mod(a * A);
|
|
75
|
+
const x1y1 = X1 + Y1;
|
|
76
|
+
const E = mod(mod(x1y1 * x1y1) - A - B);
|
|
77
|
+
const G = D + B;
|
|
78
|
+
const F = G - C;
|
|
79
|
+
const H = D - B;
|
|
80
|
+
const X3 = mod(E * F);
|
|
81
|
+
const Y3 = mod(G * H);
|
|
82
|
+
const T3 = mod(E * H);
|
|
83
|
+
const Z3 = mod(F * G);
|
|
84
|
+
return new Point(X3, Y3, Z3, T3);
|
|
85
|
+
}
|
|
86
|
+
add(other) {
|
|
87
|
+
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this; // Cost: 8M + 1*k + 8add + 1*2.
|
|
88
|
+
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = isPoint(other); // doesn't check if other on-curve
|
|
89
|
+
const { a, d } = CURVE; // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
|
90
|
+
const A = mod(X1 * X2);
|
|
91
|
+
const B = mod(Y1 * Y2);
|
|
92
|
+
const C = mod(T1 * d * T2);
|
|
93
|
+
const D = mod(Z1 * Z2);
|
|
94
|
+
const E = mod((X1 + Y1) * (X2 + Y2) - A - B);
|
|
95
|
+
const F = mod(D - C);
|
|
96
|
+
const G = mod(D + C);
|
|
97
|
+
const H = mod(B - a * A);
|
|
98
|
+
const X3 = mod(E * F);
|
|
99
|
+
const Y3 = mod(G * H);
|
|
100
|
+
const T3 = mod(E * H);
|
|
101
|
+
const Z3 = mod(F * G);
|
|
102
|
+
return new Point(X3, Y3, Z3, T3);
|
|
103
|
+
}
|
|
104
|
+
mul(n, safe = true) {
|
|
105
|
+
if (n === 0n)
|
|
106
|
+
return safe === true ? err('cannot multiply by 0') : I;
|
|
107
|
+
if (!(typeof n === 'bigint' && 0n < n && n < N))
|
|
108
|
+
err('invalid scalar, must be < L');
|
|
109
|
+
if (!safe && this.is0() || n === 1n)
|
|
110
|
+
return this; // safe=true bans 0. safe=false allows 0.
|
|
111
|
+
if (this.equals(G))
|
|
112
|
+
return wNAF(n).p; // use wNAF precomputes for base points
|
|
113
|
+
let p = I, f = G; // init result point & fake point
|
|
114
|
+
for (let d = this; n > 0n; d = d.double(), n >>= 1n) { // double-and-add ladder
|
|
115
|
+
if (n & 1n)
|
|
116
|
+
p = p.add(d); // if bit is present, add to point
|
|
117
|
+
else if (safe)
|
|
118
|
+
f = f.add(d); // if not, add to fake for timing safety
|
|
119
|
+
}
|
|
120
|
+
return p;
|
|
121
|
+
}
|
|
122
|
+
multiply(scalar) { return this.mul(scalar); } // Aliases for compatibilty
|
|
123
|
+
clearCofactor() { return this.mul(BigInt(CURVE.h), false); } // multiply by cofactor
|
|
124
|
+
isSmallOrder() { return this.clearCofactor().is0(); } // check if P is small order
|
|
125
|
+
isTorsionFree() {
|
|
126
|
+
let p = this.mul(N / 2n, false).double(); // ensures the point is not "bad".
|
|
127
|
+
if (N % 2n)
|
|
128
|
+
p = p.add(this); // P^(N+1) // P*N == (P*(N/2))*2+P
|
|
129
|
+
return p.is0();
|
|
130
|
+
}
|
|
131
|
+
toAffine() {
|
|
132
|
+
const { ex: x, ey: y, ez: z } = this; // (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy)
|
|
133
|
+
if (this.equals(I))
|
|
134
|
+
return { x: 0n, y: 1n }; // fast-path for zero point
|
|
135
|
+
const iz = invert(z); // z^-1: invert z
|
|
136
|
+
if (mod(z * iz) !== 1n)
|
|
137
|
+
err('invalid inverse'); // (z * z^-1) must be 1, otherwise bad math
|
|
138
|
+
return { x: mod(x * iz), y: mod(y * iz) }; // x = x*z^-1; y = y*z^-1
|
|
139
|
+
}
|
|
140
|
+
toRawBytes() {
|
|
141
|
+
const { x, y } = this.toAffine(); // convert to affine 2d point
|
|
142
|
+
const b = n2b_32LE(y); // encode number to 32 bytes
|
|
143
|
+
b[31] |= x & 1n ? 0x80 : 0; // store sign in first LE byte
|
|
144
|
+
return b;
|
|
145
|
+
}
|
|
146
|
+
toHex() { return b2h(this.toRawBytes()); } // encode to hex string
|
|
147
|
+
}
|
|
148
|
+
Point.BASE = new Point(Gx, Gy, 1n, mod(Gx * Gy)); // Generator / Base point
|
|
149
|
+
Point.ZERO = new Point(0n, 1n, 1n, 0n); // Identity / Zero point
|
|
150
|
+
const { BASE: G, ZERO: I } = Point; // Generator, identity points
|
|
151
|
+
const padh = (num, pad) => num.toString(16).padStart(pad, '0');
|
|
152
|
+
const b2h = (b) => Array.from(b).map(e => padh(e, 2)).join(''); // bytes to hex
|
|
153
|
+
const h2b = (hex) => {
|
|
154
|
+
const l = hex.length; // error if not string,
|
|
155
|
+
if (!str(hex) || l % 2)
|
|
156
|
+
err('hex invalid 1'); // or has odd length like 3, 5.
|
|
157
|
+
const arr = u8n(l / 2); // create result array
|
|
158
|
+
for (let i = 0; i < arr.length; i++) {
|
|
159
|
+
const j = i * 2;
|
|
160
|
+
const h = hex.slice(j, j + 2); // hexByte. slice is faster than substr
|
|
161
|
+
const b = Number.parseInt(h, 16); // byte, created from string part
|
|
162
|
+
if (Number.isNaN(b) || b < 0)
|
|
163
|
+
err('hex invalid 2'); // byte must be valid 0 <= byte < 256
|
|
164
|
+
arr[i] = b;
|
|
165
|
+
}
|
|
166
|
+
return arr;
|
|
167
|
+
};
|
|
168
|
+
const n2b_32LE = (num) => h2b(padh(num, 32 * 2)).reverse(); // number to bytes LE
|
|
169
|
+
const b2n_LE = (b) => BigInt('0x' + b2h(u8n(au8(b)).reverse())); // bytes LE to num
|
|
170
|
+
const concatB = (...arrs) => {
|
|
171
|
+
const r = u8n(arrs.reduce((sum, a) => sum + au8(a).length, 0)); // create u8a of summed length
|
|
172
|
+
let pad = 0; // walk through each array,
|
|
173
|
+
arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type
|
|
174
|
+
return r;
|
|
175
|
+
};
|
|
176
|
+
const invert = (num, md = P) => {
|
|
177
|
+
if (num === 0n || md <= 0n)
|
|
178
|
+
err('no inverse n=' + num + ' mod=' + md); // no neg exponent for now
|
|
179
|
+
let a = mod(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
|
|
180
|
+
while (a !== 0n) { // uses euclidean gcd algorithm
|
|
181
|
+
const q = b / a, r = b % a; // not constant-time
|
|
182
|
+
const m = x - u * q, n = y - v * q;
|
|
183
|
+
b = a, a = r, x = u, y = v, u = m, v = n;
|
|
184
|
+
}
|
|
185
|
+
return b === 1n ? mod(x, md) : err('no inverse'); // b is gcd at this point
|
|
186
|
+
};
|
|
187
|
+
const pow2 = (x, power) => {
|
|
188
|
+
let r = x;
|
|
189
|
+
while (power-- > 0n) {
|
|
190
|
+
r *= r;
|
|
191
|
+
r %= P;
|
|
192
|
+
}
|
|
193
|
+
return r;
|
|
194
|
+
};
|
|
195
|
+
const pow_2_252_3 = (x) => {
|
|
196
|
+
const x2 = (x * x) % P; // x^2, bits 1
|
|
197
|
+
const b2 = (x2 * x) % P; // x^3, bits 11
|
|
198
|
+
const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111
|
|
199
|
+
const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111
|
|
200
|
+
const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)
|
|
201
|
+
const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)
|
|
202
|
+
const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)
|
|
203
|
+
const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)
|
|
204
|
+
const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)
|
|
205
|
+
const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)
|
|
206
|
+
const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)
|
|
207
|
+
const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.
|
|
208
|
+
return { pow_p_5_8, b2 };
|
|
209
|
+
};
|
|
210
|
+
const RM1 = 19681161376707505956807079304988542015446066515923890162744021073123829784752n; // √-1
|
|
211
|
+
const uvRatio = (u, v) => {
|
|
212
|
+
const v3 = mod(v * v * v); // v³
|
|
213
|
+
const v7 = mod(v3 * v3 * v); // v⁷
|
|
214
|
+
const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8
|
|
215
|
+
let x = mod(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8
|
|
216
|
+
const vx2 = mod(v * x * x); // vx²
|
|
217
|
+
const root1 = x; // First root candidate
|
|
218
|
+
const root2 = mod(x * RM1); // Second root candidate; RM1 is √-1
|
|
219
|
+
const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root
|
|
220
|
+
const useRoot2 = vx2 === mod(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4)
|
|
221
|
+
const noRoot = vx2 === mod(-u * RM1); // There is no valid root, vx² = -u√-1
|
|
222
|
+
if (useRoot1)
|
|
223
|
+
x = root1;
|
|
224
|
+
if (useRoot2 || noRoot)
|
|
225
|
+
x = root2; // We return root2 anyway, for const-time
|
|
226
|
+
if ((mod(x) & 1n) === 1n)
|
|
227
|
+
x = mod(-x); // edIsNegative
|
|
228
|
+
return { isValid: useRoot1 || useRoot2, value: x };
|
|
229
|
+
};
|
|
230
|
+
const modL_LE = (hash) => mod(b2n_LE(hash), N); // modulo L; but little-endian
|
|
231
|
+
let _shaS;
|
|
232
|
+
const sha512a = (...m) => etc.sha512Async(...m); // Async SHA512
|
|
233
|
+
const sha512s = (...m) => // Sync SHA512, not set by default
|
|
234
|
+
typeof _shaS === 'function' ? _shaS(...m) : err('etc.sha512Sync not set');
|
|
235
|
+
const hash2extK = (hashed) => {
|
|
236
|
+
const head = hashed.slice(0, 32); // slice creates a copy, unlike subarray
|
|
237
|
+
head[0] &= 248; // Clamp bits: 0b1111_1000,
|
|
238
|
+
head[31] &= 127; // 0b0111_1111,
|
|
239
|
+
head[31] |= 64; // 0b0100_0000
|
|
240
|
+
const prefix = hashed.slice(32, 64); // private key "prefix"
|
|
241
|
+
const scalar = modL_LE(head); // modular division over curve order
|
|
242
|
+
const point = G.mul(scalar); // public key point
|
|
243
|
+
const pointBytes = point.toRawBytes(); // point serialized to Uint8Array
|
|
244
|
+
return { head, prefix, scalar, point, pointBytes };
|
|
245
|
+
};
|
|
246
|
+
// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.
|
|
247
|
+
const getExtendedPublicKeyAsync = (priv) => sha512a(toU8(priv, 32)).then(hash2extK);
|
|
248
|
+
const getExtendedPublicKey = (priv) => hash2extK(sha512s(toU8(priv, 32)));
|
|
249
|
+
const getPublicKeyAsync = (priv) => getExtendedPublicKeyAsync(priv).then(p => p.pointBytes);
|
|
250
|
+
const getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;
|
|
251
|
+
function hashFinish(asynchronous, res) {
|
|
252
|
+
if (asynchronous)
|
|
253
|
+
return sha512a(res.hashable).then(res.finish);
|
|
254
|
+
return res.finish(sha512s(res.hashable));
|
|
255
|
+
}
|
|
256
|
+
const _sign = (e, rBytes, msg) => {
|
|
257
|
+
const { pointBytes: P, scalar: s } = e;
|
|
258
|
+
const r = modL_LE(rBytes); // r was created outside, reduce it modulo L
|
|
259
|
+
const R = G.mul(r).toRawBytes(); // R = [r]B
|
|
260
|
+
const hashable = concatB(R, P, msg); // dom2(F, C) || R || A || PH(M)
|
|
261
|
+
const finish = (hashed) => {
|
|
262
|
+
const S = mod(r + modL_LE(hashed) * s, N); // S = (r + k * s) mod L; 0 <= s < l
|
|
263
|
+
return au8(concatB(R, n2b_32LE(S)), 64); // 64-byte sig: 32b R.x + 32b LE(S)
|
|
264
|
+
};
|
|
265
|
+
return { hashable, finish };
|
|
266
|
+
};
|
|
267
|
+
const signAsync = async (msg, privKey) => {
|
|
268
|
+
const m = toU8(msg); // RFC8032 5.1.6: sign msg with key async
|
|
269
|
+
const e = await getExtendedPublicKeyAsync(privKey); // pub,prfx
|
|
270
|
+
const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))
|
|
271
|
+
return hashFinish(true, _sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature
|
|
272
|
+
};
|
|
273
|
+
const sign = (msg, privKey) => {
|
|
274
|
+
const m = toU8(msg); // RFC8032 5.1.6: sign msg with key sync
|
|
275
|
+
const e = getExtendedPublicKey(privKey); // pub,prfx
|
|
276
|
+
const rBytes = sha512s(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))
|
|
277
|
+
return hashFinish(false, _sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature
|
|
278
|
+
};
|
|
279
|
+
const dvo = { zip215: true };
|
|
280
|
+
const _verify = (sig, msg, pub, opts = dvo) => {
|
|
281
|
+
msg = toU8(msg); // Message hex str/Bytes
|
|
282
|
+
sig = toU8(sig, 64); // Signature hex str/Bytes, must be 64 bytes
|
|
283
|
+
const { zip215 } = opts; // switch between zip215 and rfc8032 verif
|
|
284
|
+
let A, R, s, SB, hashable = new Uint8Array();
|
|
285
|
+
try {
|
|
286
|
+
A = Point.fromHex(pub, zip215); // public key A decoded
|
|
287
|
+
R = Point.fromHex(sig.slice(0, 32), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P
|
|
288
|
+
s = b2n_LE(sig.slice(32, 64)); // Decode second half as an integer S
|
|
289
|
+
SB = G.mul(s, false); // in the range 0 <= s < L
|
|
290
|
+
hashable = concatB(R.toRawBytes(), A.toRawBytes(), msg); // dom2(F, C) || R || A || PH(M)
|
|
291
|
+
}
|
|
292
|
+
catch (error) { }
|
|
293
|
+
const finish = (hashed) => {
|
|
294
|
+
if (SB == null)
|
|
295
|
+
return false; // false if try-catch catched an error
|
|
296
|
+
if (!zip215 && A.isSmallOrder())
|
|
297
|
+
return false; // false for SBS: Strongly Binding Signature
|
|
298
|
+
const k = modL_LE(hashed); // decode in little-endian, modulo L
|
|
299
|
+
const RkA = R.add(A.mul(k, false)); // [8]R + [8][k]A'
|
|
300
|
+
return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'
|
|
301
|
+
};
|
|
302
|
+
return { hashable, finish };
|
|
303
|
+
};
|
|
304
|
+
// RFC8032 5.1.7: verification async, sync
|
|
305
|
+
const verifyAsync = async (s, m, p, opts = dvo) => hashFinish(true, _verify(s, m, p, opts));
|
|
306
|
+
const verify = (s, m, p, opts = dvo) => hashFinish(false, _verify(s, m, p, opts));
|
|
307
|
+
const cr = () => // We support: 1) browsers 2) node.js 19+
|
|
308
|
+
typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
|
|
309
|
+
const etc = {
|
|
310
|
+
bytesToHex: b2h, hexToBytes: h2b, concatBytes: concatB,
|
|
311
|
+
mod, invert,
|
|
312
|
+
randomBytes: (len = 32) => {
|
|
313
|
+
const crypto = cr(); // Can be shimmed in node.js <= 18 to prevent error:
|
|
314
|
+
// import { webcrypto } from 'node:crypto';
|
|
315
|
+
// if (!globalThis.crypto) globalThis.crypto = webcrypto;
|
|
316
|
+
if (!crypto || !crypto.getRandomValues)
|
|
317
|
+
err('crypto.getRandomValues must be defined');
|
|
318
|
+
return crypto.getRandomValues(u8n(len));
|
|
319
|
+
},
|
|
320
|
+
sha512Async: async (...messages) => {
|
|
321
|
+
const crypto = cr();
|
|
322
|
+
if (!crypto || !crypto.subtle)
|
|
323
|
+
err('crypto.subtle or etc.sha512Async must be defined');
|
|
324
|
+
const m = concatB(...messages);
|
|
325
|
+
return u8n(await crypto.subtle.digest('SHA-512', m.buffer));
|
|
326
|
+
},
|
|
327
|
+
sha512Sync: undefined, // Actual logic below
|
|
328
|
+
};
|
|
329
|
+
Object.defineProperties(etc, { sha512Sync: {
|
|
330
|
+
configurable: false, get() { return _shaS; }, set(f) { if (!_shaS)
|
|
331
|
+
_shaS = f; },
|
|
332
|
+
} });
|
|
333
|
+
const utils = {
|
|
334
|
+
getExtendedPublicKeyAsync, getExtendedPublicKey,
|
|
335
|
+
randomPrivateKey: () => etc.randomBytes(32),
|
|
336
|
+
precompute(w = 8, p = G) { p.multiply(3n); w; return p; }, // no-op
|
|
337
|
+
};
|
|
338
|
+
const W = 8; // Precomputes-related code. W = window size
|
|
339
|
+
const precompute = () => {
|
|
340
|
+
const points = []; // 10x sign(), 2x verify(). To achieve this,
|
|
341
|
+
const windows = 256 / W + 1; // app needs to spend 40ms+ to calculate
|
|
342
|
+
let p = G, b = p; // a lot of points related to base point G.
|
|
343
|
+
for (let w = 0; w < windows; w++) { // Points are stored in array and used
|
|
344
|
+
b = p; // any time Gx multiplication is done.
|
|
345
|
+
points.push(b); // They consume 16-32 MiB of RAM.
|
|
346
|
+
for (let i = 1; i < 2 ** (W - 1); i++) {
|
|
347
|
+
b = b.add(p);
|
|
348
|
+
points.push(b);
|
|
349
|
+
}
|
|
350
|
+
p = b.double(); // Precomputes don't speed-up getSharedKey,
|
|
351
|
+
} // which multiplies user point by scalar,
|
|
352
|
+
return points; // when precomputes are using base point
|
|
353
|
+
};
|
|
354
|
+
let Gpows = undefined; // precomputes for base point G
|
|
355
|
+
const wNAF = (n) => {
|
|
356
|
+
// Compared to other point mult methods,
|
|
357
|
+
const comp = Gpows || (Gpows = precompute()); // stores 2x less points using subtraction
|
|
358
|
+
const neg = (cnd, p) => { let n = p.negate(); return cnd ? n : p; }; // negate
|
|
359
|
+
let p = I, f = G; // f must be G, or could become I in the end
|
|
360
|
+
const windows = 1 + 256 / W; // W=8 17 windows
|
|
361
|
+
const wsize = 2 ** (W - 1); // W=8 128 window size
|
|
362
|
+
const mask = BigInt(2 ** W - 1); // W=8 will create mask 0b11111111
|
|
363
|
+
const maxNum = 2 ** W; // W=8 256
|
|
364
|
+
const shiftBy = BigInt(W); // W=8 8
|
|
365
|
+
for (let w = 0; w < windows; w++) {
|
|
366
|
+
const off = w * wsize;
|
|
367
|
+
let wbits = Number(n & mask); // extract W bits.
|
|
368
|
+
n >>= shiftBy; // shift number by W bits.
|
|
369
|
+
if (wbits > wsize) {
|
|
370
|
+
wbits -= maxNum;
|
|
371
|
+
n += 1n;
|
|
372
|
+
} // split if bits > max: +224 => 256-32
|
|
373
|
+
const off1 = off, off2 = off + Math.abs(wbits) - 1; // offsets, evaluate both
|
|
374
|
+
const cnd1 = w % 2 !== 0, cnd2 = wbits < 0; // conditions, evaluate both
|
|
375
|
+
if (wbits === 0) {
|
|
376
|
+
f = f.add(neg(cnd1, comp[off1])); // bits are 0: add garbage to fake point
|
|
377
|
+
}
|
|
378
|
+
else { // ^ can't add off2, off2 = I
|
|
379
|
+
p = p.add(neg(cnd2, comp[off2])); // bits are 1: add to result point
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return { p, f }; // return both real and fake points for JIT
|
|
383
|
+
}; // !! you can disable precomputes by commenting-out call of the wNAF() inside Point#mul()
|
|
384
|
+
export { getPublicKey, getPublicKeyAsync, sign, verify, // Remove the export to easily use in REPL
|
|
385
|
+
signAsync, verifyAsync, CURVE, etc, utils, Point as ExtendedPoint }; // envs like browser console
|