]
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
}
/**
*/
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]
#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 {
* 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]
// 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)
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]
}
#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)
* @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')
}