diff --git a/circuits/aes-gcm/gfmulx.circom b/circuits/aes-gcm/gfmulx.circom index 16ceab7..8a6198f 100644 --- a/circuits/aes-gcm/gfmulx.circom +++ b/circuits/aes-gcm/gfmulx.circom @@ -19,7 +19,7 @@ template ghash_GFMULX() { for (var i = 1; i < 128; i++) { v[i] <== in[i-1]; } // if MSB set, assign irreducible poly bits, otherwise zero - // irreducible_poly has 1s at positions 1, 2, 7, 127 + // irreducible_poly has 1s at positions 0, 1, 6, 127 signal irreducible_poly[128]; for (var i = 0; i < 128; i++) { if (i==0 || i == 1 || i==6 || i==127) { @@ -60,10 +60,10 @@ template polyval_GFMULX() { v[i] <== left_shift.out[i]; } + // NOTE: LE logic explaining: // irreducible_poly has 1s at positions 1, 121, 126, 127 - // 0000 0001... <== encodes 1 - // ...1100 0010 <== encodes 121, 126, 127 - // ...0100 0010 <== encodes 121, 126 + // 0000 0001... <== bit at pos 7 encodes x^0 + // ...1100 0010 <== bits at pos 121, 122, 126 encode 127, 126, 121 respectively for (var i = 0; i < 128; i++) { if (i==7 || i == 120 || i==121 || i==126) { irreducible_poly[i] <== msb; @@ -111,4 +111,4 @@ template LeftShiftLE(shift) { out[j + 8*i] <== mid_2[7-j + 8*i]; } } -} \ No newline at end of file +} diff --git a/circuits/aes-gcm/ghash.circom b/circuits/aes-gcm/ghash.circom deleted file mode 100644 index e497d27..0000000 --- a/circuits/aes-gcm/ghash.circom +++ /dev/null @@ -1,77 +0,0 @@ -pragma circom 2.1.9; - -include "gfmul_int.circom"; -include "helper_functions.circom"; - -// GHASH computes the authentication tag for AES-GCM. -// Inputs: -// - `H` the hash key -// - `AAD` authenticated additional data -// - `msg` the message to authenticate -// -// Outputs: -// - `result` the authentication tag -// -// Computes: -// let M = pad(AAD) || pad(msg) || len_64(AAD) || let_64(msg) -// X_0 = 0^128 -// X_{i+1} = (X_i xor M_{i+1}) * H -// output: X_{n+1} where n is the number of blocks. -template GHASH(n_msg_bits) -{ - signal input msg[n_msg_bits]; - signal input H[128]; - signal input AAD[2][64]; - signal output result[2][64]; - - var n_msg_bytes = n_msg_bits/8; - var current_res[2][64] = AAD, in_t[2][64]; // result intermediate state - var i, j, k; - var n_msg_blocks = n_msg_bytes/16; - - component xor_1[n_msg_blocks][2][64]; - component gfmul_int_1[n_msg_blocks]; - - if(n_msg_blocks != 0) - { - // for each bit in the message - for(i=0; i { }); let bit_array = [1,0,0,0,0,0,0,0]; - let expected_output = [0,0,0,0,0,0,0,1]; + let expected_output = [0,0,0,0,0,0,0,1].map((x) => BigInt(x)); it("should have correct output", async () => { - const witness = await circuit.expectPass({ in: bit_array}, { out: expected_output }); - circuit.expectPass({in: bit_array}); + const witness = await circuit.compute({ in: bit_array }, ["out"]) - // I was initially not sure what the first bit was doing in the witness, but it's just the success flag - console.log("witness: from input: [1,0,0,0,0,0,0,0]", witness); + assert.deepEqual(witness.out, expected_output) }); }); \ No newline at end of file diff --git a/circuits/test/hashes/ghash.test.ts b/circuits/test/hashes/ghash.test.ts index 63d17f0..9cac752 100644 --- a/circuits/test/hashes/ghash.test.ts +++ b/circuits/test/hashes/ghash.test.ts @@ -1,19 +1,57 @@ import { WitnessTester } from "circomkit"; -import { circomkit } from "../common"; +import { bitArrayToHex, circomkit, hexToBitArray } from "../common"; +import { assert } from "chai"; -describe("ghash", () => { - let circuit: WitnessTester<["in"], ["out"]>; +// https://datatracker.ietf.org/doc/html/rfc8452#appendix-A +const H = hexToBitArray("25629347589242761d31f826ba4b757b"); +const X1 = "4f4f95668c83dfb6401762bb2d01a262"; +const X2 = "d1a24ddd2721d006bbe45f20d3c9f362"; +const M = hexToBitArray(X1.concat(X2)); +const EXPECT = "bd9b3997046731fb96251b91f9c99d7a"; + +describe("ghash-hash", () => { + let circuit: WitnessTester<["msg", "H"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`ghash`, { - file: "aes-gcm/ghash", + file: "aes-gcm/hashes", template: "GHASH", - params: [128], + params: [128 * 2], + }); + // console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("test ghash", async () => { + const input = { msg: M, H: H }; + const _res = await circuit.compute(input, ["out"]); + // TODO(TK 2024-08-15): bug, result returns 256 bits + // take the first 32 bytes + const result = bitArrayToHex( + (_res.out as number[]).map((bit) => Number(bit)) + ).slice(0, 32); + console.log("expect: ", EXPECT, "\nresult: ", result); + assert.equal(result, EXPECT); + }); +}); + +describe("reverse_byte_array", () => { + let circuit: WitnessTester<["in"], ["out"]>; + + before(async () => { + circuit = await circomkit.WitnessTester(`ghash`, { + file: "aes-gcm/helper_functions", + template: "ReverseByteArray", }); - console.log("#constraints:", await circuit.getConstraintCount()); }); - it("should have correct number of constraints", async () => { - await circuit.expectConstraintCount(74754, true); + it("test reverse_byte_array", async () => { + let bits = hexToBitArray("0102030405060708091011121314151f"); + let expect = "1f151413121110090807060504030201"; + const _res = await circuit.compute({ in: bits }, ["out"]); + const result = bitArrayToHex( + (_res.out as number[]).map((bit) => Number(bit)) + ); + // console.log("expect: ", expect, "\nresult: ", result); + assert.equal(expect, result); }); -}); \ No newline at end of file +}); diff --git a/circuits/test/hashes/polyval.test.ts b/circuits/test/hashes/polyval.test.ts index 20c3ba0..75ca7ca 100644 --- a/circuits/test/hashes/polyval.test.ts +++ b/circuits/test/hashes/polyval.test.ts @@ -1,14 +1,22 @@ import { WitnessTester } from "circomkit"; -import { circomkit } from "../common"; +import { bitArrayToHex, circomkit, hexToBitArray } from "../common"; +import { assert } from "chai"; + +// https://datatracker.ietf.org/doc/html/rfc8452#appendix-A +const H = hexToBitArray("25629347589242761d31f826ba4b757b"); +const X1 = "4f4f95668c83dfb6401762bb2d01a262"; +const X2 = "d1a24ddd2721d006bbe45f20d3c9f362"; +const M = hexToBitArray(X1.concat(X2)); +const EXPECT = "f7a3b47b846119fae5b7866cf5e5b77e"; describe("polyval", () => { - let circuit: WitnessTester<["in"], ["out"]>; + let circuit: WitnessTester<["msg", "H"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`polyval`, { - file: "aes-gcm/polyval", + file: "aes-gcm/hashes", template: "POLYVAL", - params: [128], + params: [128 * 2], }); console.log("#constraints:", await circuit.getConstraintCount()); }); @@ -16,4 +24,16 @@ describe("polyval", () => { it("should have correct number of constraints", async () => { await circuit.expectConstraintCount(74754, true); }); + + it("todo name polyval", async () => { + const input = { msg: M, H: H }; + const _res = await circuit.compute(input, ["out"]); + // TODO(TK 2024-08-15): bug, result returns 256 bits + // take the first 32 bytes + const result = bitArrayToHex( + (_res.out as number[]).map((bit) => Number(bit)) + ).slice(0, 32); + console.log("expect: ", EXPECT, "\nresult: ", result); + assert.equal(result, EXPECT); + }); }); \ No newline at end of file