UNPKG

@li0ard/kalyna

Version:

Kalyna (DSTU 7624:2014) cipher implementation in pure TypeScript

97 lines (96 loc) 3.66 kB
import { concatBytes, equalBytes } from "@li0ard/gost3413/dist/utils.js"; import { ctr } from "../index.js"; const ccm_mac = (cipherClass, iv, authData, plainData, q = 16, Nb = 4) => { const blockSize = cipherClass.blockSize; if (blockSize < Nb + 1) throw new Error('blockSize must be >= Nb + 1'); const tmp = blockSize - Nb - 1; const G1 = new Uint8Array(blockSize); const G2 = new Uint8Array(blockSize); const B = new Uint8Array(blockSize); G1.set(iv.slice(0, tmp), 0); G1[tmp] = plainData.length & 0xFF; if (plainData.length > 0) G1[blockSize - 1] = 1 << 7; else G1[blockSize - 1] = 0; switch (q) { case 8: G1[blockSize - 1] |= 2 << 4; break; case 16: G1[blockSize - 1] |= 3 << 4; break; case 32: G1[blockSize - 1] |= 4 << 4; break; case 48: G1[blockSize - 1] |= 5 << 4; break; case 64: G1[blockSize - 1] |= 6 << 4; break; default: throw new Error('Invalid q value'); } G1[blockSize - 1] |= (Nb - 1); G2[0] = authData.length & 0xFF; const tmp2 = authData.length % blockSize; const hLength = blockSize + (blockSize - tmp2) + authData.length; const h = new Uint8Array(hLength); h.set(G1, 0); h.set(G2.slice(0, blockSize - tmp2), blockSize); h.set(authData, blockSize + (blockSize - tmp2)); for (let i = 0; i < hLength; i += blockSize) { const chunk = h.slice(i, i + blockSize); for (let j = 0; j < blockSize; j++) B[j] ^= chunk[j]; const encrypted = cipherClass.encrypt(B); B.set(encrypted); } let paddedPlainData = plainData; if (plainData.length % blockSize !== 0) { const paddingLength = blockSize - (plainData.length % blockSize); paddedPlainData = new Uint8Array(plainData.length + paddingLength); paddedPlainData.set(plainData); paddedPlainData[plainData.length] = 0x80; } for (let i = 0; i < paddedPlainData.length; i += blockSize) { const chunk = paddedPlainData.slice(i, i + blockSize); for (let j = 0; j < blockSize; j++) B[j] ^= chunk[j]; const encrypted = cipherClass.encrypt(B); B.set(encrypted); } return B.slice(0, q); }; /** * Encrypts data using Counter with Cipher Block Chaining-Message Authentication Code (CCM) mode * @param cipherClass Initialized cipher class * @param plainData Data to be encrypted and authenticated * @param iv Initialization vector * @param authData Additional data to be authenticated * @param q MAC size * @param Nb Param `Nb` */ export const encryptCCM = (cipherClass, plainData, iv, authData = new Uint8Array(), q = 16, Nb = 4) => { const h = ccm_mac(cipherClass, iv, authData, plainData, q, Nb); return ctr(cipherClass, concatBytes(plainData, h), iv); }; /** * Decrypts data using Counter with Cipher Block Chaining-Message Authentication Code (CCM) mode * @param cipherClass Initialized cipher class * @param encryptedData Data to be decrypted and authenticated * @param iv Initialization vector * @param authData Additional data to be authenticated * @param q MAC size * @param Nb Param `Nb` */ export const decryptCCM = (cipherClass, encryptedData, iv, authData = new Uint8Array(), q = 16, Nb = 4) => { const raw = ctr(cipherClass, encryptedData, iv); const pt = raw.slice(0, -q); const hC = ccm_mac(cipherClass, iv, authData, pt, q, Nb); if (!equalBytes(raw.slice(-q), hC)) throw new Error("Invalid MAC"); return pt; };