@li0ard/gost3413
Version:
Cipher modes and padding's according to GOST R 34.13-2015 in pure TypeScript
84 lines (83 loc) • 3 kB
JavaScript
/** Key size */
export const KEYSIZE = 32;
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;
};
// Code from awesome projects `@noble/curves` and `@noble/hashes`
export function equalBytes(a, b) {
if (a.length !== b.length)
return false;
let diff = 0;
for (let i = 0; i < a.length; i++)
diff |= a[i] ^ b[i];
return diff === 0;
}
export function concatBytes(...arrays) {
let sum = 0;
for (let i = 0; i < arrays.length; i++) {
const a = arrays[i];
sum += a.length;
}
const res = new Uint8Array(sum);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const a = arrays[i];
res.set(a, pad);
pad += a.length;
}
return res;
}
const asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
function asciiToBase16(ch) {
if (ch >= asciis._0 && ch <= asciis._9)
return ch - asciis._0; // '2' => 50-48
if (ch >= asciis.A && ch <= asciis.F)
return ch - (asciis.A - 10); // 'B' => 66-(65-10)
if (ch >= asciis.a && ch <= asciis.f)
return ch - (asciis.a - 10); // 'b' => 98-(97-10)
return;
}
export function hexToBytes(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
const hl = hex.length;
const al = hl / 2;
if (hl % 2)
throw new Error('hex string expected, got unpadded hex of length ' + hl);
const array = new Uint8Array(al);
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
const n1 = asciiToBase16(hex.charCodeAt(hi));
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
if (n1 === undefined || n2 === undefined) {
const char = hex[hi] + hex[hi + 1];
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
}
array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163
}
return array;
}
const hexes = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
export const bytesToHex = (bytes) => {
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++)
hex += hexes[bytes[i]];
return hex;
};
export const numberToBytesBE = (n, len) => {
let num = n.toString(16).padStart(len * 2, '0');
while (num.length % 2 != 0)
num = "0" + num;
return hexToBytes(num);
};
export const numberToBytesLE = (n, len) => numberToBytesBE(n, len).reverse();
export const hexToNumber = (hex) => {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
return hex === '' ? 0n : BigInt('0x' + hex);
};
export const bytesToNumberBE = (bytes) => hexToNumber(bytesToHex(bytes));
export const bytesToNumberLE = (bytes) => hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));