From 59f703d6bd797a31291d9a33ee00118572fe1444 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Mon, 22 Jul 2024 09:57:27 +0000 Subject: [PATCH] feat: TS keccakf1600 --- .../src/crypto/keccak/index.test.ts | 99 +++++++ .../foundation/src/crypto/keccak/index.ts | 245 ++++++++++++++++++ 2 files changed, 344 insertions(+) create mode 100644 yarn-project/foundation/src/crypto/keccak/index.test.ts diff --git a/yarn-project/foundation/src/crypto/keccak/index.test.ts b/yarn-project/foundation/src/crypto/keccak/index.test.ts new file mode 100644 index 00000000000..31722b2fa86 --- /dev/null +++ b/yarn-project/foundation/src/crypto/keccak/index.test.ts @@ -0,0 +1,99 @@ +import { keccakf1600 } from './index.js'; + +describe('keccakf1600', () => { + it('zero test vector should match', () => { + const input = [...Array(25)].map(() => 0n); + + const out = keccakf1600(input); + const asStrings = out?.map(x => x.toString(16).padStart(16, '0')); + + expect(asStrings).toEqual([ + 'f1258f7940e1dde7', + '84d5ccf933c0478a', + 'd598261ea65aa9ee', + 'bd1547306f80494d', + '8b284e056253d057', + 'ff97a42d7f8e6fd4', + '90fee5a0a44647c4', + '8c5bda0cd6192e76', + 'ad30a6f71b19059c', + '30935ab7d08ffc64', + 'eb5aa93f2317d635', + 'a9a6e6260d712103', + '81a57c16dbcf555f', + '43b831cd0347c826', + '01f22f1a11a5569f', + '05e5635a21d9ae61', + '64befef28cc970f2', + '613670957bc46611', + 'b87c5a554fd00ecb', + '8c3ee88a1ccf32c8', + '940c7922ae3a2614', + '1841f924a2c509e4', + '16f53526e70465c2', + '75f644e97f30a13b', + 'eaf1ff7b5ceca249', + ]); + }); + + it('test vector should match', () => { + const input = [ + 'f1258f7940e1dde7', + '84d5ccf933c0478a', + 'd598261ea65aa9ee', + 'bd1547306f80494d', + '8b284e056253d057', + 'ff97a42d7f8e6fd4', + '90fee5a0a44647c4', + '8c5bda0cd6192e76', + 'ad30a6f71b19059c', + '30935ab7d08ffc64', + 'eb5aa93f2317d635', + 'a9a6e6260d712103', + '81a57c16dbcf555f', + '43b831cd0347c826', + '01f22f1a11a5569f', + '05e5635a21d9ae61', + '64befef28cc970f2', + '613670957bc46611', + 'b87c5a554fd00ecb', + '8c3ee88a1ccf32c8', + '940c7922ae3a2614', + '1841f924a2c509e4', + '16f53526e70465c2', + '75f644e97f30a13b', + 'eaf1ff7b5ceca249', + ].map(x => BigInt(`0x${x}`)); + + const out = keccakf1600(input); + const asStrings = out?.map(x => x.toString(16).padStart(16, '0')); + + expect(asStrings).toEqual([ + '2d5c954df96ecb3c', + '6a332cd07057b56d', + '093d8d1270d76b6c', + '8a20d9b25569d094', + '4f9c4f99e5e7f156', + 'f957b9a2da65fb38', + '85773dae1275af0d', + 'faf4f247c3d810f7', + '1f1b9ee6f79a8759', + 'e4fecc0fee98b425', + '68ce61b6b9ce68a1', + 'deea66c4ba8f974f', + '33c43d836eafb1f5', + 'e00654042719dbd9', + '7cf8a9f009831265', + 'fd5449a6bf174743', + '97ddad33d8994b40', + '48ead5fc5d0be774', + 'e3b8c8ee55b7b03c', + '91a0226e649e42e9', + '900e3129e7badd7b', + '202a9ec5faa3cce8', + '5b3402464e1c3db6', + '609f4e62a44c1059', + '20d06cd26a8fbf5c', + ]); + }); +}); diff --git a/yarn-project/foundation/src/crypto/keccak/index.ts b/yarn-project/foundation/src/crypto/keccak/index.ts index f4093e113eb..940e9d26ff2 100644 --- a/yarn-project/foundation/src/crypto/keccak/index.ts +++ b/yarn-project/foundation/src/crypto/keccak/index.ts @@ -31,3 +31,248 @@ export function keccak224(input: Buffer) { const hash = new Keccak(224); return hash.update(input).digest(); } + +/** + * Computes the Keccak-f1600 permutation of the input. + * @param state 25 64-bit words. + * @returns The permuted state. + */ +export function keccakf1600(state: bigint[]): bigint[] { + // Ideally we'd assert the size of the state and its constituent elements here. + // But since this gets included in the browser bundle, we can't use Node's assert module. + /* The implementation based on the "simple" implementation by Ronny Van Keer. */ + /* Adapted from Barretenberg's CPP implementation. */ + let Eba, Ebe, Ebi, Ebo, Ebu; + let Ega, Ege, Egi, Ego, Egu; + let Eka, Eke, Eki, Eko, Eku; + let Ema, Eme, Emi, Emo, Emu; + let Esa, Ese, Esi, Eso, Esu; + + let Ba, Be, Bi, Bo, Bu; + + let Da, De, Di, Do, Du; + + let Aba = state[0]; + let Abe = state[1]; + let Abi = state[2]; + let Abo = state[3]; + let Abu = state[4]; + let Aga = state[5]; + let Age = state[6]; + let Agi = state[7]; + let Ago = state[8]; + let Agu = state[9]; + let Aka = state[10]; + let Ake = state[11]; + let Aki = state[12]; + let Ako = state[13]; + let Aku = state[14]; + let Ama = state[15]; + let Ame = state[16]; + let Ami = state[17]; + let Amo = state[18]; + let Amu = state[19]; + let Asa = state[20]; + let Ase = state[21]; + let Asi = state[22]; + let Aso = state[23]; + let Asu = state[24]; + + for (let round = 0; round < 24; round += 2) { + /* Round (round + 0): Axx -> Exx */ + Ba = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + Be = Abe ^ Age ^ Ake ^ Ame ^ Ase; + Bi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + Bo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + Bu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + Da = Bu ^ rol(Be, 1n); + De = Ba ^ rol(Bi, 1n); + Di = Be ^ rol(Bo, 1n); + Do = Bi ^ rol(Bu, 1n); + Du = Bo ^ rol(Ba, 1n); + + Ba = Aba ^ Da; + Be = rol(Age ^ De, 44n); + Bi = rol(Aki ^ Di, 43n); + Bo = rol(Amo ^ Do, 21n); + Bu = rol(Asu ^ Du, 14n); + Eba = Ba ^ (~Be & Bi) ^ roundConstants[round]; + Ebe = Be ^ (~Bi & Bo); + Ebi = Bi ^ (~Bo & Bu); + Ebo = Bo ^ (~Bu & Ba); + Ebu = Bu ^ (~Ba & Be); + + Ba = rol(Abo ^ Do, 28n); + Be = rol(Agu ^ Du, 20n); + Bi = rol(Aka ^ Da, 3n); + Bo = rol(Ame ^ De, 45n); + Bu = rol(Asi ^ Di, 61n); + Ega = Ba ^ (~Be & Bi); + Ege = Be ^ (~Bi & Bo); + Egi = Bi ^ (~Bo & Bu); + Ego = Bo ^ (~Bu & Ba); + Egu = Bu ^ (~Ba & Be); + + Ba = rol(Abe ^ De, 1n); + Be = rol(Agi ^ Di, 6n); + Bi = rol(Ako ^ Do, 25n); + Bo = rol(Amu ^ Du, 8n); + Bu = rol(Asa ^ Da, 18n); + Eka = Ba ^ (~Be & Bi); + Eke = Be ^ (~Bi & Bo); + Eki = Bi ^ (~Bo & Bu); + Eko = Bo ^ (~Bu & Ba); + Eku = Bu ^ (~Ba & Be); + + Ba = rol(Abu ^ Du, 27n); + Be = rol(Aga ^ Da, 36n); + Bi = rol(Ake ^ De, 10n); + Bo = rol(Ami ^ Di, 15n); + Bu = rol(Aso ^ Do, 56n); + Ema = Ba ^ (~Be & Bi); + Eme = Be ^ (~Bi & Bo); + Emi = Bi ^ (~Bo & Bu); + Emo = Bo ^ (~Bu & Ba); + Emu = Bu ^ (~Ba & Be); + + Ba = rol(Abi ^ Di, 62n); + Be = rol(Ago ^ Do, 55n); + Bi = rol(Aku ^ Du, 39n); + Bo = rol(Ama ^ Da, 41n); + Bu = rol(Ase ^ De, 2n); + Esa = Ba ^ (~Be & Bi); + Ese = Be ^ (~Bi & Bo); + Esi = Bi ^ (~Bo & Bu); + Eso = Bo ^ (~Bu & Ba); + Esu = Bu ^ (~Ba & Be); + + /* Round (round + 1): Exx -> Axx */ + + Ba = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + Be = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + Bi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + Bo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + Bu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + Da = Bu ^ rol(Be, 1n); + De = Ba ^ rol(Bi, 1n); + Di = Be ^ rol(Bo, 1n); + Do = Bi ^ rol(Bu, 1n); + Du = Bo ^ rol(Ba, 1n); + + Ba = Eba ^ Da; + Be = rol(Ege ^ De, 44n); + Bi = rol(Eki ^ Di, 43n); + Bo = rol(Emo ^ Do, 21n); + Bu = rol(Esu ^ Du, 14n); + Aba = Ba ^ (~Be & Bi) ^ roundConstants[round + 1]; + Abe = Be ^ (~Bi & Bo); + Abi = Bi ^ (~Bo & Bu); + Abo = Bo ^ (~Bu & Ba); + Abu = Bu ^ (~Ba & Be); + + Ba = rol(Ebo ^ Do, 28n); + Be = rol(Egu ^ Du, 20n); + Bi = rol(Eka ^ Da, 3n); + Bo = rol(Eme ^ De, 45n); + Bu = rol(Esi ^ Di, 61n); + Aga = Ba ^ (~Be & Bi); + Age = Be ^ (~Bi & Bo); + Agi = Bi ^ (~Bo & Bu); + Ago = Bo ^ (~Bu & Ba); + Agu = Bu ^ (~Ba & Be); + + Ba = rol(Ebe ^ De, 1n); + Be = rol(Egi ^ Di, 6n); + Bi = rol(Eko ^ Do, 25n); + Bo = rol(Emu ^ Du, 8n); + Bu = rol(Esa ^ Da, 18n); + Aka = Ba ^ (~Be & Bi); + Ake = Be ^ (~Bi & Bo); + Aki = Bi ^ (~Bo & Bu); + Ako = Bo ^ (~Bu & Ba); + Aku = Bu ^ (~Ba & Be); + + Ba = rol(Ebu ^ Du, 27n); + Be = rol(Ega ^ Da, 36n); + Bi = rol(Eke ^ De, 10n); + Bo = rol(Emi ^ Di, 15n); + Bu = rol(Eso ^ Do, 56n); + Ama = Ba ^ (~Be & Bi); + Ame = Be ^ (~Bi & Bo); + Ami = Bi ^ (~Bo & Bu); + Amo = Bo ^ (~Bu & Ba); + Amu = Bu ^ (~Ba & Be); + + Ba = rol(Ebi ^ Di, 62n); + Be = rol(Ego ^ Do, 55n); + Bi = rol(Eku ^ Du, 39n); + Bo = rol(Ema ^ Da, 41n); + Bu = rol(Ese ^ De, 2n); + Asa = Ba ^ (~Be & Bi); + Ase = Be ^ (~Bi & Bo); + Asi = Bi ^ (~Bo & Bu); + Aso = Bo ^ (~Bu & Ba); + Asu = Bu ^ (~Ba & Be); + } + + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; + + return state; +} + +function rol(x: bigint, s: bigint) { + return BigInt.asUintN(64, x << s) | (x >> (64n - s)); +} + +const roundConstants: bigint[] = [ + 0x0000000000000001n, + 0x0000000000008082n, + 0x800000000000808an, + 0x8000000080008000n, + 0x000000000000808bn, + 0x0000000080000001n, + 0x8000000080008081n, + 0x8000000000008009n, + 0x000000000000008an, + 0x0000000000000088n, + 0x0000000080008009n, + 0x000000008000000an, + 0x000000008000808bn, + 0x800000000000008bn, + 0x8000000000008089n, + 0x8000000000008003n, + 0x8000000000008002n, + 0x8000000000000080n, + 0x000000000000800an, + 0x800000008000000an, + 0x8000000080008081n, + 0x8000000000008080n, + 0x0000000080000001n, + 0x8000000080008008n, +];