turtb 0.5.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.
Files changed (70) hide show
  1. package/LICENSE +661 -0
  2. package/bin/turtb.js +259 -0
  3. package/lib/display/h.js +204 -0
  4. package/lib/display/helpers.js +78 -0
  5. package/lib/display/render.js +177 -0
  6. package/lib/display/shapes.js +91 -0
  7. package/lib/turtle/Signer.js +120 -0
  8. package/lib/turtle/Signer.test.js +38 -0
  9. package/lib/turtle/TurtleBranch.js +147 -0
  10. package/lib/turtle/TurtleBranch.test.js +89 -0
  11. package/lib/turtle/TurtleDictionary.js +157 -0
  12. package/lib/turtle/TurtleDictionary.test.js +331 -0
  13. package/lib/turtle/U8aTurtle.js +203 -0
  14. package/lib/turtle/U8aTurtle.test.js +60 -0
  15. package/lib/turtle/Workspace.js +62 -0
  16. package/lib/turtle/Workspace.test.js +63 -0
  17. package/lib/turtle/codecs/CodecType.js +36 -0
  18. package/lib/turtle/codecs/CodecTypeVersion.js +37 -0
  19. package/lib/turtle/codecs/Commit.js +10 -0
  20. package/lib/turtle/codecs/CompositeCodec.js +86 -0
  21. package/lib/turtle/codecs/TreeNode.js +38 -0
  22. package/lib/turtle/codecs/codec.js +441 -0
  23. package/lib/turtle/connections/AbstractUpdater.js +176 -0
  24. package/lib/turtle/connections/TurtleBranchMultiplexer.js +102 -0
  25. package/lib/turtle/connections/TurtleBranchMultiplexer.test.js +26 -0
  26. package/lib/turtle/connections/TurtleBranchUpdater.js +47 -0
  27. package/lib/turtle/connections/TurtleDB.js +165 -0
  28. package/lib/turtle/connections/TurtleDB.test.js +45 -0
  29. package/lib/turtle/connections/TurtleTalker.js +34 -0
  30. package/lib/turtle/connections/TurtleTalker.test.js +101 -0
  31. package/lib/turtle/utils.js +192 -0
  32. package/lib/turtle/utils.test.js +158 -0
  33. package/lib/utils/Assert.js +115 -0
  34. package/lib/utils/NestedSet.js +68 -0
  35. package/lib/utils/NestedSet.test.js +30 -0
  36. package/lib/utils/OWN_KEYS.js +1 -0
  37. package/lib/utils/Recaller.js +175 -0
  38. package/lib/utils/Recaller.test.js +75 -0
  39. package/lib/utils/TestRunner.js +200 -0
  40. package/lib/utils/TestRunner.test.js +144 -0
  41. package/lib/utils/TestRunnerConstants.js +13 -0
  42. package/lib/utils/combineUint8ArrayLikes.js +18 -0
  43. package/lib/utils/combineUint8Arrays.js +23 -0
  44. package/lib/utils/components.js +88 -0
  45. package/lib/utils/crypto.js +17 -0
  46. package/lib/utils/deepEqual.js +16 -0
  47. package/lib/utils/deepEqual.test.js +27 -0
  48. package/lib/utils/fileTransformer.js +16 -0
  49. package/lib/utils/handleRedirect.js +93 -0
  50. package/lib/utils/logger.js +24 -0
  51. package/lib/utils/nextTick.js +47 -0
  52. package/lib/utils/noble-secp256k1.js +602 -0
  53. package/lib/utils/proxyWithRecaller.js +51 -0
  54. package/lib/utils/toCombinedVersion.js +14 -0
  55. package/lib/utils/toSubVersions.js +14 -0
  56. package/lib/utils/toVersionCount.js +5 -0
  57. package/lib/utils/webSocketMuxFactory.js +123 -0
  58. package/lib/utils/zabacaba.js +25 -0
  59. package/package.json +24 -0
  60. package/src/ArchiveUpdater.js +99 -0
  61. package/src/S3Updater.js +99 -0
  62. package/src/archiveSync.js +28 -0
  63. package/src/fileSync.js +155 -0
  64. package/src/getExistenceLength.js +19 -0
  65. package/src/manageCert.js +36 -0
  66. package/src/originSync.js +75 -0
  67. package/src/outletSync.js +50 -0
  68. package/src/proxyFolder.js +195 -0
  69. package/src/s3Sync.js +32 -0
  70. package/src/webSync.js +101 -0
@@ -0,0 +1,602 @@
1
+ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
2
+ /**
3
+ * 4KB JS implementation of secp256k1 signatures & ECDH. Compliant with RFC6979.
4
+ * @module
5
+ */
6
+ const B256 = 2n ** 256n;
7
+ const P = B256 - 0x1000003d1n; // curve's field prime
8
+ const N = B256 - 0x14551231950b75fc4402da1732fc9bebfn; // curve (group) order
9
+ const Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n; // base point x
10
+ const Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n; // base point y
11
+ /**
12
+ * secp256k1 curve parameters. Equation is x³ + ax + b.
13
+ * Gx and Gy are generator coordinates. p is field order, n is group order.
14
+ */
15
+ const CURVE = {
16
+ p: P, n: N, a: 0n, b: 7n, Gx, Gy
17
+ }; // exported variables incl. a, b
18
+ const fLen = 32; // field / group byte length
19
+ const curve = (x) => M(M(x * x) * x + CURVE.b); // x³ + ax + b weierstrass formula; a=0
20
+ const err = (m = '') => { throw new Error(m); }; // error helper, messes-up stack trace
21
+ const isB = (n) => typeof n === 'bigint'; // is big integer
22
+ const isS = (s) => typeof s === 'string'; // is string
23
+ const fe = (n) => isB(n) && 0n < n && n < P; // is field element (invertible)
24
+ const ge = (n) => isB(n) && 0n < n && n < N; // is group element
25
+ const isu8 = (a) => (a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array'));
26
+ const au8 = (a, l) => // assert is Uint8Array (of specific length)
27
+ !isu8(a) || (typeof l === 'number' && l > 0 && a.length !== l) ?
28
+ err('Uint8Array expected') : a;
29
+ const u8n = (data) => new Uint8Array(data); // creates Uint8Array
30
+ const toU8 = (a, len) => au8(isS(a) ? h2b(a) : u8n(au8(a)), len); // norm(hex/u8a) to u8a
31
+ const M = (a, b = P) => {
32
+ const r = a % b;
33
+ return r >= 0n ? r : b + r;
34
+ };
35
+ const aPoint = (p) => (p instanceof Point ? p : err('Point expected')); // is 3d point
36
+ /** Point in 3d xyz projective coordinates. 3d takes less inversions than 2d. */
37
+ class Point {
38
+ constructor(px, py, pz) {
39
+ this.px = px;
40
+ this.py = py;
41
+ this.pz = pz;
42
+ Object.freeze(this);
43
+ }
44
+ /** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */
45
+ static fromAffine(p) {
46
+ return ((p.x === 0n) && (p.y === 0n)) ? I : new Point(p.x, p.y, 1n);
47
+ }
48
+ /** Convert Uint8Array or hex string to Point. */
49
+ static fromHex(hex) {
50
+ hex = toU8(hex); // convert hex string to Uint8Array
51
+ let p = undefined;
52
+ const head = hex[0], tail = hex.subarray(1); // first byte is prefix, rest is data
53
+ const x = slc(tail, 0, fLen), len = hex.length; // next 32 bytes are x coordinate
54
+ if (len === 33 && [0x02, 0x03].includes(head)) { // compressed points: 33b, start
55
+ if (!fe(x))
56
+ err('Point hex invalid: x not FE'); // with byte 0x02 or 0x03. Check if 0<x<P
57
+ let y = sqrt(curve(x)); // x³ + ax + b is right side of equation
58
+ const isYOdd = (y & 1n) === 1n; // y² is equivalent left-side. Calculate y²:
59
+ const headOdd = (head & 1) === 1; // y = √y²; there are two solutions: y, -y
60
+ if (headOdd !== isYOdd)
61
+ y = M(-y); // determine proper solution
62
+ p = new Point(x, y, 1n); // create point
63
+ } // Uncompressed points: 65b, start with 0x04
64
+ if (len === 65 && head === 0x04)
65
+ p = new Point(x, slc(tail, fLen, 2 * fLen), 1n);
66
+ return p ? p.ok() : err('Point invalid: not on curve'); // Verify the result
67
+ }
68
+ /** Create point from a private key. */
69
+ static fromPrivateKey(k) { return G.mul(toPriv(k)); }
70
+ get x() { return this.aff().x; } // .x, .y will call expensive toAffine:
71
+ get y() { return this.aff().y; } // should be used with care.
72
+ /** Equality check: compare points P&Q. */
73
+ equals(other) {
74
+ const { px: X1, py: Y1, pz: Z1 } = this;
75
+ const { px: X2, py: Y2, pz: Z2 } = aPoint(other); // isPoint() checks class equality
76
+ const X1Z2 = M(X1 * Z2), X2Z1 = M(X2 * Z1);
77
+ const Y1Z2 = M(Y1 * Z2), Y2Z1 = M(Y2 * Z1);
78
+ return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
79
+ }
80
+ /** Flip point over y coordinate. */
81
+ negate() { return new Point(this.px, M(-this.py), this.pz); }
82
+ /** Point doubling: P+P, complete formula. */
83
+ double() { return this.add(this); }
84
+ /**
85
+ * Point addition: P+Q, complete, exception-free formula
86
+ * (Renes-Costello-Batina, algo 1 of [2015/1060](https://eprint.iacr.org/2015/1060)).
87
+ * Cost: 12M + 0S + 3*a + 3*b3 + 23add.
88
+ */
89
+ add(other) {
90
+ const { px: X1, py: Y1, pz: Z1 } = this;
91
+ const { px: X2, py: Y2, pz: Z2 } = aPoint(other);
92
+ const { a, b } = CURVE;
93
+ let X3 = 0n, Y3 = 0n, Z3 = 0n;
94
+ const b3 = M(b * 3n);
95
+ let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
96
+ let t4 = M(X2 + Y2); // step 5
97
+ t3 = M(t3 * t4);
98
+ t4 = M(t0 + t1);
99
+ t3 = M(t3 - t4);
100
+ t4 = M(X1 + Z1);
101
+ let t5 = M(X2 + Z2); // step 10
102
+ t4 = M(t4 * t5);
103
+ t5 = M(t0 + t2);
104
+ t4 = M(t4 - t5);
105
+ t5 = M(Y1 + Z1);
106
+ X3 = M(Y2 + Z2); // step 15
107
+ t5 = M(t5 * X3);
108
+ X3 = M(t1 + t2);
109
+ t5 = M(t5 - X3);
110
+ Z3 = M(a * t4);
111
+ X3 = M(b3 * t2); // step 20
112
+ Z3 = M(X3 + Z3);
113
+ X3 = M(t1 - Z3);
114
+ Z3 = M(t1 + Z3);
115
+ Y3 = M(X3 * Z3);
116
+ t1 = M(t0 + t0); // step 25
117
+ t1 = M(t1 + t0);
118
+ t2 = M(a * t2);
119
+ t4 = M(b3 * t4);
120
+ t1 = M(t1 + t2);
121
+ t2 = M(t0 - t2); // step 30
122
+ t2 = M(a * t2);
123
+ t4 = M(t4 + t2);
124
+ t0 = M(t1 * t4);
125
+ Y3 = M(Y3 + t0);
126
+ t0 = M(t5 * t4); // step 35
127
+ X3 = M(t3 * X3);
128
+ X3 = M(X3 - t0);
129
+ t0 = M(t3 * t1);
130
+ Z3 = M(t5 * Z3);
131
+ Z3 = M(Z3 + t0); // step 40
132
+ return new Point(X3, Y3, Z3);
133
+ }
134
+ mul(n, safe = true) {
135
+ if (!safe && n === 0n)
136
+ return I; // in unsafe mode, allow zero
137
+ if (!ge(n))
138
+ err('scalar invalid'); // must be 0 < n < CURVE.n
139
+ if (this.equals(G))
140
+ return wNAF(n).p; // use precomputes for base point
141
+ let p = I, f = G; // init result point & fake point
142
+ for (let d = this; n > 0n; d = d.double(), n >>= 1n) { // double-and-add ladder
143
+ if (n & 1n)
144
+ p = p.add(d); // if bit is present, add to point
145
+ else if (safe)
146
+ f = f.add(d); // if not, add to fake for timing safety
147
+ }
148
+ return p;
149
+ }
150
+ mulAddQUns(R, u1, u2) {
151
+ return this.mul(u1, false).add(R.mul(u2, false)).ok(); // Unsafe: do NOT use for stuff related
152
+ } // to private keys. Doesn't use Shamir trick
153
+ /** Convert point to 2d xy affine point. (x, y, z) ∋ (x=x/z, y=y/z) */
154
+ toAffine() {
155
+ const { px: x, py: y, pz: z } = this;
156
+ if (this.equals(I))
157
+ return { x: 0n, y: 0n }; // fast-path for zero point
158
+ if (z === 1n)
159
+ return { x, y }; // if z is 1, pass affine coordinates as-is
160
+ const iz = inv(z, P); // z^-1: invert z
161
+ if (M(z * iz) !== 1n)
162
+ err('inverse invalid'); // (z * z^-1) must be 1, otherwise bad math
163
+ return { x: M(x * iz), y: M(y * iz) }; // x = x*z^-1; y = y*z^-1
164
+ }
165
+ /** Checks if the point is valid and on-curve. */
166
+ assertValidity() {
167
+ const { x, y } = this.aff(); // convert to 2d xy affine point.
168
+ if (!fe(x) || !fe(y))
169
+ err('Point invalid: x or y'); // x and y must be in range 0 < n < P
170
+ return M(y * y) === curve(x) ? // y² = x³ + ax + b, must be equal
171
+ this : err('Point invalid: not on curve');
172
+ }
173
+ multiply(n) { return this.mul(n); } // Aliases to compress code
174
+ aff() { return this.toAffine(); }
175
+ ok() { return this.assertValidity(); }
176
+ toHex(isCompressed = true) {
177
+ const { x, y } = this.aff(); // convert to 2d xy affine point
178
+ const head = isCompressed ? ((y & 1n) === 0n ? '02' : '03') : '04'; // 0x02, 0x03, 0x04 prefix
179
+ return head + n2h(x) + (isCompressed ? '' : n2h(y)); // prefix||x and ||y
180
+ }
181
+ toRawBytes(isCompressed = true) {
182
+ return h2b(this.toHex(isCompressed)); // re-use toHex(), convert hex to bytes
183
+ }
184
+ }
185
+ /** Generator / base point */
186
+ Point.BASE = new Point(Gx, Gy, 1n);
187
+ /** Identity / zero point */
188
+ Point.ZERO = new Point(0n, 1n, 0n);
189
+ const { BASE: G, ZERO: I } = Point; // Generator, identity points
190
+ const padh = (n, pad) => n.toString(16).padStart(pad, '0');
191
+ const b2h = (b) => Array.from(au8(b)).map(e => padh(e, 2)).join(''); // bytes to hex
192
+ const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters
193
+ const _ch = (ch) => {
194
+ if (ch >= C._0 && ch <= C._9)
195
+ return ch - C._0; // '2' => 50-48
196
+ if (ch >= C.A && ch <= C.F)
197
+ return ch - (C.A - 10); // 'B' => 66-(65-10)
198
+ if (ch >= C.a && ch <= C.f)
199
+ return ch - (C.a - 10); // 'b' => 98-(97-10)
200
+ return;
201
+ };
202
+ const h2b = (hex) => {
203
+ const e = 'hex invalid';
204
+ if (!isS(hex))
205
+ return err(e);
206
+ const hl = hex.length, al = hl / 2;
207
+ if (hl % 2)
208
+ return err(e);
209
+ const array = u8n(al);
210
+ for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { // treat each char as ASCII
211
+ const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
212
+ const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
213
+ if (n1 === undefined || n2 === undefined)
214
+ return err(e);
215
+ array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
216
+ }
217
+ return array;
218
+ };
219
+ const b2n = (b) => BigInt('0x' + (b2h(b) || '0')); // bytes to number
220
+ const slc = (b, from, to) => b2n(b.slice(from, to)); // slice bytes num
221
+ const n2b = (num) => {
222
+ return isB(num) && num >= 0n && num < B256 ? h2b(padh(num, 2 * fLen)) : err('bigint expected');
223
+ };
224
+ const n2h = (num) => b2h(n2b(num)); // number to 32b hex
225
+ const concatB = (...arrs) => {
226
+ const r = u8n(arrs.reduce((sum, a) => sum + au8(a).length, 0)); // create u8a of summed length
227
+ let pad = 0; // walk through each array,
228
+ arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type
229
+ return r;
230
+ };
231
+ const inv = (num, md) => {
232
+ if (num === 0n || md <= 0n)
233
+ err('no inverse n=' + num + ' mod=' + md); // no neg exponent for now
234
+ let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
235
+ while (a !== 0n) { // uses euclidean gcd algorithm
236
+ const q = b / a, r = b % a; // not constant-time
237
+ const m = x - u * q, n = y - v * q;
238
+ b = a, a = r, x = u, y = v, u = m, v = n;
239
+ }
240
+ return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point
241
+ };
242
+ const sqrt = (n) => {
243
+ let r = 1n; // So, a special, fast case. Paper: "Square Roots from 1;24,51,10 to Dan Shanks".
244
+ for (let num = n, e = (P + 1n) / 4n; e > 0n; e >>= 1n) { // powMod: modular exponentiation.
245
+ if (e & 1n)
246
+ r = (r * num) % P; // Uses exponentiation by squaring.
247
+ num = (num * num) % P; // Not constant-time.
248
+ }
249
+ return M(r * r) === n ? r : err('sqrt invalid'); // check if result is valid
250
+ };
251
+ const toPriv = (p) => {
252
+ if (!isB(p))
253
+ p = b2n(toU8(p, fLen)); // convert to bigint when bytes
254
+ return ge(p) ? p : err('private key invalid 3'); // check if bigint is in range
255
+ };
256
+ const high = (n) => n > (N >> 1n); // if a number is bigger than CURVE.n/2
257
+ /** Creates 33/65-byte public key from 32-byte private key. */
258
+ const getPublicKey = (privKey, isCompressed = true) => {
259
+ return Point.fromPrivateKey(privKey).toRawBytes(isCompressed);
260
+ };
261
+ /** ECDSA Signature class. Supports only compact 64-byte representation, not DER. */
262
+ class Signature {
263
+ constructor(r, s, recovery) {
264
+ this.r = r;
265
+ this.s = s;
266
+ this.recovery = recovery;
267
+ this.assertValidity(); // recovery bit is optional when
268
+ } // constructed outside.
269
+ /** Create signature from 64b compact (r || s) representation. */
270
+ static fromCompact(hex) {
271
+ hex = toU8(hex, 64); // compact repr is (32b r)||(32b s)
272
+ return new Signature(slc(hex, 0, fLen), slc(hex, fLen, 2 * fLen));
273
+ }
274
+ assertValidity() { return ge(this.r) && ge(this.s) ? this : err(); } // 0 < r or s < CURVE.n
275
+ /** Create new signature, with added recovery bit. */
276
+ addRecoveryBit(rec) {
277
+ return new Signature(this.r, this.s, rec);
278
+ }
279
+ hasHighS() { return high(this.s); }
280
+ normalizeS() {
281
+ return high(this.s) ? new Signature(this.r, M(-this.s, N), this.recovery) : this;
282
+ }
283
+ /** ECDSA public key recovery. Requires msg hash and recovery id. */
284
+ recoverPublicKey(msgh) {
285
+ const { r, s, recovery: rec } = this; // secg.org/sec1-v2.pdf 4.1.6
286
+ if (![0, 1, 2, 3].includes(rec))
287
+ err('recovery id invalid'); // check recovery id
288
+ const h = bits2int_modN(toU8(msgh, fLen)); // Truncate hash
289
+ const radj = rec === 2 || rec === 3 ? r + N : r; // If rec was 2 or 3, q.x is bigger than n
290
+ if (radj >= P)
291
+ err('q.x invalid'); // ensure q.x is still a field element
292
+ const head = (rec & 1) === 0 ? '02' : '03'; // head is 0x02 or 0x03
293
+ const R = Point.fromHex(head + n2h(radj)); // concat head + hex repr of r
294
+ const ir = inv(radj, N); // r^-1
295
+ const u1 = M(-h * ir, N); // -hr^-1
296
+ const u2 = M(s * ir, N); // sr^-1
297
+ return G.mulAddQUns(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
298
+ }
299
+ /** Uint8Array 64b compact (r || s) representation. */
300
+ toCompactRawBytes() { return h2b(this.toCompactHex()); }
301
+ /** Hex string 64b compact (r || s) representation. */
302
+ toCompactHex() { return n2h(this.r) + n2h(this.s); }
303
+ }
304
+ const bits2int = (bytes) => {
305
+ const delta = bytes.length * 8 - 256; // RFC suggests optional truncating via bits2octets
306
+ if (delta > 1024)
307
+ err('msg invalid'); // our CUSTOM check, "just-in-case"
308
+ const num = b2n(bytes); // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which
309
+ return delta > 0 ? num >> BigInt(delta) : num; // matches bits2int. bits2int can produce res>N.
310
+ };
311
+ const bits2int_modN = (bytes) => {
312
+ return M(bits2int(bytes), N); // with 0: BAD for trunc as per RFC vectors
313
+ };
314
+ const i2o = (num) => n2b(num); // int to octets
315
+ const cr = () => // We support: 1) browsers 2) node.js 19+ 3) deno, other envs with crypto
316
+ typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
317
+ let _hmacSync; // Can be redefined by use in utils; built-ins don't provide it
318
+ const optS = { lowS: true }; // opts for sign()
319
+ const optV = { lowS: true }; // standard opts for verify()
320
+ const prepSig = (msgh, priv, opts = optS) => {
321
+ if (['der', 'recovered', 'canonical'].some(k => k in opts))
322
+ err('option not supported'); // legacy opts
323
+ let { lowS } = opts; // generates low-s sigs by default
324
+ if (lowS == null)
325
+ lowS = true; // RFC6979 3.2: we skip step A
326
+ const h1i = bits2int_modN(toU8(msgh)); // msg bigint
327
+ const h1o = i2o(h1i); // msg octets
328
+ const d = toPriv(priv); // validate private key, convert to bigint
329
+ const seed = [i2o(d), h1o]; // Step D of RFC6979 3.2
330
+ let ent = opts.extraEntropy; // RFC6979 3.6: additional k' (optional)
331
+ if (ent) // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
332
+ seed.push(ent === true ? etc.randomBytes(fLen) : toU8(ent)); // true == fetch from CSPRNG
333
+ const m = h1i; // convert msg to bigint
334
+ const k2sig = (kBytes) => {
335
+ const k = bits2int(kBytes); // RFC6979 method.
336
+ if (!ge(k))
337
+ return; // Check 0 < k < CURVE.n
338
+ const ik = inv(k, N); // k^-1 mod n, NOT mod P
339
+ const q = G.mul(k).aff(); // q = Gk
340
+ const r = M(q.x, N); // r = q.x mod n
341
+ if (r === 0n)
342
+ return; // r=0 invalid
343
+ const s = M(ik * M(m + M(d * r, N), N), N); // s = k^-1(m + rd) mod n
344
+ if (s === 0n)
345
+ return; // s=0 invalid
346
+ let normS = s; // normalized S
347
+ let rec = (q.x === r ? 0 : 2) | Number(q.y & 1n); // recovery bit
348
+ if (lowS && high(s)) { // if lowS was passed, ensure s is always
349
+ normS = M(-s, N); // in the bottom half of CURVE.n
350
+ rec ^= 1;
351
+ }
352
+ return new Signature(r, normS, rec); // use normS, not s
353
+ };
354
+ return { seed: concatB(...seed), k2sig };
355
+ };
356
+ function hmacDrbg(asynchronous) {
357
+ let v = u8n(fLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
358
+ let k = u8n(fLen); // Steps B, C of RFC6979 3.2: set hashLen, in our case always same
359
+ let i = 0; // Iterations counter, will throw when over 1000
360
+ const reset = () => { v.fill(1); k.fill(0); i = 0; };
361
+ const _e = 'drbg: tried 1000 values';
362
+ if (asynchronous) { // asynchronous=true
363
+ const h = (...b) => etc.hmacSha256Async(k, v, ...b); // hmac(k)(v, ...values)
364
+ const reseed = async (seed = u8n()) => {
365
+ k = await h(u8n([0x00]), seed); // k = hmac(K || V || 0x00 || seed)
366
+ v = await h(); // v = hmac(K || V)
367
+ if (seed.length === 0)
368
+ return;
369
+ k = await h(u8n([0x01]), seed); // k = hmac(K || V || 0x01 || seed)
370
+ v = await h(); // v = hmac(K || V)
371
+ };
372
+ const gen = async () => {
373
+ if (i++ >= 1000)
374
+ err(_e);
375
+ v = await h(); // v = hmac(K || V)
376
+ return v;
377
+ };
378
+ return async (seed, pred) => {
379
+ reset(); // the returned fn, don't, it's: 1. slower (JIT). 2. unsafe (async race conditions)
380
+ await reseed(seed); // Steps D-G
381
+ let res = undefined; // Step H: grind until k is in [1..n-1]
382
+ while (!(res = pred(await gen())))
383
+ await reseed(); // test predicate until it returns ok
384
+ reset();
385
+ return res;
386
+ };
387
+ }
388
+ else {
389
+ const h = (...b) => {
390
+ const f = _hmacSync;
391
+ if (!f)
392
+ err('etc.hmacSha256Sync not set');
393
+ return f(k, v, ...b); // hmac(k)(v, ...values)
394
+ };
395
+ const reseed = (seed = u8n()) => {
396
+ k = h(u8n([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
397
+ v = h(); // v = hmac(k || v)
398
+ if (seed.length === 0)
399
+ return;
400
+ k = h(u8n([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
401
+ v = h(); // v = hmac(k || v)
402
+ };
403
+ const gen = () => {
404
+ if (i++ >= 1000)
405
+ err(_e);
406
+ v = h(); // v = hmac(k || v)
407
+ return v;
408
+ };
409
+ return (seed, pred) => {
410
+ reset();
411
+ reseed(seed); // Steps D-G
412
+ let res = undefined; // Step H: grind until k is in [1..n-1]
413
+ while (!(res = pred(gen())))
414
+ reseed(); // test predicate until it returns ok
415
+ reset();
416
+ return res;
417
+ };
418
+ }
419
+ }
420
+ ;
421
+ /** ECDSA signature generation. via secg.org/sec1-v2.pdf 4.1.2 + RFC6979 deterministic k. */
422
+ /**
423
+ * Sign a msg hash using secp256k1. Async.
424
+ * It is advised to use `extraEntropy: true` (from RFC6979 3.6) to prevent fault attacks.
425
+ * Worst case: if randomness source for extraEntropy is bad, it would be as secure as if
426
+ * the option has not been used.
427
+ * @param msgh - message HASH, not message itself e.g. sha256(message)
428
+ * @param priv - private key
429
+ * @param opts - `lowS: true` to prevent malleability (s >= CURVE.n/2), `extraEntropy: boolean | Hex` to improve sig security.
430
+ */
431
+ const signAsync = async (msgh, priv, opts = optS) => {
432
+ const { seed, k2sig } = prepSig(msgh, priv, opts); // Extract arguments for hmac-drbg
433
+ return hmacDrbg(true)(seed, k2sig); // Re-run drbg until k2sig returns ok
434
+ };
435
+ /**
436
+ * Sign a msg hash using secp256k1.
437
+ * It is advised to use `extraEntropy: true` (from RFC6979 3.6) to prevent fault attacks.
438
+ * Worst case: if randomness source for extraEntropy is bad, it would be as secure as if
439
+ * the option has not been used.
440
+ * @param msgh - message HASH, not message itself e.g. sha256(message)
441
+ * @param priv - private key
442
+ * @param opts - `lowS: true` to prevent malleability (s >= CURVE.n/2), `extraEntropy: boolean | Hex` to improve sig security.
443
+ * @example
444
+ * const sig = sign(sha256('hello'), privKey, { extraEntropy: true }).toCompactRawBytes();
445
+ */
446
+ const sign = (msgh, priv, opts = optS) => {
447
+ const { seed, k2sig } = prepSig(msgh, priv, opts); // Extract arguments for hmac-drbg
448
+ return hmacDrbg(false)(seed, k2sig); // Re-run drbg until k2sig returns ok
449
+ };
450
+ /**
451
+ * Verify a signature using secp256k1.
452
+ * @param sig - signature, 64-byte or Signature instance
453
+ * @param msgh - message HASH, not message itself e.g. sha256(message)
454
+ * @param pub - public key
455
+ * @param opts - { lowS: true } is default, prohibits s >= CURVE.n/2 to prevent malleability
456
+ */
457
+ const verify = (sig, msgh, pub, opts = optV) => {
458
+ let { lowS } = opts; // ECDSA signature verification
459
+ if (lowS == null)
460
+ lowS = true; // Default lowS=true
461
+ if ('strict' in opts)
462
+ err('option not supported'); // legacy param
463
+ let sig_, h, P; // secg.org/sec1-v2.pdf 4.1.4
464
+ const rs = sig && typeof sig === 'object' && 'r' in sig; // Previous ver supported DER sigs. We
465
+ if (!rs && (toU8(sig).length !== 2 * fLen)) // throw error when DER is suspected now.
466
+ err('signature must be 64 bytes');
467
+ try {
468
+ sig_ = rs ? new Signature(sig.r, sig.s).assertValidity() : Signature.fromCompact(sig);
469
+ h = bits2int_modN(toU8(msgh)); // Truncate hash
470
+ P = pub instanceof Point ? pub.ok() : Point.fromHex(pub); // Validate public key
471
+ }
472
+ catch (e) {
473
+ return false;
474
+ } // Check sig for validity in both cases
475
+ if (!sig_)
476
+ return false;
477
+ const { r, s } = sig_;
478
+ if (lowS && high(s))
479
+ return false; // lowS bans sig.s >= CURVE.n/2
480
+ let R;
481
+ try {
482
+ const is = inv(s, N); // s^-1
483
+ const u1 = M(h * is, N); // u1 = hs^-1 mod n
484
+ const u2 = M(r * is, N); // u2 = rs^-1 mod n
485
+ R = G.mulAddQUns(P, u1, u2).aff(); // R = u1⋅G + u2⋅P
486
+ }
487
+ catch (error) {
488
+ return false;
489
+ }
490
+ if (!R)
491
+ return false; // stop if R is identity / zero point
492
+ const v = M(R.x, N); // R.x must be in N's field, not P's
493
+ return v === r; // mod(R.x, n) == r
494
+ };
495
+ /**
496
+ * Elliptic Curve Diffie-Hellman (ECDH) on secp256k1.
497
+ * Result is **NOT hashed**. Use hash on it if you need.
498
+ * @param privA private key A
499
+ * @param pubB public key B
500
+ * @param isCompressed 33-byte or 65-byte output
501
+ * @returns public key C
502
+ */
503
+ const getSharedSecret = (privA, pubB, isCompressed = true) => {
504
+ return Point.fromHex(pubB).mul(toPriv(privA)).toRawBytes(isCompressed); // ECDH
505
+ };
506
+ const hashToPrivateKey = (hash) => {
507
+ hash = toU8(hash); // produces private keys with modulo bias
508
+ if (hash.length < fLen + 8 || hash.length > 1024)
509
+ err('expected 40-1024b'); // being neglible.
510
+ const num = M(b2n(hash), N - 1n); // takes n+8 bytes
511
+ return n2b(num + 1n); // returns (hash mod n-1)+1
512
+ };
513
+ /** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */
514
+ const etc = {
515
+ hexToBytes: h2b,
516
+ bytesToHex: b2h,
517
+ concatBytes: concatB,
518
+ bytesToNumberBE: b2n,
519
+ numberToBytesBE: n2b,
520
+ mod: M,
521
+ invert: inv, // math utilities
522
+ hmacSha256Async: async (key, ...msgs) => {
523
+ const c = cr(); // async HMAC-SHA256, no sync built-in!
524
+ const s = c && c.subtle; // For React Native support, see README.
525
+ if (!s)
526
+ return err('etc.hmacSha256Async or crypto.subtle must be defined'); // Uses webcrypto built-in cryptography.
527
+ const k = await s.importKey('raw', key, { name: 'HMAC', hash: { name: 'SHA-256' } }, false, ['sign']);
528
+ return u8n(await s.sign('HMAC', k, concatB(...msgs)));
529
+ },
530
+ hmacSha256Sync: _hmacSync, // For TypeScript. Actual logic is below
531
+ hashToPrivateKey: hashToPrivateKey,
532
+ randomBytes: (len = 32) => {
533
+ const crypto = cr(); // Must be shimmed in node.js <= 18 to prevent error. See README.
534
+ if (!crypto || !crypto.getRandomValues)
535
+ err('crypto.getRandomValues must be defined');
536
+ return crypto.getRandomValues(u8n(len));
537
+ },
538
+ };
539
+ /** Curve-specific utilities for private keys. */
540
+ const utils = {
541
+ normPrivateKeyToScalar: toPriv,
542
+ isValidPrivateKey: (key) => { try {
543
+ return !!toPriv(key);
544
+ }
545
+ catch (e) {
546
+ return false;
547
+ } },
548
+ randomPrivateKey: () => hashToPrivateKey(etc.randomBytes(fLen + 16)), // FIPS 186 B.4.1.
549
+ precompute: (w = 8, p = G) => { p.multiply(3n); w; return p; }, // no-op
550
+ };
551
+ Object.defineProperties(etc, { hmacSha256Sync: {
552
+ configurable: false, get() { return _hmacSync; }, set(f) { if (!_hmacSync)
553
+ _hmacSync = f; },
554
+ } });
555
+ const W = 8; // Precomputes-related code. W = window size
556
+ const precompute = () => {
557
+ const points = []; // 10x sign(), 2x verify(). To achieve this,
558
+ const windows = 256 / W + 1; // app needs to spend 40ms+ to calculate
559
+ let p = G, b = p; // a lot of points related to base point G.
560
+ for (let w = 0; w < windows; w++) { // Points are stored in array and used
561
+ b = p; // any time Gx multiplication is done.
562
+ points.push(b); // They consume 16-32 MiB of RAM.
563
+ for (let i = 1; i < 2 ** (W - 1); i++) {
564
+ b = b.add(p);
565
+ points.push(b);
566
+ }
567
+ p = b.double(); // Precomputes don't speed-up getSharedKey,
568
+ } // which multiplies user point by scalar,
569
+ return points; // when precomputes are using base point
570
+ };
571
+ let Gpows = undefined; // precomputes for base point G
572
+ const wNAF = (n) => {
573
+ // Compared to other point mult methods,
574
+ const comp = Gpows || (Gpows = precompute()); // stores 2x less points using subtraction
575
+ const neg = (cnd, p) => { let n = p.negate(); return cnd ? n : p; }; // negate
576
+ let p = I, f = G; // f must be G, or could become I in the end
577
+ const windows = 1 + 256 / W; // W=8 17 windows
578
+ const wsize = 2 ** (W - 1); // W=8 128 window size
579
+ const mask = BigInt(2 ** W - 1); // W=8 will create mask 0b11111111
580
+ const maxNum = 2 ** W; // W=8 256
581
+ const shiftBy = BigInt(W); // W=8 8
582
+ for (let w = 0; w < windows; w++) {
583
+ const off = w * wsize;
584
+ let wbits = Number(n & mask); // extract W bits.
585
+ n >>= shiftBy; // shift number by W bits.
586
+ if (wbits > wsize) {
587
+ wbits -= maxNum;
588
+ n += 1n;
589
+ } // split if bits > max: +224 => 256-32
590
+ const off1 = off, off2 = off + Math.abs(wbits) - 1; // offsets, evaluate both
591
+ const cnd1 = w % 2 !== 0, cnd2 = wbits < 0; // conditions, evaluate both
592
+ if (wbits === 0) {
593
+ f = f.add(neg(cnd1, comp[off1])); // bits are 0: add garbage to fake point
594
+ }
595
+ else { // ^ can't add off2, off2 = I
596
+ p = p.add(neg(cnd2, comp[off2])); // bits are 1: add to result point
597
+ }
598
+ }
599
+ return { p, f }; // return both real and fake points for JIT
600
+ }; // !! you can disable precomputes by commenting-out call of the wNAF() inside Point#mul()
601
+ export { getPublicKey, sign, signAsync, verify, CURVE, // Remove the export to easily use in REPL
602
+ getSharedSecret, etc, utils, Point as ProjectivePoint, Signature }; // envs like browser console
@@ -0,0 +1,51 @@
1
+ import { OWN_KEYS } from './OWN_KEYS.js'
2
+ import { Recaller } from './Recaller.js'
3
+
4
+ /**
5
+ * @param {Object} target
6
+ * @param {Recaller} [recaller=new Recaller('<unnamed proxyWithRecaller>')]
7
+ * @param {string} [name=recaller.name]
8
+ */
9
+ export function proxyWithRecaller (
10
+ target,
11
+ recaller = new Recaller('<unnamed proxyWithRecaller>'),
12
+ name = recaller.name
13
+ ) {
14
+ if (!name && recaller) name = recaller.name
15
+ if (!target || typeof target !== 'object') throw new Error('proxyWithRecaller can only proxy objects')
16
+ return new Proxy(target, {
17
+ has: (target, propertyKey) => {
18
+ recaller.reportKeyAccess(target, propertyKey, 'get', name)
19
+ return Reflect.has(target, propertyKey)
20
+ },
21
+ get: (target, propertyKey) => {
22
+ recaller.reportKeyAccess(target, propertyKey, 'get', name)
23
+ if (propertyKey === 'length' && Array.isArray(target)) return target.length
24
+ return Reflect.get(target, propertyKey)
25
+ },
26
+ set: (target, propertyKey, value) => {
27
+ const length = target.length
28
+ if (value !== target[propertyKey]) {
29
+ recaller.reportKeyMutation(target, propertyKey, 'set', name)
30
+ }
31
+ if (!(propertyKey in target)) {
32
+ recaller.reportKeyMutation(target, OWN_KEYS, 'set', name)
33
+ }
34
+ const result = Reflect.set(target, propertyKey, value)
35
+ if (Array.isArray(target) && length !== target.length) {
36
+ recaller.reportKeyMutation(target, 'length', 'set', name)
37
+ }
38
+ return result
39
+ },
40
+ deleteProperty: (target, propertyKey) => {
41
+ if (propertyKey in target) {
42
+ recaller.reportKeyMutation(target, OWN_KEYS, 'delete', name)
43
+ }
44
+ return Reflect.deleteProperty(target, propertyKey)
45
+ },
46
+ ownKeys: target => {
47
+ recaller.reportKeyAccess(target, OWN_KEYS, 'ownKeys', name)
48
+ return Reflect.ownKeys(target)
49
+ }
50
+ })
51
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @param {Array.<number>} versionArrays
3
+ * @param {Array.<number>} versionArrayCounts
4
+ * @return number
5
+ */
6
+ export const toCombinedVersion = (versionArrays, versionArrayCounts) => {
7
+ if (versionArrays.length !== versionArrayCounts.length) throw new Error('versionArrays/versionArrayCounts mismatch')
8
+ let combinedVersion = 0
9
+ for (let i = 0; i < versionArrays.length; ++i) {
10
+ combinedVersion *= versionArrayCounts[i]
11
+ combinedVersion += versionArrays[i]
12
+ }
13
+ return combinedVersion
14
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @param {number} combinedVersion
3
+ * @param {Array.<number>} versionArrayCounts
4
+ * @return {Array.<number}
5
+ */
6
+ export const toSubVersions = (combinedVersion, versionArrayCounts) => {
7
+ const versionArrays = new Array(versionArrayCounts.length)
8
+ for (let i = versionArrays.length - 1; i >= 0; --i) {
9
+ const versionArrayCount = versionArrayCounts[i]
10
+ versionArrays[i] = combinedVersion % versionArrayCount
11
+ combinedVersion = Math.floor(combinedVersion / versionArrayCount)
12
+ }
13
+ return versionArrays
14
+ }