@bsv/sdk 1.6.17 → 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.
- package/README.md +11 -11
- package/dist/cjs/package.json +4 -8
- package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +39 -39
- package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
- package/dist/cjs/src/overlay-tools/LookupResolver.js +4 -0
- package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/cjs/src/primitives/ECDSA.js +26 -125
- package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
- package/dist/cjs/src/primitives/Hash.js +660 -436
- package/dist/cjs/src/primitives/Hash.js.map +1 -1
- package/dist/cjs/src/primitives/Point.js +226 -213
- package/dist/cjs/src/primitives/Point.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +4 -4
- package/dist/cjs/src/transaction/Transaction.js +3 -3
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +39 -39
- package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
- package/dist/esm/src/overlay-tools/LookupResolver.js +4 -0
- package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/esm/src/primitives/ECDSA.js +26 -125
- package/dist/esm/src/primitives/ECDSA.js.map +1 -1
- package/dist/esm/src/primitives/Hash.js +672 -444
- package/dist/esm/src/primitives/Hash.js.map +1 -1
- package/dist/esm/src/primitives/Point.js +211 -213
- package/dist/esm/src/primitives/Point.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +4 -4
- package/dist/esm/src/transaction/Transaction.js +3 -3
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts +1 -1
- package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -1
- package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
- package/dist/types/src/primitives/Hash.d.ts +12 -19
- package/dist/types/src/primitives/Hash.d.ts.map +1 -1
- package/dist/types/src/primitives/Point.d.ts +34 -0
- package/dist/types/src/primitives/Point.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +20 -1
- package/dist/umd/bundle.js.map +1 -0
- package/package.json +4 -8
- package/src/auth/transports/SimplifiedFetchTransport.ts +64 -67
- package/src/overlay-tools/LookupResolver.ts +5 -0
- package/src/primitives/ECDSA.ts +30 -173
- package/src/primitives/Hash.ts +752 -589
- package/src/primitives/Point.ts +222 -247
- package/src/transaction/Beef.ts +4 -4
- 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.
|
|
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": "
|
|
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
|
-
"
|
|
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-
|
|
3
|
-
import { AuthMessage, RequestedCertificateSet, Transport } from
|
|
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 (
|
|
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(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
|
+
}
|
|
@@ -33,6 +33,11 @@ export type LookupAnswer =
|
|
|
33
33
|
|
|
34
34
|
/** Default SLAP trackers */
|
|
35
35
|
export const DEFAULT_SLAP_TRACKERS: string[] = [
|
|
36
|
+
// BSVA clusters
|
|
37
|
+
'https://overlay-us-1.bsvb.tech',
|
|
38
|
+
'https://overlay-eu-1.bsvb.tech',
|
|
39
|
+
'https://overlay-ap-1.bsvb.tech',
|
|
40
|
+
|
|
36
41
|
// Babbage primary overlay service
|
|
37
42
|
'https://users.bapp.dev'
|
|
38
43
|
|
package/src/primitives/ECDSA.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
}
|