@bsv/sdk 1.6.18 → 1.6.19

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 (44) hide show
  1. package/README.md +11 -11
  2. package/dist/cjs/package.json +4 -8
  3. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +39 -39
  4. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  5. package/dist/cjs/src/primitives/ECDSA.js +26 -125
  6. package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
  7. package/dist/cjs/src/primitives/Hash.js +660 -436
  8. package/dist/cjs/src/primitives/Hash.js.map +1 -1
  9. package/dist/cjs/src/primitives/Point.js +226 -213
  10. package/dist/cjs/src/primitives/Point.js.map +1 -1
  11. package/dist/cjs/src/transaction/Beef.js +4 -4
  12. package/dist/cjs/src/transaction/Transaction.js +3 -3
  13. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  14. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  15. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +39 -39
  16. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  17. package/dist/esm/src/primitives/ECDSA.js +26 -125
  18. package/dist/esm/src/primitives/ECDSA.js.map +1 -1
  19. package/dist/esm/src/primitives/Hash.js +672 -444
  20. package/dist/esm/src/primitives/Hash.js.map +1 -1
  21. package/dist/esm/src/primitives/Point.js +211 -213
  22. package/dist/esm/src/primitives/Point.js.map +1 -1
  23. package/dist/esm/src/transaction/Beef.js +4 -4
  24. package/dist/esm/src/transaction/Transaction.js +3 -3
  25. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  26. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  27. package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts +1 -1
  28. package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts.map +1 -1
  29. package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
  30. package/dist/types/src/primitives/Hash.d.ts +12 -19
  31. package/dist/types/src/primitives/Hash.d.ts.map +1 -1
  32. package/dist/types/src/primitives/Point.d.ts +34 -0
  33. package/dist/types/src/primitives/Point.d.ts.map +1 -1
  34. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  35. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  36. package/dist/umd/bundle.js +20 -1
  37. package/dist/umd/bundle.js.map +1 -0
  38. package/package.json +4 -8
  39. package/src/auth/transports/SimplifiedFetchTransport.ts +64 -67
  40. package/src/primitives/ECDSA.ts +30 -173
  41. package/src/primitives/Hash.ts +752 -589
  42. package/src/primitives/Point.ts +222 -247
  43. package/src/transaction/Beef.ts +4 -4
  44. package/src/transaction/Transaction.ts +11 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.6.18",
3
+ "version": "1.6.19",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -216,7 +216,7 @@
216
216
  "lint": "ts-standard --fix src/**/*.ts",
217
217
  "build": "npm run build:ts && npm run build:umd",
218
218
  "build:ts": "tsc -b && tsconfig-to-dual-package tsconfig.cjs.json",
219
- "build:umd": "webpack --config webpack.config.js",
219
+ "build:umd": "rspack --config rspack.config.js",
220
220
  "dev": "tsc -b -w",
221
221
  "prepublish": "npm run build",
222
222
  "doc": "ts2md",
@@ -256,16 +256,12 @@
256
256
  "tsconfig-to-dual-package": "^1.2.0",
257
257
  "typescript": "5.1",
258
258
  "typescript-eslint": "^8.29.0",
259
- "webpack": "^5.98.0",
260
- "webpack-cli": "^6.0.1"
259
+ "@rspack/cli": "^0.4.4"
261
260
  },
262
261
  "ts-standard": {
263
262
  "project": "tsconfig.eslint.json",
264
263
  "ignore": [
265
- "dist",
266
- "src/script/ScriptTemplate.ts",
267
- "src/transaction/Transaction.ts",
268
- "src/auth/transports/SimplifiedFetchTransport.ts"
264
+ "dist"
269
265
  ]
270
266
  }
271
267
  }
@@ -1,12 +1,10 @@
1
1
  // @ts-nocheck
2
- // @ts-ignore
3
- import { AuthMessage, RequestedCertificateSet, Transport } from "../types.js"
2
+ // @ts-expect-error
3
+ import { AuthMessage, RequestedCertificateSet, Transport } from '../types.js'
4
4
  import * as Utils from '../../primitives/utils.js'
5
5
 
6
- const SUCCESS_STATUS_CODES = [200, 402]
7
-
8
6
  // Only bind window.fetch in the browser
9
- const defaultFetch = typeof window !== 'undefined' ? fetch.bind(window) : fetch;
7
+ const defaultFetch = typeof window !== 'undefined' ? fetch.bind(window) : fetch
10
8
 
11
9
  /**
12
10
  * Implements an HTTP-specific transport for handling Peer mutual authentication messages.
@@ -17,13 +15,12 @@ export class SimplifiedFetchTransport implements Transport {
17
15
  fetchClient: typeof fetch
18
16
  baseUrl: string
19
17
 
20
-
21
18
  /**
22
19
  * Constructs a new instance of SimplifiedFetchTransport.
23
20
  * @param baseUrl - The base URL for all HTTP requests made by this transport.
24
21
  * @param fetchClient - A fetch implementation to use for HTTP requests (default: global fetch).
25
22
  */
26
- constructor(baseUrl: string, fetchClient = defaultFetch) {
23
+ constructor (baseUrl: string, fetchClient = defaultFetch) {
27
24
  this.fetchClient = fetchClient
28
25
  this.baseUrl = baseUrl
29
26
  }
@@ -33,47 +30,48 @@ export class SimplifiedFetchTransport implements Transport {
33
30
  * Handles both general and authenticated message types. For general messages,
34
31
  * the payload is deserialized and sent as an HTTP request. For other message types,
35
32
  * the message is sent as a POST request to the `/auth` endpoint.
36
- *
33
+ *
37
34
  * @param message - The AuthMessage to send.
38
35
  * @returns A promise that resolves when the message is successfully sent.
39
- *
36
+ *
40
37
  * @throws Will throw an error if no listener has been registered via `onData`.
41
38
  */
42
- async send(message: AuthMessage): Promise<void> {
43
- if (!this.onDataCallback) {
39
+ async send (message: AuthMessage): Promise<void> {
40
+ if (this.onDataCallback == null) {
44
41
  throw new Error('Listen before you start speaking. God gave you two ears and one mouth for a reason.')
45
42
  }
46
43
  if (message.messageType !== 'general') {
47
- return new Promise(async (resolve, reject) => {
48
- try {
49
- const responsePromise = this.fetchClient(`${this.baseUrl}/.well-known/auth`, {
50
- method: 'POST',
51
- headers: {
52
- 'Content-Type': 'application/json'
53
- },
54
- body: JSON.stringify(message)
55
- })
44
+ return await new Promise((resolve, reject) => {
45
+ void (async () => {
46
+ try {
47
+ const responsePromise = this.fetchClient(`${this.baseUrl}/.well-known/auth`, {
48
+ method: 'POST',
49
+ headers: {
50
+ 'Content-Type': 'application/json'
51
+ },
52
+ body: JSON.stringify(message)
53
+ })
56
54
 
57
- // For initialRequest message, mark connection as established and start pool.
58
- if (message.messageType !== "initialRequest") {
59
- resolve()
60
- }
61
- const response = await responsePromise
62
- // Handle the response if data is received and callback is set
63
- if (response.ok && this.onDataCallback) {
64
- const responseMessage = await response.json()
65
- this.onDataCallback(responseMessage as AuthMessage)
66
- } else {
55
+ // For initialRequest message, mark connection as established and start pool.
56
+ if (message.messageType !== 'initialRequest') {
57
+ resolve()
58
+ }
59
+ const response = await responsePromise
60
+ // Handle the response if data is received and callback is set
61
+ if (response.ok && (this.onDataCallback != null)) {
62
+ const responseMessage = await response.json()
63
+ this.onDataCallback(responseMessage as AuthMessage)
64
+ } else {
67
65
  // Server may be a non authenticated server
68
- throw new Error('HTTP server failed to authenticate')
66
+ throw new Error('HTTP server failed to authenticate')
67
+ }
68
+ if (message.messageType === 'initialRequest') {
69
+ resolve()
70
+ }
71
+ } catch (e) {
72
+ reject(e)
69
73
  }
70
- if (message.messageType === "initialRequest") {
71
- resolve()
72
- }
73
- } catch (e) {
74
- reject(e)
75
- return
76
- }
74
+ })()
77
75
  })
78
76
  } else {
79
77
  // Parse message payload
@@ -81,7 +79,7 @@ export class SimplifiedFetchTransport implements Transport {
81
79
 
82
80
  // Send the byte array as the HTTP payload
83
81
  const url = `${this.baseUrl}${httpRequest.urlPostfix}`
84
- let httpRequestWithAuthHeaders: any = httpRequest
82
+ const httpRequestWithAuthHeaders: any = httpRequest
85
83
  if (typeof httpRequest.headers !== 'object') {
86
84
  httpRequestWithAuthHeaders.headers = {}
87
85
  }
@@ -95,27 +93,27 @@ export class SimplifiedFetchTransport implements Transport {
95
93
  httpRequestWithAuthHeaders.headers['x-bsv-auth-request-id'] = httpRequest.requestId
96
94
 
97
95
  // Ensure Content-Type is set for requests with a body
98
- if (httpRequestWithAuthHeaders.body) {
99
- const headers = httpRequestWithAuthHeaders.headers;
100
- if (!headers['content-type']) {
101
- throw new Error('Content-Type header is required for requests with a body.');
96
+ if (httpRequestWithAuthHeaders.body != null) {
97
+ const headers = httpRequestWithAuthHeaders.headers
98
+ if (headers['content-type'] == null) {
99
+ throw new Error('Content-Type header is required for requests with a body.')
102
100
  }
103
101
 
104
- const contentType = headers['content-type'];
102
+ const contentType = String(headers['content-type'] ?? '')
105
103
 
106
104
  // Transform body based on Content-Type
107
105
  if (contentType.includes('application/json')) {
108
106
  // Convert byte array to JSON string
109
- httpRequestWithAuthHeaders.body = Utils.toUTF8(httpRequestWithAuthHeaders.body);
107
+ httpRequestWithAuthHeaders.body = Utils.toUTF8(httpRequestWithAuthHeaders.body)
110
108
  } else if (contentType.includes('application/x-www-form-urlencoded')) {
111
109
  // Convert byte array to URL-encoded string
112
- httpRequestWithAuthHeaders.body = Utils.toUTF8(httpRequestWithAuthHeaders.body);
110
+ httpRequestWithAuthHeaders.body = Utils.toUTF8(httpRequestWithAuthHeaders.body)
113
111
  } else if (contentType.includes('text/plain')) {
114
112
  // Convert byte array to plain UTF-8 string
115
- httpRequestWithAuthHeaders.body = Utils.toUTF8(httpRequestWithAuthHeaders.body);
113
+ httpRequestWithAuthHeaders.body = Utils.toUTF8(httpRequestWithAuthHeaders.body)
116
114
  } else {
117
115
  // For all other content types, treat as binary data
118
- httpRequestWithAuthHeaders.body = new Uint8Array(httpRequestWithAuthHeaders.body);
116
+ httpRequestWithAuthHeaders.body = new Uint8Array(httpRequestWithAuthHeaders.body)
119
117
  }
120
118
  }
121
119
 
@@ -132,14 +130,13 @@ export class SimplifiedFetchTransport implements Transport {
132
130
  // Try parsing JSON error
133
131
  const errorInfo = await response.json()
134
132
  // Otherwise just throw whatever we got
135
- throw new Error(`HTTP ${response.status} - ${JSON.stringify(errorInfo)}`);
133
+ throw new Error(`HTTP ${response.status} - ${JSON.stringify(errorInfo)}`)
136
134
  }
137
135
 
138
136
  const parsedBody = await response.arrayBuffer()
139
137
  const payloadWriter = new Utils.Writer()
140
- if(response.headers.get('x-bsv-auth-request-id') != null)
141
- {
142
- payloadWriter.write(Utils.toArray(response.headers.get('x-bsv-auth-request-id'), 'base64'));
138
+ if (response.headers.get('x-bsv-auth-request-id') != null) {
139
+ payloadWriter.write(Utils.toArray(response.headers.get('x-bsv-auth-request-id'), 'base64'))
143
140
  }
144
141
  payloadWriter.writeVarIntNum(response.status)
145
142
 
@@ -147,7 +144,7 @@ export class SimplifiedFetchTransport implements Transport {
147
144
  // Parse response headers from the server and include only the signed headers:
148
145
  // - Include custom headers prefixed with x-bsv (excluding those starting with x-bsv-auth)
149
146
  // - Include the authorization header
150
- const includedHeaders: [string, string][] = []
147
+ const includedHeaders: Array<[string, string]> = []
151
148
  response.headers.forEach((value, key) => {
152
149
  const lowerKey = key.toLowerCase()
153
150
  if ((lowerKey.startsWith('x-bsv-') || lowerKey === 'authorization') && !lowerKey.startsWith('x-bsv-auth')) {
@@ -174,7 +171,7 @@ export class SimplifiedFetchTransport implements Transport {
174
171
  }
175
172
 
176
173
  // Handle body
177
- if (parsedBody) {
174
+ if (parsedBody != null) {
178
175
  const bodyAsArray = Array.from(new Uint8Array(parsedBody))
179
176
  payloadWriter.writeVarIntNum(bodyAsArray.length)
180
177
  payloadWriter.write(bodyAsArray)
@@ -191,11 +188,11 @@ export class SimplifiedFetchTransport implements Transport {
191
188
  yourNonce: response.headers.get('x-bsv-auth-your-nonce'),
192
189
  requestedCertificates: JSON.parse(response.headers.get('x-bsv-auth-requested-certificates')) as RequestedCertificateSet,
193
190
  payload: payloadWriter.toArray(),
194
- signature: Utils.toArray(response.headers.get('x-bsv-auth-signature'), 'hex'),
191
+ signature: Utils.toArray(response.headers.get('x-bsv-auth-signature'), 'hex')
195
192
  }
196
193
 
197
194
  // If the server didn't provide the correct authentication headers, throw an error
198
- if (!responseMessage.version) {
195
+ if (responseMessage.version == null) {
199
196
  throw new Error('HTTP server failed to authenticate')
200
197
  }
201
198
 
@@ -205,30 +202,30 @@ export class SimplifiedFetchTransport implements Transport {
205
202
  }
206
203
 
207
204
  /**
208
- * Registers a callback to handle incoming messages.
205
+ * Registers a callback to handle incoming messages.
209
206
  * This must be called before sending any messages to ensure responses can be processed.
210
- *
207
+ *
211
208
  * @param callback - A function to invoke when an incoming AuthMessage is received.
212
209
  * @returns A promise that resolves once the callback is set.
213
210
  */
214
- async onData(callback: (message: AuthMessage) => Promise<void>): Promise<void> {
211
+ async onData (callback: (message: AuthMessage) => Promise<void>): Promise<void> {
215
212
  this.onDataCallback = (m) => {
216
- callback(m)
213
+ void callback(m)
217
214
  }
218
215
  }
219
216
 
220
217
  /**
221
218
  * Deserializes a request payload from a byte array into an HTTP request-like structure.
222
- *
219
+ *
223
220
  * @param payload - The serialized payload to deserialize.
224
221
  * @returns An object representing the deserialized request, including the method,
225
222
  * URL postfix (path and query string), headers, body, and request ID.
226
223
  */
227
- deserializeRequestPayload(payload: number[]): {
228
- method: string,
229
- urlPostfix: string,
230
- headers: Record<string, string>,
231
- body: number[],
224
+ deserializeRequestPayload (payload: number[]): {
225
+ method: string
226
+ urlPostfix: string
227
+ headers: Record<string, string>
228
+ body: number[]
232
229
  requestId: string
233
230
  } {
234
231
  // Create a reader
@@ -288,4 +285,4 @@ export class SimplifiedFetchTransport implements Transport {
288
285
  requestId
289
286
  }
290
287
  }
291
- }
288
+ }
@@ -1,7 +1,7 @@
1
1
  import BigNumber from './BigNumber.js'
2
2
  import Signature from './Signature.js'
3
3
  import Curve from './Curve.js'
4
- import Point from './Point.js'
4
+ import Point, { scalarMultiplyWNAF, biModInv, BI_ZERO, biModMul, GX_BIGINT, GY_BIGINT, jpAdd, N_BIGINT, modInvN, modMulN, modN } from './Point.js'
5
5
  import DRBG from './DRBG.js'
6
6
 
7
7
  /**
@@ -161,177 +161,7 @@ export const sign = (
161
161
  * const isVerified = verify(msg, sig, key)
162
162
  */
163
163
  export const verify = (msg: BigNumber, sig: Signature, key: Point): boolean => {
164
- // Curve parameters for secp256k1
165
- const zero = BigInt(0)
166
- const one = BigInt(1)
167
- const two = BigInt(2)
168
- const three = BigInt(3)
169
- const p = BigInt(
170
- '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'
171
- ) // Field prime
172
- const n = BigInt(
173
- '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'
174
- ) // Order of the curve
175
- const G = {
176
- x: BigInt(
177
- '0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'
178
- ),
179
- y: BigInt(
180
- '0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8'
181
- )
182
- }
183
-
184
- // Modular arithmetic functions
185
- const mod = (a: bigint, m: bigint): bigint => ((a % m) + m) % m
186
- const modInv = (a: bigint, m: bigint): bigint => {
187
- // Extended Euclidean Algorithm for modular inverse
188
- let [oldr, r] = [a, m]
189
- let [olds, s] = [BigInt(1), BigInt(0)]
190
- while (r !== zero) {
191
- const q = oldr / r;
192
- [oldr, r] = [r, oldr - q * r];
193
- [olds, s] = [s, olds - q * s]
194
- }
195
- if (oldr > one) return zero // No inverse
196
- return mod(olds, m)
197
- }
198
- const modMul = (a: bigint, b: bigint, m: bigint): bigint => mod(a * b, m)
199
- const modSub = (a: bigint, b: bigint, m: bigint): bigint => mod(a - b, m)
200
-
201
- // Define constants
202
- const four = BigInt(4)
203
- const eight = BigInt(8)
204
-
205
- // Elliptic curve point operations in Jacobian coordinates
206
- interface JacobianPoint {
207
- X: bigint
208
- Y: bigint
209
- Z: bigint
210
- }
211
-
212
- // Point Doubling
213
- const pointDouble = (P: JacobianPoint): JacobianPoint => {
214
- const { X: X1, Y: Y1, Z: Z1 } = P
215
-
216
- if (Y1 === zero) {
217
- return { X: zero, Y: one, Z: zero } // Point at infinity
218
- }
219
-
220
- const Y1sq = modMul(Y1, Y1, p) // Y1^2
221
- const S = modMul(four, modMul(X1, Y1sq, p), p) // S = 4 * X1 * Y1^2
222
- const M = modMul(three, modMul(X1, X1, p), p) // M = 3 * X1^2
223
- const X3 = modSub(modMul(M, M, p), modMul(two, S, p), p) // X3 = M^2 - 2 * S
224
- const Y3 = modSub(
225
- modMul(M, modSub(S, X3, p), p),
226
- modMul(eight, modMul(Y1sq, Y1sq, p), p),
227
- p
228
- ) // Y3 = M * (S - X3) - 8 * Y1^4
229
- const Z3 = modMul(two, modMul(Y1, Z1, p), p) // Z3 = 2 * Y1 * Z1
230
-
231
- return { X: X3, Y: Y3, Z: Z3 }
232
- }
233
-
234
- // Point Addition
235
- const pointAdd = (P: JacobianPoint, Q: JacobianPoint): JacobianPoint => {
236
- if (P.Z === zero) return Q
237
- if (Q.Z === zero) return P
238
-
239
- const Z1Z1 = modMul(P.Z, P.Z, p)
240
- const Z2Z2 = modMul(Q.Z, Q.Z, p)
241
- const U1 = modMul(P.X, Z2Z2, p)
242
- const U2 = modMul(Q.X, Z1Z1, p)
243
- const S1 = modMul(P.Y, modMul(Z2Z2, Q.Z, p), p)
244
- const S2 = modMul(Q.Y, modMul(Z1Z1, P.Z, p), p)
245
-
246
- const H = modSub(U2, U1, p)
247
- const r = modSub(S2, S1, p)
248
-
249
- if (H === zero) {
250
- if (r === zero) {
251
- // P == Q
252
- return pointDouble(P)
253
- } else {
254
- // Point at infinity
255
- return { X: zero, Y: one, Z: zero }
256
- }
257
- }
258
-
259
- const HH = modMul(H, H, p)
260
- const HHH = modMul(H, HH, p)
261
- const V = modMul(U1, HH, p)
262
-
263
- const X3 = modSub(modSub(modMul(r, r, p), HHH, p), modMul(two, V, p), p)
264
- const Y3 = modSub(modMul(r, modSub(V, X3, p), p), modMul(S1, HHH, p), p)
265
- const Z3 = modMul(H, modMul(P.Z, Q.Z, p), p)
266
-
267
- return { X: X3, Y: Y3, Z: Z3 }
268
- }
269
-
270
- // Scalar Multiplication
271
- const scalarMultiply = (
272
- k: bigint,
273
- P: { x: bigint, y: bigint }
274
- ): JacobianPoint => {
275
- const N: JacobianPoint = { X: P.x, Y: P.y, Z: one }
276
- let Q: JacobianPoint = { X: zero, Y: one, Z: zero } // Point at infinity
277
-
278
- const kBin = k.toString(2)
279
- for (let i = 0; i < kBin.length; i++) {
280
- Q = pointDouble(Q)
281
- if (kBin[i] === '1') {
282
- Q = pointAdd(Q, N)
283
- }
284
- }
285
- return Q
286
- }
287
-
288
- // Verify Function Using Jacobian Coordinates
289
- const verifyECDSA = (
290
- hash: bigint,
291
- publicKey: { x: bigint, y: bigint },
292
- signature: { r: bigint, s: bigint }
293
- ): boolean => {
294
- const { r, s } = signature
295
- const z = hash
296
-
297
- // Check r and s are in [1, n - 1]
298
- if (r <= zero || r >= n || s <= zero || s >= n) {
299
- return false
300
- }
301
-
302
- const w = modInv(s, n) // w = s^-1 mod n
303
- if (w === zero) {
304
- return false // No inverse exists
305
- }
306
- const u1 = modMul(z, w, n)
307
- const u2 = modMul(r, w, n)
308
-
309
- // Compute point R = u1 * G + u2 * Q
310
- const RG = scalarMultiply(u1, G)
311
- const RQ = scalarMultiply(u2, publicKey)
312
- const R = pointAdd(RG, RQ)
313
-
314
- if (R.Z === zero) {
315
- // Point at infinity
316
- return false
317
- }
318
-
319
- // Compute affine x-coordinate x1 = X / Z^2 mod p
320
- const ZInv = modInv(R.Z, p)
321
- if (ZInv === zero) {
322
- return false // No inverse exists
323
- }
324
- const ZInv2 = modMul(ZInv, ZInv, p)
325
- const x1affine = modMul(R.X, ZInv2, p)
326
-
327
- // Compute v = x1_affine mod n
328
- const v = mod(x1affine, n)
329
-
330
- // Signature is valid if v == r mod n
331
- return v === r
332
- }
333
-
334
- // Convert inputs to BigInt
164
+ // Convert inputs to BigInt
335
165
  const hash = BigInt('0x' + msg.toString(16))
336
166
  if ((key.x == null) || (key.y == null)) {
337
167
  throw new Error('Invalid public key: missing coordinates.')
@@ -346,5 +176,32 @@ export const verify = (msg: BigNumber, sig: Signature, key: Point): boolean => {
346
176
  s: BigInt('0x' + sig.s.toString(16))
347
177
  }
348
178
 
349
- return verifyECDSA(hash, publicKey, signature)
179
+ const { r, s } = signature
180
+ const z = hash
181
+
182
+ // Check r and s are in [1, n - 1]
183
+ if (r <= BI_ZERO || r >= N_BIGINT || s <= BI_ZERO || s >= N_BIGINT) {
184
+ return false
185
+ }
186
+
187
+ // ── compute u₁ = z·s⁻¹ mod n and u₂ = r·s⁻¹ mod n ───────────────────────
188
+ const w = modInvN(s) // s⁻¹ mod n
189
+ if (w === 0n) return false // should never happen
190
+ const u1 = modMulN(z, w)
191
+ const u2 = modMulN(r, w)
192
+
193
+ // ── R = u₁·G + u₂·Q (Jacobian, window‑NAF) ──────────────────────────────
194
+ const RG = scalarMultiplyWNAF(u1, { x: GX_BIGINT, y: GY_BIGINT })
195
+ const RQ = scalarMultiplyWNAF(u2, publicKey)
196
+ const R = jpAdd(RG, RQ)
197
+ if (R.Z === 0n) return false // point at infinity
198
+
199
+ // ── affine x‑coordinate of R (mod p) ─────────────────────────────────────
200
+ const zInv = biModInv(R.Z) // (Z⁻¹ mod p)
201
+ const zInv2 = biModMul(zInv, zInv) // Z⁻²
202
+ const xAff = biModMul(R.X, zInv2) // X / Z² mod p
203
+
204
+ // ── v = xAff mod n and final check ───────────────────────────────────────
205
+ const v = modN(xAff)
206
+ return v === r
350
207
  }