@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.
@@ -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