Skip to content

Commit 26f8954

Browse files
Security Fix: Enforce strict hex validation (TOB-21)
1 parent fa011bb commit 26f8954

File tree

2 files changed

+37
-42
lines changed

2 files changed

+37
-42
lines changed

src/primitives/Hash.ts

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -170,53 +170,48 @@ abstract class BaseHash {
170170
* @returns Returns an array denoting the padding.
171171
*/
172172
private _pad (): number[] {
173-
const len = this.pendingTotal
174-
175-
// 🔐 New: guarantee len is a sane byte count
176-
if (!Number.isSafeInteger(len) || len < 0) {
177-
// Anything outside the safe integer range (or negative)
178-
// must be treated as "too long" by definition.
179-
throw new Error('Message too long for this hash function')
180-
}
173+
const len = this.pendingTotal
174+
if (!Number.isSafeInteger(len) || len < 0) {
175+
throw new Error('Message too long for this hash function')
176+
}
181177

182-
const bytes = this._delta8
183-
const k = bytes - ((len + this.padLength) % bytes)
184-
const res = new Array(k + this.padLength)
185-
res[0] = 0x80
186-
let i: number
187-
for (i = 1; i < k; i++) {
188-
res[i] = 0
189-
}
178+
const bytes = this._delta8
179+
const k = bytes - ((len + this.padLength) % bytes)
180+
const res = new Array(k + this.padLength)
181+
res[0] = 0x80
182+
let i: number
183+
for (i = 1; i < k; i++) {
184+
res[i] = 0
185+
}
186+
const lengthBytes = this.padLength
187+
const maxBits = 1n << BigInt(lengthBytes * 8)
188+
let totalBits = BigInt(len) * 8n
190189

191-
// Append length
192-
const lengthBytes = this.padLength
193-
const maxBits = 1n << BigInt(lengthBytes * 8)
194-
let totalBits = BigInt(len) * 8n
190+
if (totalBits >= maxBits) {
191+
throw new Error('Message too long for this hash function')
192+
}
195193

196-
if (totalBits >= maxBits) {
197-
throw new Error('Message too long for this hash function')
198-
}
194+
if (this.endian === 'big') {
195+
const lenArray = new Array<number>(lengthBytes)
199196

200-
if (this.endian === 'big') {
201-
const lenArray = new Array<number>(lengthBytes)
197+
for (let b = lengthBytes - 1; b >= 0; b--) {
198+
lenArray[b] = Number(totalBits & 0xffn)
199+
totalBits >>= 8n
200+
}
202201

203-
for (let b = lengthBytes - 1; b >= 0; b--) {
204-
lenArray[b] = Number(totalBits & 0xffn)
205-
totalBits >>= 8n
202+
for (let b = 0; b < lengthBytes; b++) {
203+
res[i++] = lenArray[b]
204+
}
205+
} else {
206+
for (let b = 0; b < lengthBytes; b++) {
207+
res[i++] = Number(totalBits & 0xffn)
208+
totalBits >>= 8n
209+
}
206210
}
207211

208-
for (let b = 0; b < lengthBytes; b++) {
209-
res[i++] = lenArray[b]
210-
}
211-
} else {
212-
for (let b = 0; b < lengthBytes; b++) {
213-
res[i++] = Number(totalBits & 0xffn)
214-
totalBits >>= 8n
215-
}
212+
return res
216213
}
217-
218-
return res
219-
}}
214+
}
220215

221216
function isSurrogatePair (msg: string, i: number): boolean {
222217
if ((msg.charCodeAt(i) & 0xfc00) !== 0xd800) {

src/primitives/hex.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// src/primitives/hex.ts
22

33
// Accepts empty string because empty byte arrays are valid in Bitcoin.
4-
const PURE_HEX_REGEX = /^[0-9a-fA-F]*$/;
4+
const PURE_HEX_REGEX = /^[0-9a-fA-F]*$/
55

66
export function assertValidHex(msg: string): void {
77
if (typeof msg !== 'string') {
8-
console.error("assertValidHex FAIL (non-string):", msg);
9-
throw new Error('Invalid hex string');
8+
console.error("assertValidHex FAIL (non-string):", msg)
9+
throw new Error('Invalid hex string')
1010
}
1111

1212
// allow empty

0 commit comments

Comments
 (0)