From 45f60ef883cec8e1089b7fab44433b1528d8438b Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Wed, 1 Jul 2026 00:17:05 -0700 Subject: [PATCH] Set final block compression flag with constant time bit flip. Remove bytelength requirement to assert as bytes. Pass literal doubled index values to G function instead of calculating. Revert to conditional Uint8Array cast on buffer input for update call. --- src/lib/crypto/blake2b.ts | 40 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/lib/crypto/blake2b.ts b/src/lib/crypto/blake2b.ts index 4dc3153..fcd7528 100644 --- a/src/lib/crypto/blake2b.ts +++ b/src/lib/crypto/blake2b.ts @@ -33,7 +33,7 @@ const SIGMA: number[][] = [ ] function isBytes (a: unknown): a is Bytes { - return a instanceof Uint8Array && a.buffer instanceof ArrayBuffer && a.byteLength > 0 + return a instanceof Uint8Array && a.buffer instanceof ArrayBuffer } /** @@ -47,13 +47,13 @@ function isBytes (a: unknown): a is Bytes { */ export class Blake2b { #G (r: number, i: number, a: number, b: number, c: number, d: number): void { - this.#v[a] += this.#v[b] + this.#m[SIGMA[r][i << 1]] + this.#v[a] += this.#v[b] + this.#m[SIGMA[r][i]] this.#v[d] ^= this.#v[a] this.#v[d] = (this.#v[d] >> 32n) | (this.#v[d] << 32n) this.#v[c] += this.#v[d] this.#v[b] ^= this.#v[c] this.#v[b] = (this.#v[b] >> 24n) | (this.#v[b] << 40n) - this.#v[a] += this.#v[b] + this.#m[SIGMA[r][(i << 1) + 1]] + this.#v[a] += this.#v[b] + this.#m[SIGMA[r][i + 1]] this.#v[d] ^= this.#v[a] this.#v[d] = (this.#v[d] >> 16n) | (this.#v[d] << 48n) this.#v[c] += this.#v[d] @@ -63,13 +63,13 @@ export class Blake2b { #ROUND (r: number): void { this.#G(r, 0, 0, 4, 8, 12) - this.#G(r, 1, 1, 5, 9, 13) - this.#G(r, 2, 2, 6, 10, 14) - this.#G(r, 3, 3, 7, 11, 15) - this.#G(r, 4, 0, 5, 10, 15) - this.#G(r, 5, 1, 6, 11, 12) - this.#G(r, 6, 2, 7, 8, 13) - this.#G(r, 7, 3, 4, 9, 14) + this.#G(r, 2, 1, 5, 9, 13) + this.#G(r, 4, 2, 6, 10, 14) + this.#G(r, 6, 3, 7, 11, 15) + this.#G(r, 8, 0, 5, 10, 15) + this.#G(r, 10, 1, 6, 11, 12) + this.#G(r, 12, 2, 7, 8, 13) + this.#G(r, 14, 3, 4, 9, 14) } #blake2bInit (length: number, key?: Bytes, salt?: Bytes, personal?: Bytes): void { @@ -99,10 +99,9 @@ export class Blake2b { * Compression function called during three phases: on keying (if applicable), * each time the input buffer is filled, and when finalizing message block. * - * @param {boolean} final - If true, invert bits of v[14] + * @param {bigint} final - Mask used to invert bits of v[14] on final block */ - #blake2bCompress (final: boolean): void { - + #blake2bCompress (final: 0n | 0xffffffffffffffffn): void { // init work variables for (let i = 0; i < 8; i++) { this.#v[i] = this.#h[i] @@ -111,14 +110,11 @@ export class Blake2b { // lo 64 bits of counter this.#v[12] ^= this.#t & 0xffffffffffffffffn - // hi 64 bits of counter this.#v[13] ^= this.#t & 0xffffffffffffffff0000000000000000n - // is last block flag set? - if (final) { - this.#v[14] = ~this.#v[14] - } + // flips bits to ~v[14] in constant time if final block mask is all 1's + this.#v[14] ^= final // copy input buffer to message block const buf = new DataView(this.#b.buffer) @@ -146,7 +142,7 @@ export class Blake2b { if (this.#c === this.#b.byteLength) { // is buffer full? this.#t += BigInt(this.#b.byteLength) // increment total byte counter this.#c = 0 // reset buffer counter to zero - this.#blake2bCompress(false) // compress (not final) + this.#blake2bCompress(0n) // compress (not final) } this.#b[this.#c++] = input[i] } @@ -160,7 +156,7 @@ export class Blake2b { #blake2bFinal (out: Bytes): void { this.#t += BigInt(this.#c) // add final message block size to total bytes this.#b.fill(0, this.#c) // pad final block with zeros - this.#blake2bCompress(true) // set final block flag and compress + this.#blake2bCompress(0xffffffffffffffffn) // set final block flag and compress const data = new DataView(this.#h.buffer) for (let i = 0; i < out.length; i++) { out[i] = data.getUint8(i) @@ -238,7 +234,9 @@ export class Blake2b { * @returns {Blake2b} */ update (input: ArrayBuffer | Bytes): Blake2b { - input = new Uint8Array(input) + if (input instanceof ArrayBuffer) { + input = new Uint8Array(input) + } if (!isBytes(input)) { throw new TypeError('input must be bytes') } -- 2.52.0