Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gfmul #44

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion circuits/aes-gcm/ghash.circom
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
72 changes: 72 additions & 0 deletions circuits/aes-gcm/helper_functions.circom
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion circuits/aes-gcm/polyval.circom
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
192 changes: 192 additions & 0 deletions circuits/aes-gcm/polyval_gfmul_int.circom
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
55 changes: 55 additions & 0 deletions circuits/test/gfmulint/ghash_gfmul_int.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { assert } from "chai";
import { WitnessTester } from "circomkit";
import { circomkit } from "../common";

// input and output type of GFMULInt
type Arr128 = number[][];
type _Arr128 = number[];

describe("mulX_polyval", () => {
let circuit: WitnessTester<["in"], ["out"]>;

before(async () => {
circuit = await circomkit.WitnessTester("mulX_polyval", {
file: "aes-gcm/helper_functions",
template: "mulX_polyval",
});
});

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("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 {
return value
.toString(2)
.padStart(128, "0")
.split("")
.map((bit) => parseInt(bit, 10));
}

function _parse_arr128_to_number(res: _Arr128): number {
return parseInt(res.join(""), 2);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
File renamed without changes.
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.