]> git.codecow.com Git - libnemo.git/commitdiff
Set final block compression flag with constant time bit flip. Remove bytelength requi...
authorChris Duncan <chris@codecow.com>
Wed, 1 Jul 2026 07:17:05 +0000 (00:17 -0700)
committerChris Duncan <chris@codecow.com>
Wed, 1 Jul 2026 07:17:05 +0000 (00:17 -0700)
src/lib/crypto/blake2b.ts

index 4dc3153f0c85248f3c63eea324ec1e5ab77c7ad2..fcd7528fe505228d72289224b0edda50a6b98bb5 100644 (file)
@@ -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')
                }