@li0ard/streebog
Version:
Streebog hash function in pure TypeScript
113 lines (112 loc) • 3.96 kB
JavaScript
import { BLOCKSIZE } from "./const.js";
import { concatBytes } from "@noble/hashes/utils.js";
import { transformG, add512, pad } from "./utils.js";
/**
* Streebog core class
*/
class Streebog {
is512;
buffer;
blockLen = BLOCKSIZE;
outputLen;
canXOF = false;
/**
* Streebog core constructor
* @param is512 Use 512 bits version of algorithm
*/
constructor(is512) {
this.is512 = is512;
this.buffer = new Uint8Array();
this.outputLen = is512 ? 64 : 32;
}
/** Reset hash state */
destroy() { this.buffer = new Uint8Array(); }
/** Update hash buffer */
update(data) {
this.buffer = concatBytes(this.buffer, data);
return this;
}
/** Finalize hash computation and return result as Uint8Array */
digest() {
const buffer = new Uint8Array(this.outputLen);
this.digestInto(buffer);
return buffer;
}
/**
* Finalize hash computation and write result into Uint8Array
* @param buf - Output Uint8Array
*/
digestInto(buf) {
const message = this.buffer.slice().reverse();
let n = new Uint8Array(BLOCKSIZE);
let sigma = new Uint8Array(BLOCKSIZE);
let hash = new Uint8Array(64).fill(this.is512 ? 0 : 1);
let blocks = 1;
for (let i = message.length; i >= BLOCKSIZE; i -= BLOCKSIZE) {
const pos = message.length - blocks * BLOCKSIZE;
hash = transformG(n, hash, message.slice(pos, pos + BLOCKSIZE));
n = add512(n, new Uint8Array([0, 0, 2, 0]));
sigma = add512(sigma, message.slice(pos, pos + BLOCKSIZE));
blocks++;
}
let paddedMsg = new Uint8Array(BLOCKSIZE);
const msg = message.slice(0, message.length - (blocks - 1) * 64);
if (msg.length < BLOCKSIZE) {
paddedMsg = pad(paddedMsg);
paddedMsg[BLOCKSIZE - msg.length - 1] = 0x01;
for (let i = 0; i < msg.length; i++)
paddedMsg[BLOCKSIZE - msg.length + i] = msg[i];
}
const msgLen = new Uint8Array(4);
for (let i = 0; i < 4; i++)
msgLen[i] = (msg.length * 8 >> i * 8) & 255;
hash = transformG(new Uint8Array(64), transformG(new Uint8Array(64), transformG(n, hash, paddedMsg), add512(n, msgLen.reverse())), add512(sigma, paddedMsg));
if (this.is512)
buf.set(hash.slice().reverse());
else
buf.set(hash.slice(0, 32).reverse());
this.destroy();
}
}
/** Streebog 256 aka `GOST R 34.11-2012 256 bits` */
export class Streebog256 extends Streebog {
/** Streebog 256 aka `GOST R 34.11-2012 256 bits` */
constructor() { super(false); }
/** Create hash instance */
static create() { return new Streebog256(); }
/** Clone hash instance */
clone() { return this._cloneInto(); }
_cloneInto(to) {
to ||= new Streebog256();
to.buffer = this.buffer.slice();
return to;
}
}
/** Streebog 512 bit aka `GOST R 34.11-2012 512 bits` */
export class Streebog512 extends Streebog {
/** Streebog 512 bit aka `GOST R 34.11-2012 512 bits` */
constructor() { super(true); }
/** Create hash instance */
static create() { return new Streebog512(); }
/** Clone hash instance */
clone() { return this._cloneInto(); }
_cloneInto(to) {
to ||= new Streebog512();
to.buffer = this.buffer.slice();
return to;
}
}
/**
* Compute hash with `Streebog 256 bit` (aka `GOST R 34.11-2012 256 bits`)
* @param input Input bytes
*/
export const streebog256 = (input) => new Streebog256().update(input).digest();
/**
* Compute hash with `Streebog 512 bit` (aka `GOST R 34.11-2012 512 bits`)
* @param input Input bytes
* @returns {Uint8Array}
*/
export const streebog512 = (input) => new Streebog512().update(input).digest();
export * from "./hmac.js";
export * from "./kdf.js";
export * from "./pbkdf2.js";