@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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/primitives/Curve.js +7 -7
- package/dist/cjs/src/primitives/Curve.js.map +1 -1
- package/dist/cjs/src/primitives/ECDSA.js +394 -71
- package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
- package/dist/cjs/src/primitives/Point.js +103 -23
- package/dist/cjs/src/primitives/Point.js.map +1 -1
- package/dist/cjs/src/primitives/TransactionSignature.js +4 -3
- package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/cjs/src/primitives/utils.js +14 -15
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +4 -4
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/totp/totp.js +1 -1
- package/dist/cjs/src/totp/totp.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +492 -0
- package/dist/cjs/src/transaction/Beef.js.map +1 -0
- package/dist/cjs/src/transaction/BeefParty.js +97 -0
- package/dist/cjs/src/transaction/BeefParty.js.map +1 -0
- package/dist/cjs/src/transaction/BeefTx.js +123 -0
- package/dist/cjs/src/transaction/BeefTx.js.map +1 -0
- package/dist/cjs/src/transaction/Transaction.js +81 -66
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/index.js +7 -1
- package/dist/cjs/src/transaction/index.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/primitives/Curve.js +7 -7
- package/dist/esm/src/primitives/Curve.js.map +1 -1
- package/dist/esm/src/primitives/ECDSA.js +394 -71
- package/dist/esm/src/primitives/ECDSA.js.map +1 -1
- package/dist/esm/src/primitives/Point.js +103 -23
- package/dist/esm/src/primitives/Point.js.map +1 -1
- package/dist/esm/src/primitives/TransactionSignature.js +4 -3
- package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/esm/src/primitives/utils.js +14 -15
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/src/script/Spend.js +4 -4
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/totp/totp.js +1 -1
- package/dist/esm/src/totp/totp.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +485 -0
- package/dist/esm/src/transaction/Beef.js.map +1 -0
- package/dist/esm/src/transaction/BeefParty.js +93 -0
- package/dist/esm/src/transaction/BeefParty.js.map +1 -0
- package/dist/esm/src/transaction/BeefTx.js +121 -0
- package/dist/esm/src/transaction/BeefTx.js.map +1 -0
- package/dist/esm/src/transaction/Transaction.js +81 -66
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/index.js +3 -0
- package/dist/esm/src/transaction/index.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
- package/dist/types/src/primitives/Point.d.ts +5 -0
- package/dist/types/src/primitives/Point.d.ts.map +1 -1
- package/dist/types/src/primitives/TransactionSignature.d.ts.map +1 -1
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/src/transaction/Beef.d.ts +143 -0
- package/dist/types/src/transaction/Beef.d.ts.map +1 -0
- package/dist/types/src/transaction/BeefParty.d.ts +62 -0
- package/dist/types/src/transaction/BeefParty.d.ts.map +1 -0
- package/dist/types/src/transaction/BeefTx.d.ts +35 -0
- package/dist/types/src/transaction/BeefTx.d.ts.map +1 -0
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/TransactionInput.d.ts +1 -1
- package/dist/types/src/transaction/TransactionInput.d.ts.map +1 -1
- package/dist/types/src/transaction/index.d.ts +3 -0
- package/dist/types/src/transaction/index.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/primitives.md +373 -55
- package/docs/transaction.md +467 -1
- package/package.json +1 -1
- package/src/primitives/Curve.ts +7 -7
- package/src/primitives/ECDSA.ts +485 -75
- package/src/primitives/Point.ts +110 -25
- package/src/primitives/TransactionSignature.ts +4 -3
- package/src/primitives/utils.ts +15 -11
- package/src/script/Spend.ts +4 -4
- package/src/totp/totp.ts +1 -1
- package/src/transaction/Beef.ts +533 -0
- package/src/transaction/BeefParty.ts +100 -0
- package/src/transaction/BeefTx.ts +121 -0
- package/src/transaction/Transaction.ts +95 -69
- package/src/transaction/TransactionInput.ts +1 -1
- package/src/transaction/__tests/Beef.test.ts +290 -0
- package/src/transaction/__tests/Transaction.benchmarks.test.ts +222 -0
- package/src/transaction/index.ts +3 -0
package/src/primitives/ECDSA.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
116
|
+
return k
|
|
91
117
|
}
|
|
92
118
|
}
|
|
93
119
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
}
|