From aa55b78168153c94328b7db6bec0eda41a1d7c86 Mon Sep 17 00:00:00 2001 From: Thor Kampefner Date: Wed, 14 Aug 2024 09:57:23 -0700 Subject: [PATCH 1/8] init --- circuits/aes-gcm/gfmul_int.circom | 1 + 1 file changed, 1 insertion(+) diff --git a/circuits/aes-gcm/gfmul_int.circom b/circuits/aes-gcm/gfmul_int.circom index 85a7e2f..bea885c 100644 --- a/circuits/aes-gcm/gfmul_int.circom +++ b/circuits/aes-gcm/gfmul_int.circom @@ -21,6 +21,7 @@ template GFMULInt() signal input b[2][64]; signal output res[2][64]; + // TODO(TK 2024-08-10): note about magic nuhmebr var tmp[5][2][64]; From 5e2f64e8d615abb8bbe15b4b2cc491f486786365 Mon Sep 17 00:00:00 2001 From: Thor Kampefner Date: Wed, 14 Aug 2024 10:04:33 -0700 Subject: [PATCH 2/8] polyval gfmulint disambiguate ghash gfmulint --- circuits/aes-gcm/ghash.circom | 2 +- ...fmul_int.circom => ghash_gfmul_int.circom} | 0 circuits/aes-gcm/polyval.circom | 2 +- circuits/aes-gcm/polyval_gfmul_int | 192 ++++++++++++++++++ .../polyval_gfmulint.test.ts} | 2 +- 5 files changed, 195 insertions(+), 3 deletions(-) rename circuits/aes-gcm/{gfmul_int.circom => ghash_gfmul_int.circom} (100%) create mode 100644 circuits/aes-gcm/polyval_gfmul_int rename circuits/test/{gfmulint/gfmulint.test.ts => polyval_gfmulint/polyval_gfmulint.test.ts} (97%) diff --git a/circuits/aes-gcm/ghash.circom b/circuits/aes-gcm/ghash.circom index e497d27..39546d2 100644 --- a/circuits/aes-gcm/ghash.circom +++ b/circuits/aes-gcm/ghash.circom @@ -1,6 +1,6 @@ pragma circom 2.1.9; -include "gfmul_int.circom"; +include "ghash_gfmul_int.circom"; include "helper_functions.circom"; // GHASH computes the authentication tag for AES-GCM. diff --git a/circuits/aes-gcm/gfmul_int.circom b/circuits/aes-gcm/ghash_gfmul_int.circom similarity index 100% rename from circuits/aes-gcm/gfmul_int.circom rename to circuits/aes-gcm/ghash_gfmul_int.circom diff --git a/circuits/aes-gcm/polyval.circom b/circuits/aes-gcm/polyval.circom index cb63743..6277c8f 100644 --- a/circuits/aes-gcm/polyval.circom +++ b/circuits/aes-gcm/polyval.circom @@ -1,6 +1,6 @@ pragma circom 2.1.9; -include "gfmul_int.circom"; +include "polyval_gfmul_int.circom"; include "helper_functions.circom"; template POLYVAL(n_bits) diff --git a/circuits/aes-gcm/polyval_gfmul_int b/circuits/aes-gcm/polyval_gfmul_int new file mode 100644 index 0000000..bea885c --- /dev/null +++ b/circuits/aes-gcm/polyval_gfmul_int @@ -0,0 +1,192 @@ +pragma circom 2.1.9; + +include "vclmul_emulator.circom"; +include "helper_functions.circom"; + +// GFMULInt multiplies two 128-bit numbers in GF(2^128) and returns the result. +// +// Inputs: +// - `a` the first 128-bit number, encoded as a polynomial over GF(2^128) +// - `b` the second 128-bit number, encoded as a polynomial over GF(2^128) +// +// Outputs: +// - `res` the result of the multiplication +// +// Computes: +// res = a * b mod p +// where p = x^128 + x^7 + x^2 + x + 1 +template GFMULInt() +{ + signal input a[2][64]; + signal input b[2][64]; + signal output res[2][64]; + + + // TODO(TK 2024-08-10): note about magic nuhmebr + var tmp[5][2][64]; + + // XMMMask is x^128 + x^7 + x^2 + x + 1 + // GHASH is big endian, polyval is little endian + // bitwise: 11100001 .... 00000001 + // + // 0x87 = 0b1000_0111 => encode x^7 + x^2 + x + 1 + var XMMMASK[2] = [0x87, 0x0]; + + var i, j, k; + + log("GFMULInt"); + log(a[0][0]); + // log(b); + // log("res", res); + + component num2bits_1[2]; + var XMMMASK_bits[2][64]; + for(i=0; i<2; i++) + { + num2bits_1[i] = Num2Bits(64); + num2bits_1[i].in <== XMMMASK[i]; + for(j=0; j<8; j++) + { + for(k=0; k<8; k++) XMMMASK_bits[i][j*8+k] = num2bits_1[i].out[j*8+7-k]; + } + } + + component vclmul_emulator_1[4]; + + for(i=0; i<4; i++) + { + vclmul_emulator_1[i] = VCLMULEmulator(i); + for(j=0; j<2; j++) + { + for(k=0; k<64; k++) + { + vclmul_emulator_1[i].src1[j][k] <== a[j][k]; + vclmul_emulator_1[i].src2[j][k] <== b[j][k]; + } + } + + tmp[i+1] = vclmul_emulator_1[i].destination; + } + + component xor_1[2][64]; + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + xor_1[i][j] = XOR(); + xor_1[i][j].a <== tmp[2][i][j]; + xor_1[i][j].b <== tmp[3][i][j]; + + tmp[2][i][j] = xor_1[i][j].out; + } + } + + for(i=0; i<64; i++) tmp[3][0][i] = 0; + + for(i=0; i<64; i++) tmp[3][1][i] = tmp[2][0][i]; + + for(i=0; i<64; i++) tmp[2][0][i] = tmp[2][1][i]; + + for(i=0; i<64; i++) tmp[2][1][i] = 0; + + component xor_2[2][64]; + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + xor_2[i][j] = XOR(); + xor_2[i][j].a <== tmp[1][i][j]; + xor_2[i][j].b <== tmp[3][i][j]; + + tmp[1][i][j] = xor_2[i][j].out; + } + } + + component xor_3[2][64]; + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + xor_3[i][j] = XOR(); + xor_3[i][j].a <== tmp[4][i][j]; + xor_3[i][j].b <== tmp[2][i][j]; + + tmp[4][i][j] = xor_3[i][j].out; + } + } + + component vclmul_emulator_2 = VCLMULEmulator(1); + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + vclmul_emulator_2.src1[i][j] <== XMMMASK_bits[i][j]; + vclmul_emulator_2.src2[i][j] <== tmp[1][i][j]; + } + } + + tmp[2] = vclmul_emulator_2.destination; + + for(i=0; i<64; i++) + { + tmp[3][0][i] = tmp[1][1][i]; + tmp[3][1][i] = tmp[1][0][i]; + } + + component xor_4[2][64]; + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + xor_4[i][j] = XOR(); + xor_4[i][j].a <== tmp[2][i][j]; + xor_4[i][j].b <== tmp[3][i][j]; + + tmp[1][i][j] = xor_4[i][j].out; + } + } + + component vclmul_emulator_3 = VCLMULEmulator(1); + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + vclmul_emulator_3.src1[i][j] <== XMMMASK_bits[i][j]; + vclmul_emulator_3.src2[i][j] <== tmp[1][i][j]; + } + } + + tmp[2] = vclmul_emulator_3.destination; + + for(i=0; i<64; i++) + { + tmp[3][0][i] = tmp[1][1][i]; + tmp[3][1][i] = tmp[1][0][i]; + } + + component xor_5[2][64]; + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + xor_5[i][j] = XOR(); + xor_5[i][j].a <== tmp[2][i][j]; + xor_5[i][j].b <== tmp[3][i][j]; + + tmp[1][i][j] = xor_5[i][j].out; + } + } + + component xor_6[2][64]; + for(i=0; i<2; i++) + { + for(j=0; j<64; j++) + { + xor_6[i][j] = XOR(); + xor_6[i][j].a <== tmp[1][i][j]; + xor_6[i][j].b <== tmp[4][i][j]; + + res[i][j] <== xor_6[i][j].out; + } + } +} \ No newline at end of file diff --git a/circuits/test/gfmulint/gfmulint.test.ts b/circuits/test/polyval_gfmulint/polyval_gfmulint.test.ts similarity index 97% rename from circuits/test/gfmulint/gfmulint.test.ts rename to circuits/test/polyval_gfmulint/polyval_gfmulint.test.ts index 256986c..03c8e42 100644 --- a/circuits/test/gfmulint/gfmulint.test.ts +++ b/circuits/test/polyval_gfmulint/polyval_gfmulint.test.ts @@ -10,7 +10,7 @@ describe("gfmulint", () => { before(async () => { circuit = await circomkit.WitnessTester("gfmulint", { - file: "aes-gcm/gfmul_int", + file: "aes-gcm/polyval_gfmul_int", template: "GFMULInt", }); console.log("#constraints:", await circuit.getConstraintCount()); From a965abd3463cf0b6fa0f78fa014d6723f204bf00 Mon Sep 17 00:00:00 2001 From: Thor Kampefner Date: Wed, 14 Aug 2024 10:05:52 -0700 Subject: [PATCH 3/8] separate ghash from polyval gfmul test --- .../test/gfmulint/ghash_gfmul_int.test.ts | 51 +++++++++++++++++++ .../polyval_gfmulint.test.ts | 0 2 files changed, 51 insertions(+) create mode 100644 circuits/test/gfmulint/ghash_gfmul_int.test.ts rename circuits/test/{polyval_gfmulint => gfmulint}/polyval_gfmulint.test.ts (100%) diff --git a/circuits/test/gfmulint/ghash_gfmul_int.test.ts b/circuits/test/gfmulint/ghash_gfmul_int.test.ts new file mode 100644 index 0000000..fc46cc7 --- /dev/null +++ b/circuits/test/gfmulint/ghash_gfmul_int.test.ts @@ -0,0 +1,51 @@ +import { assert } from "chai"; +import { WitnessTester } from "circomkit"; +import { circomkit } from "../common"; + +// input and output type of GFMULInt +type Arr128 = number[][]; + +describe("gfmulint", () => { + let circuit: WitnessTester<["a", "b"], ["res"]>; + + before(async () => { + circuit = await circomkit.WitnessTester("gfmulint", { + file: "aes-gcm/ghash_gfmul_int", + template: "GFMULInt", + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("should have correct number of constraints", async () => { + await circuit.expectConstraintCount(74626, true); + }); + + it("should output correct gfmul", async () => { + const a = 128; + const b = 128; + const expected = a * b; + const input = { a: pad_num_to_arr128(a), b: pad_num_to_arr128(b) }; + + let _res = await circuit.compute(input, ["res"]); + console.log(`res: ${_res.res}`); + let result = parse_arr128_to_number(_res.res as Arr128); + console.log(`${a} x ${b} = ${result}`); + assert.equal(result, expected); + }); +}); + +function pad_num_to_arr128(value: number): Arr128 { + let tmp = value + .toString(2) + .padStart(128, "0") + .split("") + .map((bit) => parseInt(bit, 10)); + return [tmp.slice(0, 64), tmp.slice(64, 128)]; +} + +function parse_arr128_to_number(res: Arr128): number { + let first_64: number[] = res[0]; + let last_64: number[] = res[1]; + let all_bits: number[] = first_64.concat(last_64); + return parseInt(all_bits.join(""), 2); +} diff --git a/circuits/test/polyval_gfmulint/polyval_gfmulint.test.ts b/circuits/test/gfmulint/polyval_gfmulint.test.ts similarity index 100% rename from circuits/test/polyval_gfmulint/polyval_gfmulint.test.ts rename to circuits/test/gfmulint/polyval_gfmulint.test.ts From e98673c7f1caf2d22824b560d52a0bf0c341b001 Mon Sep 17 00:00:00 2001 From: Thor Kampefner Date: Wed, 14 Aug 2024 10:16:43 -0700 Subject: [PATCH 4/8] file-renamings --- circuits/aes-gcm/{polyval_gfmul_int => polyval_gfmul_int.circom} | 0 .../{polyval_gfmulint.test.ts => polyval_gfmul_int.test.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename circuits/aes-gcm/{polyval_gfmul_int => polyval_gfmul_int.circom} (100%) rename circuits/test/gfmulint/{polyval_gfmulint.test.ts => polyval_gfmul_int.test.ts} (100%) diff --git a/circuits/aes-gcm/polyval_gfmul_int b/circuits/aes-gcm/polyval_gfmul_int.circom similarity index 100% rename from circuits/aes-gcm/polyval_gfmul_int rename to circuits/aes-gcm/polyval_gfmul_int.circom diff --git a/circuits/test/gfmulint/polyval_gfmulint.test.ts b/circuits/test/gfmulint/polyval_gfmul_int.test.ts similarity index 100% rename from circuits/test/gfmulint/polyval_gfmulint.test.ts rename to circuits/test/gfmulint/polyval_gfmul_int.test.ts From f05becc1acb9763b3ca35b62c4de00dd99785a96 Mon Sep 17 00:00:00 2001 From: Thor Kampefner Date: Wed, 14 Aug 2024 10:19:03 -0700 Subject: [PATCH 5/8] test-file-renamings-hash --- circuits/test/{ghash => hashes}/ghash.test.ts | 0 circuits/test/{ghash => hashes}/polyval.test.ts | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename circuits/test/{ghash => hashes}/ghash.test.ts (100%) rename circuits/test/{ghash => hashes}/polyval.test.ts (100%) diff --git a/circuits/test/ghash/ghash.test.ts b/circuits/test/hashes/ghash.test.ts similarity index 100% rename from circuits/test/ghash/ghash.test.ts rename to circuits/test/hashes/ghash.test.ts diff --git a/circuits/test/ghash/polyval.test.ts b/circuits/test/hashes/polyval.test.ts similarity index 100% rename from circuits/test/ghash/polyval.test.ts rename to circuits/test/hashes/polyval.test.ts From c4c11dd8a44bf9cc1d54f53546348f95f65f9225 Mon Sep 17 00:00:00 2001 From: KaiGeffen Date: Wed, 14 Aug 2024 16:58:37 -0400 Subject: [PATCH 6/8] Fixed polyval filename --- circuits/aes-gcm/{polyval_gfmul_int => polyval_gfmul_int.circom} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename circuits/aes-gcm/{polyval_gfmul_int => polyval_gfmul_int.circom} (100%) diff --git a/circuits/aes-gcm/polyval_gfmul_int b/circuits/aes-gcm/polyval_gfmul_int.circom similarity index 100% rename from circuits/aes-gcm/polyval_gfmul_int rename to circuits/aes-gcm/polyval_gfmul_int.circom From 9e5ad702c29d76e28c873808e8b6895a21307238 Mon Sep 17 00:00:00 2001 From: KaiGeffen Date: Wed, 14 Aug 2024 16:58:51 -0400 Subject: [PATCH 7/8] Added chai dep --- package-lock.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index b9dc24e..feaa306 100644 --- a/package-lock.json +++ b/package-lock.json @@ -107,7 +107,8 @@ "version": "4.3.17", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.17.tgz", "integrity": "sha512-zmZ21EWzR71B4Sscphjief5djsLre50M6lI622OSySTmn9DB3j+C3kWroHfBQWXbOBwbgg/M8CG/hUxDLIloow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/mocha": { "version": "10.0.7", From c3838302cf5cdca3004817252a1478401bbdf6b5 Mon Sep 17 00:00:00 2001 From: Thor Kampefner Date: Thu, 15 Aug 2024 11:46:40 -0700 Subject: [PATCH 8/8] impl mulx --- circuits/aes-gcm/ghash_gfmul_int.circom | 1 - circuits/aes-gcm/helper_functions.circom | 72 +++++++++++++++++++ .../test/gfmulint/ghash_gfmul_int.test.ts | 58 ++++++++------- 3 files changed, 103 insertions(+), 28 deletions(-) diff --git a/circuits/aes-gcm/ghash_gfmul_int.circom b/circuits/aes-gcm/ghash_gfmul_int.circom index bea885c..85a7e2f 100644 --- a/circuits/aes-gcm/ghash_gfmul_int.circom +++ b/circuits/aes-gcm/ghash_gfmul_int.circom @@ -21,7 +21,6 @@ template GFMULInt() signal input b[2][64]; signal output res[2][64]; - // TODO(TK 2024-08-10): note about magic nuhmebr var tmp[5][2][64]; diff --git a/circuits/aes-gcm/helper_functions.circom b/circuits/aes-gcm/helper_functions.circom index 7424b5c..c6efb16 100644 --- a/circuits/aes-gcm/helper_functions.circom +++ b/circuits/aes-gcm/helper_functions.circom @@ -257,3 +257,75 @@ template ReverseBitsArray(n) { out[i] <== in[n-i-1]; } } + +// todo: check bit settings +// compute x * n over polyval polynomial +// if the msb of in is 1, +// compute in << 1 and xor with 127, 126, 121, 1 +template mulX_ghash() { + signal input in[128]; + signal output out[128]; + + // v = in left-shifted by 1 + signal v[128]; + // v_xor = 0 if in[0] is 0, or the irreducible poly if in[0] is 1 + signal v_xor[128]; + + // initialize v and v_xor. + v[127] <== 0; + v_xor[127] <== in[0]; + + for (var i=126; i>=0; i--) { + v[i] <== in[i+1]; + + // XOR with polynomial if MSB is 1 + // v_xor has 1s at positions 127, 126, 121, 1 + if (i==0 || i == 1 || i == 6) { + v_xor[i] <== in[0]; + } else { + v_xor[i] <== 0; + } + } + + // compute out + component xor = BitwiseXor(128); + xor.a <== v; + xor.b <== v_xor; + out <== xor.out; +} + +// todo: check bit settings +// compute x * n over polyval polynomial +// if the msb of in is 1, +// compute in << 1 and xor with 127, 126, 121, 1 +template mulX_polyval() { + signal input in[128]; + signal output out[128]; + + // v = in left-shifted by 1 + signal v[128]; + // v_xor = 0 if in[0] is 0, or the irreducible poly if in[0] is 1 + signal v_xor[128]; + + // initialize v and v_xor. + v[127] <== 0; + v_xor[127] <== in[0]; + + for (var i=126; i>=0; i--) { + v[i] <== in[i+1]; + + // polyval: v_xor at positions 127, 7, 2, 1 + if (i == 0 || i == 1 || i == 7) { + // ghash: v_xor at positions 127, 126, 121, 1 + // if (i==121 || i == 126 || i == 127) { + v_xor[i] <== in[0]; + } else { + v_xor[i] <== 0; + } + } + + component xor = BitwiseXor(128); + xor.a <== v; + xor.b <== v_xor; + out <== xor.out; +} \ No newline at end of file diff --git a/circuits/test/gfmulint/ghash_gfmul_int.test.ts b/circuits/test/gfmulint/ghash_gfmul_int.test.ts index fc46cc7..d6b8c90 100644 --- a/circuits/test/gfmulint/ghash_gfmul_int.test.ts +++ b/circuits/test/gfmulint/ghash_gfmul_int.test.ts @@ -4,48 +4,52 @@ import { circomkit } from "../common"; // input and output type of GFMULInt type Arr128 = number[][]; +type _Arr128 = number[]; -describe("gfmulint", () => { - let circuit: WitnessTester<["a", "b"], ["res"]>; +describe("mulX_polyval", () => { + let circuit: WitnessTester<["in"], ["out"]>; before(async () => { - circuit = await circomkit.WitnessTester("gfmulint", { - file: "aes-gcm/ghash_gfmul_int", - template: "GFMULInt", + circuit = await circomkit.WitnessTester("mulX_polyval", { + file: "aes-gcm/helper_functions", + template: "mulX_polyval", }); - console.log("#constraints:", await circuit.getConstraintCount()); }); - it("should have correct number of constraints", async () => { - await circuit.expectConstraintCount(74626, true); + it("it should compute leftshift of one", async () => { + let _res = await circuit.compute({ in: _pad_num_to_arr128(1) }, ["out"]); + // console.log(`${_res.out}`); + let res = _parse_arr128_to_number(_res.out as _Arr128); + assert.equal(res, 2); }); - it("should output correct gfmul", async () => { - const a = 128; - const b = 128; - const expected = a * b; - const input = { a: pad_num_to_arr128(a), b: pad_num_to_arr128(b) }; - - let _res = await circuit.compute(input, ["res"]); - console.log(`res: ${_res.res}`); - let result = parse_arr128_to_number(_res.res as Arr128); - console.log(`${a} x ${b} = ${result}`); - assert.equal(result, expected); + it("it should compute leftshift of two", async () => { + let _res = await circuit.compute({ in: _pad_num_to_arr128(2) }, ["out"]); + // console.log(`${_res.out}`); + let res = _parse_arr128_to_number(_res.out as _Arr128); + assert.equal(res, 4); + }); + + it("it should compute leftshift of 127", async () => { + let input = [1, ...Array(127).fill(0)]; + let _res = await circuit.compute({ in: input }, ["out"]); + console.log(`${_res.out}`); + + // recall + // x^127 + x^126 + x^121 + 1 + let expected = [1, 1, 0, 0, 0, 0, 0, 1, ...Array(119).fill(0), 1]; + assert.equal(_res.out, expected); }); }); -function pad_num_to_arr128(value: number): Arr128 { - let tmp = value +function _pad_num_to_arr128(value: number): _Arr128 { + return value .toString(2) .padStart(128, "0") .split("") .map((bit) => parseInt(bit, 10)); - return [tmp.slice(0, 64), tmp.slice(64, 128)]; } -function parse_arr128_to_number(res: Arr128): number { - let first_64: number[] = res[0]; - let last_64: number[] = res[1]; - let all_bits: number[] = first_64.concat(last_64); - return parseInt(all_bits.join(""), 2); +function _parse_arr128_to_number(res: _Arr128): number { + return parseInt(res.join(""), 2); }