import { MEMBER_MASK, NB_ROUNDS, TWEAK_LENGTH, PT, LFSR_8_TK2, LFSR_8_TK3, S8, C } from './constants' /** * Create a tweakey based on the specified domain separation, nonce, key and current counter state. * @param counter The counter. * @param domainSeparation The domain separation. * @param nonce The nonce. * @param key The encryption key. * @returns The tweakey. */ export function tweakeyEncode (counter: number[], domainSeparation: number, nonce: number[], key: number[]): number[] { return counter.concat([domainSeparation ^ MEMBER_MASK], Array(8).fill(0), nonce, key) } /** * Perform a round of SKINNY-188/384+ encryption. * @param plaintext The plaintext to encrypt. * @param tweakey The tweakey to use for encryption. * @returns The ciphertext. */ export function skinnyEncrypt (plaintext: number[], tweakey: number[]): number[] { const tk = Array(NB_ROUNDS + 1).fill(Array(TWEAK_LENGTH).fill(0)) tk[0] = [...Array(TWEAK_LENGTH).keys()].map(i => tweakey[i]) for (let i = 0; i < NB_ROUNDS - 1; i++) { tk[i + 1] = [...tk[i]] for (let j = 0; j < TWEAK_LENGTH; j++) { tk[i + 1][j] = tk[i][j - j % 16 + PT[j % 16]] } for (let j = 0; j < 8; j++) { tk[i + 1][j + 16] = LFSR_8_TK2[tk[i + 1][j + 16]] tk[i + 1][j + 32] = LFSR_8_TK3[tk[i + 1][j + 32]] } } let s = [...Array(16).keys()].map(i => plaintext[i]) for (let i = 0; i < NB_ROUNDS; i++) { for (let j = 0; j < 16; j++) { s[j] = S8[s[j]] } s[0] ^= (C[i] & 0xf) s[4] ^= (C[i] >> 4) & 0xf s[8] ^= 0x2 for (let j = 0; j < 8; j++) { s[j] ^= tk[i][j] ^ tk[i][j + 16] ^ tk[i][j + 32] } s = [s[0], s[1], s[2], s[3], s[7], s[4], s[5], s[6], s[10], s[11], s[8], s[9], s[13], s[14], s[15], s[12]] for (let j = 0; j < 4; j++) { const tmp = [...s] s[j] = tmp[j] ^ tmp[8 + j] ^ tmp[12 + j] s[4 + j] = tmp[j] s[8 + j] = tmp[4 + j] ^ tmp[8 + j] s[12 + j] = tmp[0 + j] ^ tmp[8 + j] } } return [...Array(16).keys()].map(i => s[i]) }