UNPKG

@li0ard/streebog

Version:

Streebog hash function in pure TypeScript

113 lines (112 loc) 3.75 kB
import { A, BLOCKSIZE, C, PI, TAU } from "./const.js"; import { hexToBytes } from "@noble/hashes/utils.js"; /** XOR two Uint8Array arrays */ export const xor = (a, b) => { let mlen = Math.min(a.length, b.length); let result = new Uint8Array(mlen); for (let i = 0; i < mlen; i++) result[i] = a[i] ^ b[i]; return result.slice(); }; /** Addition of two 512 bit numbers */ export const add512 = (a, b) => { const c = new Uint8Array(64); const tmpA = new Uint8Array(64); const tmpB = new Uint8Array(64); for (let i = 0; i < a.length; i++) tmpA[63 - i] = a[a.length - i - 1]; for (let i = 0; i < b.length; i++) tmpB[63 - i] = b[b.length - i - 1]; for (let i = 63, tmp = 0; i >= 0; i--) { tmp = tmpA[i] + tmpB[i] + (tmp >> 8); c[i] = tmp & 0xff; } return c; }; /** * `S`-transformation. * The `S` function is a regular substitution function. Each byte of the * 512-bit input sequence is replaced by the corresponding byte from * the `PI` substitution table. */ export const transformS = (input) => { const result = new Uint8Array(BLOCKSIZE); for (let i = 0; i < BLOCKSIZE; i++) result[i] = PI[input[i]]; return result; }; /** * `P`-transformation. * Permutation function. For each pair of bytes from the input sequence, * one byte is replaced by another. */ export const transformP = (input) => { const result = new Uint8Array(BLOCKSIZE); for (let i = 0; i < BLOCKSIZE; i++) result[i] = input[TAU[i]]; return result; }; /** * `L`-transformation. * Represents the multiplication of a 64-bit input vector by a 64x64 binary matrix `A`. */ export const transformL = (input) => { const result = new Uint8Array(BLOCKSIZE); for (let i = 0; i < 8; i++) { const parts = new Uint32Array(2); const tmp = input.slice(i * 8, i * 8 + 8).reverse(); for (let j = 0; j < 8; j++) { for (let k = 0; k < 8; k++) { if ((tmp[7 - j] >> 7 - k) & 1) { parts[0] ^= A[j * 16 + k * 2]; parts[1] ^= A[j * 16 + k * 2 + 1]; } } } result[i * 8] = parts[0] >> 24; result[i * 8 + 1] = (parts[0] << 8) >> 24; result[i * 8 + 2] = (parts[0] << 16) >> 24; result[i * 8 + 3] = (parts[0] << 24) >> 24; result[i * 8 + 4] = parts[1] >> 24; result[i * 8 + 5] = (parts[1] << 8) >> 24; result[i * 8 + 6] = (parts[1] << 16) >> 24; result[i * 8 + 7] = (parts[1] << 24) >> 24; } return result; }; /** * `E`-transformation. * Part of compression function. */ export const transformE = (block, keys) => { let c = xor(block, keys); for (let i = 0; i < 12; i++) { block = transformL(transformP(transformS(xor(block, C[i])))); c = xor(transformL(transformP(transformS(c))), block); } return c; }; /** Compression function `G` aka XSPLEXX-algorithm */ export const transformG = (hash, n, message) => { return xor(xor(transformE(transformL(transformP(transformS(xor(n, hash)))), message), n), message); }; /** Convert number to bytes (Big-Endian) */ export function numberToBytesBE(n, len) { let num = n.toString(16).padStart(len * 2, '0'); while (num.length % 2 != 0) num = "0" + num; return hexToBytes(num); } const getPadLength = (dataLength) => { if (dataLength < BLOCKSIZE) return BLOCKSIZE - dataLength; if (dataLength % BLOCKSIZE == 0) return 0; return BLOCKSIZE - dataLength % BLOCKSIZE; }; /** Padding for blocks */ export const pad = (data) => { const padded = new Uint8Array(data.length + getPadLength(data.length)); padded.set(data); return padded; };