@bsv/sdk 1.1.23 → 1.1.25

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 (86) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/primitives/Curve.js +7 -7
  3. package/dist/cjs/src/primitives/Curve.js.map +1 -1
  4. package/dist/cjs/src/primitives/ECDSA.js +394 -71
  5. package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
  6. package/dist/cjs/src/primitives/Point.js +103 -23
  7. package/dist/cjs/src/primitives/Point.js.map +1 -1
  8. package/dist/cjs/src/primitives/TransactionSignature.js +4 -3
  9. package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
  10. package/dist/cjs/src/primitives/utils.js +14 -15
  11. package/dist/cjs/src/primitives/utils.js.map +1 -1
  12. package/dist/cjs/src/script/Spend.js +4 -4
  13. package/dist/cjs/src/script/Spend.js.map +1 -1
  14. package/dist/cjs/src/totp/totp.js +1 -1
  15. package/dist/cjs/src/totp/totp.js.map +1 -1
  16. package/dist/cjs/src/transaction/Beef.js +492 -0
  17. package/dist/cjs/src/transaction/Beef.js.map +1 -0
  18. package/dist/cjs/src/transaction/BeefParty.js +97 -0
  19. package/dist/cjs/src/transaction/BeefParty.js.map +1 -0
  20. package/dist/cjs/src/transaction/BeefTx.js +123 -0
  21. package/dist/cjs/src/transaction/BeefTx.js.map +1 -0
  22. package/dist/cjs/src/transaction/Transaction.js +81 -66
  23. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  24. package/dist/cjs/src/transaction/index.js +7 -1
  25. package/dist/cjs/src/transaction/index.js.map +1 -1
  26. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  27. package/dist/esm/src/primitives/Curve.js +7 -7
  28. package/dist/esm/src/primitives/Curve.js.map +1 -1
  29. package/dist/esm/src/primitives/ECDSA.js +394 -71
  30. package/dist/esm/src/primitives/ECDSA.js.map +1 -1
  31. package/dist/esm/src/primitives/Point.js +103 -23
  32. package/dist/esm/src/primitives/Point.js.map +1 -1
  33. package/dist/esm/src/primitives/TransactionSignature.js +4 -3
  34. package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
  35. package/dist/esm/src/primitives/utils.js +14 -15
  36. package/dist/esm/src/primitives/utils.js.map +1 -1
  37. package/dist/esm/src/script/Spend.js +4 -4
  38. package/dist/esm/src/script/Spend.js.map +1 -1
  39. package/dist/esm/src/totp/totp.js +1 -1
  40. package/dist/esm/src/totp/totp.js.map +1 -1
  41. package/dist/esm/src/transaction/Beef.js +485 -0
  42. package/dist/esm/src/transaction/Beef.js.map +1 -0
  43. package/dist/esm/src/transaction/BeefParty.js +93 -0
  44. package/dist/esm/src/transaction/BeefParty.js.map +1 -0
  45. package/dist/esm/src/transaction/BeefTx.js +121 -0
  46. package/dist/esm/src/transaction/BeefTx.js.map +1 -0
  47. package/dist/esm/src/transaction/Transaction.js +81 -66
  48. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  49. package/dist/esm/src/transaction/index.js +3 -0
  50. package/dist/esm/src/transaction/index.js.map +1 -1
  51. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  52. package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
  53. package/dist/types/src/primitives/Point.d.ts +5 -0
  54. package/dist/types/src/primitives/Point.d.ts.map +1 -1
  55. package/dist/types/src/primitives/TransactionSignature.d.ts.map +1 -1
  56. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  57. package/dist/types/src/transaction/Beef.d.ts +143 -0
  58. package/dist/types/src/transaction/Beef.d.ts.map +1 -0
  59. package/dist/types/src/transaction/BeefParty.d.ts +62 -0
  60. package/dist/types/src/transaction/BeefParty.d.ts.map +1 -0
  61. package/dist/types/src/transaction/BeefTx.d.ts +35 -0
  62. package/dist/types/src/transaction/BeefTx.d.ts.map +1 -0
  63. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  64. package/dist/types/src/transaction/TransactionInput.d.ts +1 -1
  65. package/dist/types/src/transaction/TransactionInput.d.ts.map +1 -1
  66. package/dist/types/src/transaction/index.d.ts +3 -0
  67. package/dist/types/src/transaction/index.d.ts.map +1 -1
  68. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  69. package/docs/primitives.md +373 -55
  70. package/docs/transaction.md +467 -1
  71. package/package.json +1 -1
  72. package/src/primitives/Curve.ts +7 -7
  73. package/src/primitives/ECDSA.ts +485 -75
  74. package/src/primitives/Point.ts +110 -25
  75. package/src/primitives/TransactionSignature.ts +4 -3
  76. package/src/primitives/utils.ts +15 -11
  77. package/src/script/Spend.ts +4 -4
  78. package/src/totp/totp.ts +1 -1
  79. package/src/transaction/Beef.ts +533 -0
  80. package/src/transaction/BeefParty.ts +100 -0
  81. package/src/transaction/BeefTx.ts +121 -0
  82. package/src/transaction/Transaction.ts +95 -69
  83. package/src/transaction/TransactionInput.ts +1 -1
  84. package/src/transaction/__tests/Beef.test.ts +290 -0
  85. package/src/transaction/__tests/Transaction.benchmarks.test.ts +222 -0
  86. package/src/transaction/index.ts +3 -0
@@ -23,10 +23,9 @@ import DRBG from './DRBG.js'
23
23
  * let msg = new BigNumber('1234567890abcdef', 16);
24
24
  * let truncatedMsg = truncateToN(msg);
25
25
  */
26
- function truncateToN (msg: BigNumber, truncOnly?: boolean): BigNumber {
27
- const curve = new Curve()
26
+ function truncateToN (msg: BigNumber, truncOnly?: boolean, curve = new Curve()): BigNumber {
28
27
  const delta = msg.byteLength() * 8 - curve.n.bitLength()
29
- if (delta > 0) { msg = msg.ushrn(delta) }
28
+ if (delta > 0) { msg.iushrn(delta) }
30
29
  if (!truncOnly && msg.cmp(curve.n) >= 0) {
31
30
  return msg.sub(curve.n)
32
31
  } else {
@@ -50,73 +49,293 @@ function truncateToN (msg: BigNumber, truncOnly?: boolean): BigNumber {
50
49
  * const signature = sign(msg, key)
51
50
  */
52
51
  export const sign = (msg: BigNumber, key: BigNumber, forceLowS: boolean = false, customK?: BigNumber | Function): Signature => {
53
- const curve = new Curve()
54
- msg = truncateToN(msg)
55
-
56
- // Zero-extend key to provide enough entropy
57
- const bytes = curve.n.byteLength()
58
- const bkey = key.toArray('be', bytes)
59
-
60
- // Zero-extend nonce to have the same byte size as N
61
- const nonce = msg.toArray('be', bytes)
62
-
63
- // Instantiate Hmac_DRBG
64
- const drbg = new DRBG(bkey, nonce)
65
-
66
- // Number of bytes to generate
67
- const ns1 = curve.n.subn(1)
68
-
69
- for (let iter = 0; ; iter++) {
70
- // Compute the k-value
71
- let k = typeof customK === 'function'
72
- ? customK(iter)
73
- : BigNumber.isBN(customK)
74
- ? customK
75
- : new BigNumber(drbg.generate(bytes), 16)
76
- k = truncateToN(k, true)
77
- if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) {
78
- if (BigNumber.isBN(customK)) {
79
- throw new Error('Invalid fixed custom K value (must be more than 1 and less than N-1)')
80
- } else {
81
- continue
52
+ if (typeof BigInt === 'function') {
53
+ // Curve parameters for secp256k1
54
+ const zero = BigInt(0)
55
+ const one = BigInt(1)
56
+ const two = BigInt(2)
57
+ const n = BigInt(
58
+ '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'
59
+ ) // Order of the curve
60
+ const p = BigInt(
61
+ '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'
62
+ ) // Field prime
63
+ const Gx = BigInt(
64
+ '0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'
65
+ )
66
+ const Gy = BigInt(
67
+ '0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8'
68
+ )
69
+ const G = { x: Gx, y: Gy }
70
+
71
+ // Convert msg and key to BigInt
72
+ const z = BigInt('0x' + msg.toString(16))
73
+ const d = BigInt('0x' + key.toString(16))
74
+
75
+ // Validate private key
76
+ if (d <= zero || d >= n) {
77
+ throw new Error('Invalid private key')
78
+ }
79
+
80
+ // Helper function to convert BigInt to byte array
81
+ function bigIntToBytes (value: bigint, length: number): Uint8Array {
82
+ const hex = value.toString(16).padStart(length * 2, '0')
83
+ const bytes = new Uint8Array(length)
84
+ for (let i = 0; i < length; i++) {
85
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16)
82
86
  }
87
+ return bytes
83
88
  }
84
89
 
85
- const kp = curve.g.mul(k)
86
- if (kp.isInfinity()) {
87
- if (BigNumber.isBN(customK)) {
88
- throw new Error('Invalid fixed custom K value (must not create a point at infinity when multiplied by the generator point)')
90
+ // Zero-extend key to provide enough entropy
91
+ const bytes = 32 // Assuming 256-bit curve
92
+ const bkey = bigIntToBytes(d, bytes) // 'd' is the private key BigInt
93
+
94
+ // Zero-extend nonce to have the same byte size as N
95
+ const nonce = bigIntToBytes(z, bytes) // 'z' is the message hash BigInt
96
+
97
+ // Instantiate Hmac_DRBG
98
+ const drbg = new DRBG(Array.from(bkey), Array.from(nonce))
99
+
100
+ // Number of bytes to generate
101
+ const ns1 = n - one
102
+
103
+ let iter = 0
104
+
105
+ // Truncate to N function for BigInt
106
+ function truncateToN (k: bigint, n: bigint, truncOnly: boolean = true): bigint {
107
+ const kBitLength = k.toString(2).length
108
+ const nBitLength = n.toString(2).length
109
+ const delta = kBitLength - nBitLength
110
+ if (delta > 0) {
111
+ k = k >> BigInt(delta)
112
+ }
113
+ if (!truncOnly && k >= n) {
114
+ return k - n
89
115
  } else {
90
- continue
116
+ return k
91
117
  }
92
118
  }
93
119
 
94
- const kpX = kp.getX()
95
- const r = kpX.umod(curve.n)
96
- if (r.cmpn(0) === 0) {
97
- if (BigNumber.isBN(customK)) {
98
- throw new Error('Invalid fixed custom K value (when multiplied by G, the resulting x coordinate mod N must not be zero)')
120
+ function generateK (): bigint {
121
+ if (typeof customK === 'function') {
122
+ // Call customK function to get k as BigNumber
123
+ const k_bn = customK(iter)
124
+ // Convert k_bn (BigNumber) to BigInt
125
+ const k_str = k_bn.toString(16)
126
+ return BigInt('0x' + k_str)
127
+ } else if (BigNumber.isBN(customK)) {
128
+ // Use customK provided, convert to BigInt
129
+ const k_str = customK.toString(16)
130
+ return BigInt('0x' + k_str)
99
131
  } else {
100
- continue
132
+ // Use DRBG to generate k
133
+ const k_hex = drbg.generate(bytes) // Generate hex string
134
+ return BigInt('0x' + k_hex)
101
135
  }
102
136
  }
103
137
 
104
- let s = k.invm(curve.n).mul(r.mul(key).iadd(msg))
105
- s = s.umod(curve.n)
106
- if (s.cmpn(0) === 0) {
107
- if (BigNumber.isBN(customK)) {
108
- throw new Error('Invalid fixed custom K value (when used with the key, it cannot create a zero value for S)')
138
+ // Modular arithmetic functions
139
+ function mod (a: bigint, m: bigint): bigint {
140
+ return ((a % m) + m) % m
141
+ }
142
+
143
+ function modInv (a: bigint, m: bigint): bigint {
144
+ let lm = one
145
+ let hm = zero
146
+ let low = mod(a, m)
147
+ let high = m
148
+ while (low > one) {
149
+ const r = high / low
150
+ const nm = hm - lm * r
151
+ const neww = high - low * r
152
+ hm = lm
153
+ lm = nm
154
+ high = low
155
+ low = neww
156
+ }
157
+ return mod(lm, m)
158
+ }
159
+
160
+ function pointAdd (
161
+ P: { x: bigint, y: bigint } | null,
162
+ Q: { x: bigint, y: bigint } | null
163
+ ): { x: bigint, y: bigint } | null {
164
+ if (P === null) return Q
165
+ if (Q === null) return P
166
+
167
+ if (P.x === Q.x && P.y === mod(-Q.y, p)) {
168
+ return null // Point at infinity
169
+ }
170
+
171
+ let m: bigint
172
+ if (P.x === Q.x && P.y === Q.y) {
173
+ // Point doubling
174
+ if (P.y === zero) {
175
+ return null // Point at infinity
176
+ }
177
+ const numerator = mod(BigInt(3) * P.x * P.x, p) // 3 * x^2
178
+ const denominator = modInv(two * P.y, p)
179
+ m = mod(numerator * denominator, p)
109
180
  } else {
110
- continue
181
+ const numerator = mod(Q.y - P.y, p)
182
+ const denominator = modInv(Q.x - P.x, p)
183
+ m = mod(numerator * denominator, p)
184
+ }
185
+
186
+ const xR = mod(m * m - P.x - Q.x, p)
187
+ const yR = mod(m * (P.x - xR) - P.y, p)
188
+
189
+ return { x: xR, y: yR }
190
+ }
191
+
192
+ function scalarMul (
193
+ k: bigint,
194
+ P: { x: bigint, y: bigint }
195
+ ): { x: bigint, y: bigint } | null {
196
+ let N = P
197
+ let Q = null // Point at infinity
198
+
199
+ while (k > zero) {
200
+ if (k % two === one) {
201
+ Q = pointAdd(Q, N)
202
+ }
203
+ N = pointAdd(N, N)
204
+ k >>= one
111
205
  }
206
+ return Q
112
207
  }
113
208
 
114
- // Use complement of `s`, if it is > `n / 2`
115
- if (forceLowS && s.cmp(curve.n.ushrn(1)) > 0) {
116
- s = curve.n.sub(s)
209
+ while (true) {
210
+ let k = generateK()
211
+ iter += 1
212
+
213
+ // Truncate k to n bits
214
+ k = truncateToN(k, n, true)
215
+
216
+ if (k <= one || k >= ns1) {
217
+ if (customK instanceof BigNumber) {
218
+ throw new Error(
219
+ 'Invalid fixed custom K value (must be more than 1 and less than N-1)'
220
+ )
221
+ } else {
222
+ continue
223
+ }
224
+ }
225
+
226
+ const R = scalarMul(k, G)
227
+ if (R === null) {
228
+ if (customK instanceof BigNumber) {
229
+ throw new Error(
230
+ 'Invalid fixed custom K value (must not create a point at infinity when multiplied by the generator point)'
231
+ )
232
+ } else {
233
+ continue
234
+ }
235
+ }
236
+
237
+ const r = mod(R.x, n)
238
+ if (r === zero) {
239
+ if (customK instanceof BigNumber) {
240
+ throw new Error(
241
+ 'Invalid fixed custom K value (when multiplied by G, the resulting x coordinate mod N must not be zero)'
242
+ )
243
+ } else {
244
+ continue
245
+ }
246
+ }
247
+
248
+ const kInv = modInv(k, n)
249
+ const rd = mod(r * d, n)
250
+ let s = mod(kInv * (z + rd), n)
251
+ if (s === zero) {
252
+ if (customK instanceof BigNumber) {
253
+ throw new Error(
254
+ 'Invalid fixed custom K value (when used with the key, it cannot create a zero value for S)'
255
+ )
256
+ } else {
257
+ continue
258
+ }
259
+ }
260
+
261
+ // Use complement of `s` if it is > n / 2
262
+ if (forceLowS && s > n / two) {
263
+ s = n - s
264
+ }
265
+
266
+ // Return signature as BigNumbers
267
+ const r_bn = new BigNumber(r.toString(16), 16)
268
+ const s_bn = new BigNumber(s.toString(16), 16)
269
+ return new Signature(r_bn, s_bn)
117
270
  }
271
+ } else {
272
+ const curve = new Curve()
273
+ msg = truncateToN(msg)
274
+
275
+ // Zero-extend key to provide enough entropy
276
+ const bytes = curve.n.byteLength()
277
+ const bkey = key.toArray('be', bytes)
278
+
279
+ // Zero-extend nonce to have the same byte size as N
280
+ const nonce = msg.toArray('be', bytes)
281
+
282
+ // Instantiate Hmac_DRBG
283
+ const drbg = new DRBG(bkey, nonce)
284
+
285
+ // Number of bytes to generate
286
+ const ns1 = curve.n.subn(1)
287
+
288
+ for (let iter = 0; ; iter++) {
289
+ // Compute the k-value
290
+ let k = typeof customK === 'function'
291
+ ? customK(iter)
292
+ : BigNumber.isBN(customK)
293
+ ? customK
294
+ : new BigNumber(drbg.generate(bytes), 16)
295
+ k = truncateToN(k, true)
296
+ if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) {
297
+ if (BigNumber.isBN(customK)) {
298
+ throw new Error('Invalid fixed custom K value (must be more than 1 and less than N-1)')
299
+ } else {
300
+ continue
301
+ }
302
+ }
303
+
304
+ const kp = curve.g.mul(k)
305
+ if (kp.isInfinity()) {
306
+ if (BigNumber.isBN(customK)) {
307
+ throw new Error('Invalid fixed custom K value (must not create a point at infinity when multiplied by the generator point)')
308
+ } else {
309
+ continue
310
+ }
311
+ }
312
+
313
+ const kpX = kp.getX()
314
+ const r = kpX.umod(curve.n)
315
+ if (r.cmpn(0) === 0) {
316
+ if (BigNumber.isBN(customK)) {
317
+ throw new Error('Invalid fixed custom K value (when multiplied by G, the resulting x coordinate mod N must not be zero)')
318
+ } else {
319
+ continue
320
+ }
321
+ }
322
+
323
+ let s = k.invm(curve.n).mul(r.mul(key).iadd(msg))
324
+ s = s.umod(curve.n)
325
+ if (s.cmpn(0) === 0) {
326
+ if (BigNumber.isBN(customK)) {
327
+ throw new Error('Invalid fixed custom K value (when used with the key, it cannot create a zero value for S)')
328
+ } else {
329
+ continue
330
+ }
331
+ }
118
332
 
119
- return new Signature(r, s)
333
+ // Use complement of `s`, if it is > `n / 2`
334
+ if (forceLowS && s.cmp(curve.n.ushrn(1)) > 0) {
335
+ s = curve.n.sub(s)
336
+ }
337
+ return new Signature(r, s)
338
+ }
120
339
  }
121
340
  }
122
341
 
@@ -139,26 +358,217 @@ export const sign = (msg: BigNumber, key: BigNumber, forceLowS: boolean = false,
139
358
  * const isVerified = verify(msg, sig, key)
140
359
  */
141
360
  export const verify = (msg: BigNumber, sig: Signature, key: Point): boolean => {
142
- const curve = new Curve()
143
- msg = truncateToN(msg)
144
- // Perform primitive values validation
145
- const r = sig.r
146
- const s = sig.s
147
- if (r.cmpn(1) < 0 || r.cmp(curve.n) >= 0) { return false }
148
- if (s.cmpn(1) < 0 || s.cmp(curve.n) >= 0) { return false }
149
-
150
- // Validate signature
151
- const sinv = s.invm(curve.n)
152
- const u1 = sinv.mul(msg).umod(curve.n)
153
- const u2 = sinv.mul(r).umod(curve.n)
154
-
155
- // NOTE: Greg Maxwell's trick, inspired by:
156
- // https://git.io/vad3K
157
- const p = curve.g.jmulAdd(u1, key, u2)
158
- if (p.isInfinity()) { return false }
159
-
160
- // Compare `p.x` of Jacobian point with `r`,
161
- // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
162
- // inverse of `p.z^2`
163
- return p.eqXToP(r)
361
+ // Use BigInt for verification opportunistically
362
+ if (typeof BigInt === 'function') {
363
+ // Curve parameters for secp256k1
364
+ const zero = BigInt(0)
365
+ const one = BigInt(1)
366
+ const two = BigInt(2)
367
+ const three = BigInt(3)
368
+ const p = BigInt(
369
+ '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'
370
+ ) // Field prime
371
+ const n = BigInt(
372
+ '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'
373
+ ) // Order of the curve
374
+ const G = {
375
+ x: BigInt(
376
+ '0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'
377
+ ),
378
+ y: BigInt(
379
+ '0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8'
380
+ )
381
+ }
382
+
383
+ // Modular arithmetic functions
384
+ const mod = (a: bigint, m: bigint): bigint => ((a % m) + m) % m
385
+ const modInv = (a: bigint, m: bigint): bigint => {
386
+ // Extended Euclidean Algorithm for modular inverse
387
+ let [old_r, r] = [a, m]
388
+ let [old_s, s] = [BigInt(1), BigInt(0)]
389
+ while (r !== zero) {
390
+ const q = old_r / r;
391
+ [old_r, r] = [r, old_r - q * r];
392
+ [old_s, s] = [s, old_s - q * s]
393
+ }
394
+ if (old_r > one) return zero // No inverse
395
+ return mod(old_s, m)
396
+ }
397
+ const modMul = (a: bigint, b: bigint, m: bigint): bigint =>
398
+ mod(a * b, m)
399
+ const modSub = (a: bigint, b: bigint, m: bigint): bigint =>
400
+ mod(a - b, m)
401
+ const modAdd = (a: bigint, b: bigint, m: bigint): bigint =>
402
+ mod(a + b, m)
403
+
404
+ // Define constants
405
+ const four = BigInt(4)
406
+ const eight = BigInt(8)
407
+
408
+ // Elliptic curve point operations in Jacobian coordinates
409
+ interface JacobianPoint {
410
+ X: bigint
411
+ Y: bigint
412
+ Z: bigint
413
+ }
414
+
415
+ // Point Doubling
416
+ const pointDouble = (P: JacobianPoint): JacobianPoint => {
417
+ const { X: X1, Y: Y1, Z: Z1 } = P
418
+
419
+ if (Y1 === zero) {
420
+ return { X: zero, Y: one, Z: zero } // Point at infinity
421
+ }
422
+
423
+ const Y1_sq = modMul(Y1, Y1, p) // Y1^2
424
+ const S = modMul(four, modMul(X1, Y1_sq, p), p) // S = 4 * X1 * Y1^2
425
+ const M = modMul(three, modMul(X1, X1, p), p) // M = 3 * X1^2
426
+ const X3 = modSub(modMul(M, M, p), modMul(two, S, p), p) // X3 = M^2 - 2 * S
427
+ const Y3 = modSub(
428
+ modMul(M, modSub(S, X3, p), p),
429
+ modMul(eight, modMul(Y1_sq, Y1_sq, p), p),
430
+ p
431
+ ) // Y3 = M * (S - X3) - 8 * Y1^4
432
+ const Z3 = modMul(two, modMul(Y1, Z1, p), p) // Z3 = 2 * Y1 * Z1
433
+
434
+ return { X: X3, Y: Y3, Z: Z3 }
435
+ }
436
+
437
+ // Point Addition
438
+ const pointAdd = (P: JacobianPoint, Q: JacobianPoint): JacobianPoint => {
439
+ if (P.Z === zero) return Q
440
+ if (Q.Z === zero) return P
441
+
442
+ const Z1Z1 = modMul(P.Z, P.Z, p)
443
+ const Z2Z2 = modMul(Q.Z, Q.Z, p)
444
+ const U1 = modMul(P.X, Z2Z2, p)
445
+ const U2 = modMul(Q.X, Z1Z1, p)
446
+ const S1 = modMul(P.Y, modMul(Z2Z2, Q.Z, p), p)
447
+ const S2 = modMul(Q.Y, modMul(Z1Z1, P.Z, p), p)
448
+
449
+ const H = modSub(U2, U1, p)
450
+ const r = modSub(S2, S1, p)
451
+
452
+ if (H === zero) {
453
+ if (r === zero) {
454
+ // P == Q
455
+ return pointDouble(P)
456
+ } else {
457
+ // Point at infinity
458
+ return { X: zero, Y: one, Z: zero }
459
+ }
460
+ }
461
+
462
+ const HH = modMul(H, H, p)
463
+ const HHH = modMul(H, HH, p)
464
+ const V = modMul(U1, HH, p)
465
+
466
+ const X3 = modSub(modSub(modMul(r, r, p), HHH, p), modMul(two, V, p), p)
467
+ const Y3 = modSub(
468
+ modMul(r, modSub(V, X3, p), p),
469
+ modMul(S1, HHH, p),
470
+ p
471
+ )
472
+ const Z3 = modMul(H, modMul(P.Z, Q.Z, p), p)
473
+
474
+ return { X: X3, Y: Y3, Z: Z3 }
475
+ }
476
+
477
+ // Scalar Multiplication
478
+ const scalarMultiply = (k: bigint, P: { x: bigint, y: bigint }): JacobianPoint => {
479
+ const N: JacobianPoint = { X: P.x, Y: P.y, Z: one }
480
+ let Q: JacobianPoint = { X: zero, Y: one, Z: zero } // Point at infinity
481
+
482
+ const kBin = k.toString(2)
483
+ for (let i = 0; i < kBin.length; i++) {
484
+ Q = pointDouble(Q)
485
+ if (kBin[i] === '1') {
486
+ Q = pointAdd(Q, N)
487
+ }
488
+ }
489
+ return Q
490
+ }
491
+
492
+ // Verify Function Using Jacobian Coordinates
493
+ const verifyECDSA = (
494
+ hash: bigint,
495
+ publicKey: { x: bigint, y: bigint },
496
+ signature: { r: bigint, s: bigint }
497
+ ): boolean => {
498
+ const { r, s } = signature
499
+ const z = hash
500
+
501
+ // Check r and s are in [1, n - 1]
502
+ if (r <= zero || r >= n || s <= zero || s >= n) {
503
+ return false
504
+ }
505
+
506
+ const w = modInv(s, n) // w = s^-1 mod n
507
+ if (w === zero) {
508
+ return false // No inverse exists
509
+ }
510
+ const u1 = modMul(z, w, n)
511
+ const u2 = modMul(r, w, n)
512
+
513
+ // Compute point R = u1 * G + u2 * Q
514
+ const RG = scalarMultiply(u1, G)
515
+ const RQ = scalarMultiply(u2, publicKey)
516
+ const R = pointAdd(RG, RQ)
517
+
518
+ if (R.Z === zero) {
519
+ // Point at infinity
520
+ return false
521
+ }
522
+
523
+ // Compute affine x-coordinate x1 = X / Z^2 mod p
524
+ const ZInv = modInv(R.Z, p)
525
+ if (ZInv === zero) {
526
+ return false // No inverse exists
527
+ }
528
+ const ZInv2 = modMul(ZInv, ZInv, p)
529
+ const x1_affine = modMul(R.X, ZInv2, p)
530
+
531
+ // Compute v = x1_affine mod n
532
+ const v = mod(x1_affine, n)
533
+
534
+ // Signature is valid if v == r mod n
535
+ return v === r
536
+ }
537
+
538
+ // Convert inputs to BigInt
539
+ const hash = BigInt('0x' + msg.toString(16))
540
+ const publicKey = {
541
+ x: BigInt('0x' + key.x.toString(16)),
542
+ y: BigInt('0x' + key.y.toString(16))
543
+ }
544
+ const signature = {
545
+ r: BigInt('0x' + sig.r.toString(16)),
546
+ s: BigInt('0x' + sig.s.toString(16))
547
+ }
548
+
549
+ return verifyECDSA(hash, publicKey, signature)
550
+ } else {
551
+ const curve = new Curve()
552
+ msg = truncateToN(msg)
553
+ // Perform primitive values validation
554
+ const r = sig.r
555
+ const s = sig.s
556
+ if (r.cmpn(1) < 0 || r.cmp(curve.n) >= 0) { return false }
557
+ if (s.cmpn(1) < 0 || s.cmp(curve.n) >= 0) { return false }
558
+
559
+ // Validate signature
560
+ const sinv = s.invm(curve.n)
561
+ const u1 = sinv.mul(msg).umod(curve.n)
562
+ const u2 = sinv.mul(r).umod(curve.n)
563
+
564
+ // NOTE: Greg Maxwell's trick, inspired by:
565
+ // https://git.io/vad3K
566
+ const p = curve.g.jmulAdd(u1, key, u2)
567
+ if (p.isInfinity()) { return false }
568
+
569
+ // Compare `p.x` of Jacobian point with `r`,
570
+ // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
571
+ // inverse of `p.z^2`
572
+ return p.eqXToP(r)
573
+ }
164
574
  }