@bsv/sdk 1.9.31 → 1.10.2
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/auth/Peer.js +68 -48
- package/dist/cjs/src/auth/Peer.js.map +1 -1
- package/dist/cjs/src/identity/IdentityClient.js +124 -20
- package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
- package/dist/cjs/src/primitives/BigNumber.js +28 -54
- package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
- package/dist/cjs/src/primitives/ECDSA.js +36 -1
- package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
- package/dist/cjs/src/primitives/ReductionContext.js +35 -46
- package/dist/cjs/src/primitives/ReductionContext.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/Peer.js +68 -48
- package/dist/esm/src/auth/Peer.js.map +1 -1
- package/dist/esm/src/identity/IdentityClient.js +124 -20
- package/dist/esm/src/identity/IdentityClient.js.map +1 -1
- package/dist/esm/src/primitives/BigNumber.js +28 -54
- package/dist/esm/src/primitives/BigNumber.js.map +1 -1
- package/dist/esm/src/primitives/ECDSA.js +36 -1
- package/dist/esm/src/primitives/ECDSA.js.map +1 -1
- package/dist/esm/src/primitives/ReductionContext.js +35 -46
- package/dist/esm/src/primitives/ReductionContext.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/Peer.d.ts.map +1 -1
- package/dist/types/src/auth/types.d.ts +2 -0
- package/dist/types/src/auth/types.d.ts.map +1 -1
- package/dist/types/src/identity/IdentityClient.d.ts +8 -0
- package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
- package/dist/types/src/primitives/BigNumber.d.ts +8 -0
- package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
- package/dist/types/src/primitives/ECDSA.d.ts +24 -0
- package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
- package/dist/types/src/primitives/ReductionContext.d.ts +9 -0
- package/dist/types/src/primitives/ReductionContext.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/dist/umd/bundle.js.map +1 -1
- package/docs/index.md +15 -1
- package/docs/reference/auth.md +2 -0
- package/docs/reference/messages.md +0 -24
- package/docs/reference/primitives.md +91 -31
- package/package.json +1 -1
- package/src/auth/Peer.ts +122 -57
- package/src/auth/__tests/Peer.test.ts +166 -257
- package/src/auth/types.ts +2 -0
- package/src/identity/IdentityClient.ts +153 -29
- package/src/identity/__tests/IdentityClient.test.ts +289 -1
- package/src/primitives/BigNumber.ts +27 -31
- package/src/primitives/ECDSA.ts +41 -2
- package/src/primitives/ReductionContext.ts +44 -48
- package/src/primitives/__tests/ECDSA.test.ts +16 -0
package/src/primitives/ECDSA.ts
CHANGED
|
@@ -22,6 +22,8 @@ import DRBG from './DRBG.js'
|
|
|
22
22
|
* @example
|
|
23
23
|
* let msg = new BigNumber('1234567890abcdef', 16);
|
|
24
24
|
* let truncatedMsg = truncateToN(msg);
|
|
25
|
+
*
|
|
26
|
+
* This behavior follows the message truncation rules defined in FIPS 186-4.
|
|
25
27
|
*/
|
|
26
28
|
function truncateToN (
|
|
27
29
|
msg: BigNumber,
|
|
@@ -32,7 +34,7 @@ function truncateToN (
|
|
|
32
34
|
if (delta > 0) {
|
|
33
35
|
msg.iushrn(delta)
|
|
34
36
|
}
|
|
35
|
-
if (truncOnly
|
|
37
|
+
if (truncOnly !== true && msg.cmp(curve.n) >= 0) {
|
|
36
38
|
return msg.sub(curve.n)
|
|
37
39
|
} else {
|
|
38
40
|
return msg
|
|
@@ -68,12 +70,34 @@ const halfN = N_BIGINT >> 1n
|
|
|
68
70
|
* const key = new BigNumber('123456')
|
|
69
71
|
* const signature = sign(msg, key)
|
|
70
72
|
*/
|
|
73
|
+
/**
|
|
74
|
+
* SECURITY NOTE:
|
|
75
|
+
*
|
|
76
|
+
* This function implements ECDSA signing and expects `msg` to be the output of
|
|
77
|
+
* a cryptographic hash function (e.g. SHA-256), not an arbitrary-length message.
|
|
78
|
+
*
|
|
79
|
+
* Per FIPS 186-4 / SEC 1, the message representative used by ECDSA must not
|
|
80
|
+
* exceed the bit length of the curve order `n`. Inputs larger than `n` must be
|
|
81
|
+
* hashed before signing.
|
|
82
|
+
*
|
|
83
|
+
* As a short-term mitigation for TOB-22, this implementation explicitly rejects
|
|
84
|
+
* messages whose bit length exceeds that of the curve order.
|
|
85
|
+
*
|
|
86
|
+
* Long-term, callers SHOULD always hash messages before invoking `sign()`.
|
|
87
|
+
*/
|
|
71
88
|
export const sign = (
|
|
72
89
|
msg: BigNumber,
|
|
73
90
|
key: BigNumber,
|
|
74
91
|
forceLowS: boolean = false,
|
|
75
92
|
customK?: BigNumber | ((iter: number) => BigNumber)
|
|
76
93
|
): Signature => {
|
|
94
|
+
const nBitLength = curve.n.bitLength()
|
|
95
|
+
if (msg.bitLength() > nBitLength) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`ECDSA message is too large: expected <= ${nBitLength} bits. Callers must hash messages before signing.`
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
77
101
|
msg = truncateToN(msg)
|
|
78
102
|
const msgBig = bnToBigInt(msg)
|
|
79
103
|
const keyBig = bnToBigInt(key)
|
|
@@ -163,8 +187,23 @@ export const sign = (
|
|
|
163
187
|
* const signature = sign(msg, new BigNumber('123456'))
|
|
164
188
|
* const isVerified = verify(msg, sig, key)
|
|
165
189
|
*/
|
|
190
|
+
/**
|
|
191
|
+
* SECURITY NOTE:
|
|
192
|
+
*
|
|
193
|
+
* This verification routine assumes that `msg` is a hashed message
|
|
194
|
+
* representative produced using the same hash function used during signing.
|
|
195
|
+
*
|
|
196
|
+
* As part of TOB-22 short-term hardening, messages exceeding the curve order
|
|
197
|
+
* bit length are rejected to prevent misuse with non-hashed inputs.
|
|
198
|
+
*/
|
|
166
199
|
export const verify = (msg: BigNumber, sig: Signature, key: Point): boolean => {
|
|
167
|
-
|
|
200
|
+
const nBitLength = curve.n.bitLength()
|
|
201
|
+
if (msg.bitLength() > nBitLength) {
|
|
202
|
+
// could be throw or return false; returning false is typical for verify
|
|
203
|
+
return false
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Convert inputs to BigInt
|
|
168
207
|
const hash = bnToBigInt(msg)
|
|
169
208
|
if ((key.x == null) || (key.y == null)) {
|
|
170
209
|
throw new Error('Invalid public key: missing coordinates.')
|
|
@@ -2,6 +2,16 @@ import BigNumber from './BigNumber.js'
|
|
|
2
2
|
import K256 from './K256.js'
|
|
3
3
|
import Mersenne from './Mersenne.js'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* SECURITY NOTE:
|
|
7
|
+
* This reduction context avoids obvious variable-time constructs (such as
|
|
8
|
+
* sliding-window exponentiation and conditional modular reduction) to reduce
|
|
9
|
+
* timing side-channel leakage. However, JavaScript BigInt arithmetic does not
|
|
10
|
+
* provide constant-time guarantees. These mitigations improve resistance to
|
|
11
|
+
* coarse timing attacks but do not make the implementation suitable for
|
|
12
|
+
* hostile multi-tenant or shared-CPU environments.
|
|
13
|
+
*/
|
|
14
|
+
|
|
5
15
|
/**
|
|
6
16
|
* A base reduction engine that provides several arithmetic operations over
|
|
7
17
|
* big numbers under a modulus context. It's particularly suitable for
|
|
@@ -152,11 +162,21 @@ export default class ReductionContext {
|
|
|
152
162
|
add (a: BigNumber, b: BigNumber): BigNumber {
|
|
153
163
|
this.verify2(a, b)
|
|
154
164
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
// Start from a red clone
|
|
166
|
+
const res = a.clone()
|
|
167
|
+
|
|
168
|
+
// In-place add keeps red context
|
|
169
|
+
res.iadd(b)
|
|
170
|
+
|
|
171
|
+
// Always subtract modulus (still red)
|
|
172
|
+
res.isub(this.m)
|
|
173
|
+
|
|
174
|
+
// If negative, add modulus back
|
|
175
|
+
if (res.isNeg()) {
|
|
176
|
+
res.iadd(this.m)
|
|
158
177
|
}
|
|
159
|
-
|
|
178
|
+
|
|
179
|
+
return res
|
|
160
180
|
}
|
|
161
181
|
|
|
162
182
|
/**
|
|
@@ -178,11 +198,14 @@ export default class ReductionContext {
|
|
|
178
198
|
iadd (a: BigNumber, b: BigNumber): BigNumber {
|
|
179
199
|
this.verify2(a, b)
|
|
180
200
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
201
|
+
a.iadd(b)
|
|
202
|
+
a.isub(this.m)
|
|
203
|
+
|
|
204
|
+
if (a.isNeg()) {
|
|
205
|
+
a.iadd(this.m)
|
|
184
206
|
}
|
|
185
|
-
|
|
207
|
+
|
|
208
|
+
return a
|
|
186
209
|
}
|
|
187
210
|
|
|
188
211
|
/**
|
|
@@ -439,52 +462,25 @@ export default class ReductionContext {
|
|
|
439
462
|
* context.pow(new BigNumber(3), new BigNumber(2)); // Returns 2 (3^2 % 7)
|
|
440
463
|
*/
|
|
441
464
|
pow (a: BigNumber, num: BigNumber): BigNumber {
|
|
465
|
+
this.verify1(a)
|
|
466
|
+
|
|
442
467
|
if (num.isZero()) return new BigNumber(1).toRed(this)
|
|
443
|
-
if (num.cmpn(1) === 0) return a.clone()
|
|
444
|
-
|
|
445
|
-
const windowSize = 4
|
|
446
|
-
const wnd = new Array(1 << windowSize)
|
|
447
|
-
wnd[0] = new BigNumber(1).toRed(this)
|
|
448
|
-
wnd[1] = a
|
|
449
|
-
let i = 2
|
|
450
|
-
for (; i < wnd.length; i++) {
|
|
451
|
-
wnd[i] = this.mul(wnd[i - 1], a)
|
|
452
|
-
}
|
|
453
468
|
|
|
454
|
-
let
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
let start = num.bitLength() % 26
|
|
458
|
-
if (start === 0) {
|
|
459
|
-
start = 26
|
|
460
|
-
}
|
|
469
|
+
let result = new BigNumber(1).toRed(this)
|
|
470
|
+
const base = a.clone()
|
|
471
|
+
const bits = num.bitLength()
|
|
461
472
|
|
|
462
|
-
for (i =
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if (bit === 0 && current === 0) {
|
|
471
|
-
currentLen = 0
|
|
472
|
-
continue
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
current <<= 1
|
|
476
|
-
current |= bit
|
|
477
|
-
currentLen++
|
|
478
|
-
if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue
|
|
479
|
-
|
|
480
|
-
res = this.mul(res, wnd[current])
|
|
481
|
-
currentLen = 0
|
|
482
|
-
current = 0
|
|
473
|
+
for (let i = bits - 1; i >= 0; i--) {
|
|
474
|
+
// Always square
|
|
475
|
+
result = this.sqr(result)
|
|
476
|
+
|
|
477
|
+
// Multiply if bit is set
|
|
478
|
+
if (num.testn(i)) {
|
|
479
|
+
result = this.mul(result, base)
|
|
483
480
|
}
|
|
484
|
-
start = 26
|
|
485
481
|
}
|
|
486
482
|
|
|
487
|
-
return
|
|
483
|
+
return result
|
|
488
484
|
}
|
|
489
485
|
|
|
490
486
|
/**
|
|
@@ -129,4 +129,20 @@ describe('ECDSA', () => {
|
|
|
129
129
|
|
|
130
130
|
expect(ECDSA.verify(msg, sig, pub)).toBe(true)
|
|
131
131
|
})
|
|
132
|
+
|
|
133
|
+
it('should reject signing messages larger than curve order bit length (TOB-22)', () => {
|
|
134
|
+
// Create a message definitely larger than secp256k1 order size
|
|
135
|
+
const tooLargeMsg = new BigNumber(1).iushln(curve.n.bitLength() + 1)
|
|
136
|
+
|
|
137
|
+
expect(() =>
|
|
138
|
+
ECDSA.sign(tooLargeMsg, key)
|
|
139
|
+
).toThrow(/message is too large/i)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('verify should return false for messages larger than curve order bit length (TOB-22)', () => {
|
|
143
|
+
const signature = ECDSA.sign(msg, key)
|
|
144
|
+
const tooLargeMsg = new BigNumber(1).iushln(curve.n.bitLength() + 1)
|
|
145
|
+
|
|
146
|
+
expect(ECDSA.verify(tooLargeMsg, signature, pub)).toBe(false)
|
|
147
|
+
})
|
|
132
148
|
})
|