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

impl multiplication for polyval #55

Merged
merged 23 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3698750
impl bmul multiplication subroutine
thor314 Aug 20, 2024
0c4fd52
impl gfmul (mostly)
thor314 Aug 21, 2024
b706432
implement rest of mul
thor314 Sep 4, 2024
e9e4495
notes on rev64 added, renamed mul64 -> wrappingMul64 for clarity
thor314 Sep 5, 2024
b839880
wrapping mul partial impl 1 - testing in progress
thor314 Sep 5, 2024
02a1830
wrapping mul tests correctly running, not passing
thor314 Sep 5, 2024
8980c89
rm component
thor314 Sep 9, 2024
bed8b7a
debug wrapping mul - pause - tests for wrapping mul failing
thor314 Sep 9, 2024
09a7674
impl LE wrapping mul, tests pass
thor314 Sep 11, 2024
ec817c8
parse int le/be
thor314 Sep 12, 2024
35549ee
Switched suite name from Wrapping_LE to Wrapping_BE
KaiGeffen Sep 12, 2024
c6e57c5
Merge https://github.com/pluto/aes-proof into impl-polymul
KaiGeffen Sep 12, 2024
8a18505
wrapping mul moved
thor314 Sep 12, 2024
0176062
Merge branch 'impl-polymul' of https://github.com/pluto/aes-proof int…
KaiGeffen Sep 12, 2024
dcc42ae
Removed unused test impl / resolved test todos
KaiGeffen Sep 12, 2024
0fb4b23
Added wrap_mul test, fixed minor scope smell
KaiGeffen Sep 12, 2024
177658b
tests written for bmul
thor314 Sep 12, 2024
f8fe80b
bugfix: indexing error in BMUL
thor314 Sep 13, 2024
42fe433
tests for gfmul
thor314 Sep 13, 2024
3046074
annotate gfmul empty bits issue
thor314 Sep 13, 2024
a37608c
gfmul debugged
thor314 Sep 13, 2024
c585377
adjusted expected values in gfmul tests; circomkit still only capturi…
thor314 Sep 13, 2024
b11e9be
gfmul tests fixed
thor314 Sep 13, 2024
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
1 change: 1 addition & 0 deletions circuits/aes-gcm/component
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file needed?

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

274 changes: 274 additions & 0 deletions circuits/aes-gcm/gfmul.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
pragma circom 2.1.9;

include "mul.circom";

// Computes carryless POLYVAL multiplication over GF(2^128) in constant time.
//
// Method described at:
// <https://www.bearssl.org/constanttime.html#ghash-for-gcm>
//
// POLYVAL multiplication is effectively the little endian equivalent of
// GHASH multiplication, aside from one small detail described here:
//
// <https://crypto.stackexchange.com/questions/66448/how-does-bearssls-gcm-modular-reduction-work/66462#66462>
//
// > The product of two bit-reversed 128-bit polynomials yields the
// > bit-reversed result over 255 bits, not 256. The BearSSL code ends up
// > with a 256-bit result in zw[], and that value is shifted by one bit,
// > because of that reversed convention issue. Thus, the code must
// > include a shifting step to put it back where it should
//
// This shift is unnecessary for POLYVAL and has been removed.
//
// ref: https://github.com/RustCrypto/universal-hashes/blob/master/polyval/src/backend/soft64.rs#L151
template MUL() {
signal input a[2][64];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the intuition behind doing [2][64] vs [128]? Given each is represented as a field element internally, my hunch is we don't gain efficiency out of this (tho not certain) while having worse readability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe to use REV64()?

signal input b[2][64];
signal output out[2][64];

// variable aliases to make indexing logic easier to state
signal h[3][64];
signal y[3][64];
signal h_r[3][64];
signal y_r[3][64];

h[0] <== a[0];
h[1] <== a[1];
y[0] <== b[0];
y[1] <== b[1];
component Revs[4];
for (var i = 0; i < 2; i++) {
Revs[i] = REV64();
Revs[i].in <== h[i];
h_r[i] <== Revs[i].out;
}
for (var i = 0; i < 2; i++) {
Revs[i+2] = REV64();
Revs[i+2].in <== y[i];
y_r[i] <== Revs[i+2].out;
}

// h2 = h0^h1; y2 = y0^y1
component Xors[4];
for (var i = 0; i < 4; i++) Xors[i] = BitwiseXor(64);
Xors[0].a <== h[0];
Xors[0].b <== h[1];
h[2] <== Xors[0].out;
Xors[1].a <== y[0];
Xors[1].b <== y[1];
y[2] <== Xors[1].out;

// h2_r = h0_r^h1_r; y2_r = y0_r^y1_r
Xors[2].a <== h_r[0];
Xors[2].b <== h_r[1];
h_r[2] <== Xors[2].out;
Xors[3].a <== y_r[0];
Xors[3].b <== y_r[1];
y_r[2] <== Xors[3].out;

// z0 = bmul64(y0, h0); z1 = bmul64(y1, h1); z2 = bmul64(y2, h2);
// z0_h = bmul64(y0_r, h0_r); z1_h = bmul64(y1_r, h1_r); z2_h = bmul64(y2_r, h2_r);
component BMUL64_z[6];
signal z[3][64];
signal zh[3][64];
for (var i = 0; i < 3; i++) {
BMUL64_z[i] = BMUL64();
BMUL64_z[i].x <== y[i];
BMUL64_z[i].y <== h[i];
z[i] <== BMUL64_z[i].out;

BMUL64_z[i+3] = BMUL64();
BMUL64_z[i+3].x <== y_r[i];
BMUL64_z[i+3].y <== h_r[i];
zh[i] <== BMUL64_z[i+3].out;
}

// _z2 = z0 ^ z1 ^ z2;
// _z2h = z0h ^ z1h ^ z2h;
signal _z2[64];
signal _zh[3][64];
component XorMultiples[2];
XorMultiples[0] = XorMultiple(3, 64);
XorMultiples[0].inputs <== z;
_z2 <== XorMultiples[0].out;

XorMultiples[1] = XorMultiple(3, 64);
XorMultiples[1].inputs <== zh;
_zh[0] <== XorMultiples[1].out;
_zh[1] <== zh[1];
_zh[2] <== zh[2];

// z0h = rev64(z0h) >> 1;
// z1h = rev64(z1h) >> 1;
// _z2h = rev64(_z2h) >> 1;
// signal _zh[3][64];
signal __zh[3][64];
component Revs_zh[3];
component RightShifts_zh[3];
for (var i = 0; i < 3; i++) {
Revs_zh[i] = REV64();
RightShifts_zh[i] = BitwiseRightShift(64, 1);
Revs_zh[i].in <== zh[i];
RightShifts_zh[i].in <== Revs_zh[i].out;
__zh[i] <== RightShifts_zh[i].out;
}

// let v0 = z0;
// let mut v1 = z0h ^ z2;
// let mut v2 = z1 ^ z2h;
// let mut v3 = z1h;
signal v[4][64];
component Xors_v[2];
v[0] <== z[0];
v[3] <== __zh[1];
Xors_v[0] = BitwiseXor(64);
Xors_v[0].a <== __zh[0];
Xors_v[0].b <== _z2;
v[1] <== Xors_v[0].out;
Xors_v[1] = BitwiseXor(64);
Xors_v[1].a <== z[1];
Xors_v[1].b <== __zh[2];
v[2] <== Xors_v[1].out;


// _v2 = v2 ^ v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7);
// _v1 = v1 ^ (v0 << 63) ^ (v0 << 62) ^ (v0 << 57);
// _v3 = v3 ^ _v1 ^ (_v1 >> 1) ^ (_v1 >> 2) ^ (_v1 >> 7);
// __v2 = _v2 ^ (_v1 << 63) ^ (_v1 << 62) ^ (_v1 << 57);
signal _v2[64];
signal _v1[64];
signal _v3[64];
signal __v2[64];
component RS_v[6];
component LS_v[6];

component XorMultiples_R[2];
component XorMultiples_L[2];
for (var i=0; i<2; i++) {
XorMultiples_R[i] = XorMultiple(5, 64);
XorMultiples_L[i] = XorMultiple(4, 64);
}

RS_v[0] = BitwiseRightShift(64, 1);
RS_v[0].in <== v[0];
RS_v[1] = BitwiseRightShift(64, 2);
RS_v[1].in <== v[0];
RS_v[2] = BitwiseRightShift(64, 7);
RS_v[2].in <== v[0];

LS_v[0] = BitwiseLeftShift(64, 63);
LS_v[0].in <== v[0];
LS_v[1] = BitwiseLeftShift(64, 62);
LS_v[1].in <== v[0];
LS_v[2] = BitwiseLeftShift(64, 57);
LS_v[2].in <== v[0];

XorMultiples_R[0].inputs <== [v[2], v[0], RS_v[0].out, RS_v[1].out, RS_v[2].out];
_v2 <== XorMultiples_R[0].out;
XorMultiples_L[0].inputs <== [v[1], LS_v[0].out, LS_v[1].out, LS_v[2].out];
_v1 <== XorMultiples_L[0].out;

RS_v[3] = BitwiseRightShift(64, 1);
RS_v[3].in <== _v1;
RS_v[4] = BitwiseRightShift(64, 2);
RS_v[4].in <== _v1;
RS_v[5] = BitwiseRightShift(64, 7);
RS_v[5].in <== _v1;

LS_v[3] = BitwiseLeftShift(64, 63);
LS_v[3].in <== _v1;
LS_v[4] = BitwiseLeftShift(64, 62);
LS_v[4].in <== _v1;
LS_v[5] = BitwiseLeftShift(64, 57);
LS_v[5].in <== _v1;

XorMultiples_R[1].inputs <== [v[3], _v1, RS_v[3].out, RS_v[4].out, RS_v[5].out];
_v3 <== XorMultiples_R[1].out;
XorMultiples_L[1].inputs <== [_v2, LS_v[0].out, LS_v[1].out, LS_v[2].out];
__v2 <== XorMultiples_L[1].out;

out <== [__v2, _v3];
}

// Multiplication in GF(2)[X], truncated to the low 64-bits, with “holes”
// (sequences of zeroes) to avoid carry spilling.
//
// When carries do occur, they wind up in a "hole" and are subsequently masked
// out of the result.
//
// ref: https://github.com/RustCrypto/universal-hashes/blob/master/polyval/src/backend/soft64.rs#L206
template BMUL64() {
signal input x[64];
signal input y[64];
signal output out[64];

signal xs[4][64];
signal ys[4][64];
// var masks[4] = [0x1111111111111111, 0x2222222222222222, 0x4444444444444444, 0x8888888888888888];
var masks[4][64] = [
[0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1],
[0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0],
[0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,
0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0],
[1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,
1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0]];

component ands[3][4];
for (var i = 0; i < 4; i++) {
ands[0][i] = BitwiseAnd(64);
ands[0][i].a <== masks[i];
ands[0][i].b <== x;
xs[i] <== ands[0][i].out;

ands[1][i] = BitwiseAnd(64);
ands[1][i].a <== masks[i];
ands[1][i].b <== y;
ys[i] <== ands[1][i].out;
}

// w_{i,j} = x_j * y_{i-j%4}
component muls[4][4];
// z_i = XOR(w_{i,0}, w_{i,1}, w_{i,2}, w_{i,3})
component xor_multiples[4];
signal z_mid[4][4][64];
signal z[4][64];
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
var Y_INDEX = (i - j) % 4;
muls[i][j] = Mul64();
muls[i][j].src1 <== xs[j];
muls[i][j].src2 <== ys[Y_INDEX];
z_mid[i][j] <== muls[i][j].out;
}

xor_multiples[i] = XorMultiple(4, 64);
xor_multiples[i].inputs <== z_mid[i];
z[i] <== xor_multiples[i].out;
}

// z_masked[i] = z[i] & masks[i]
signal z_masked[4][64];
for (var i = 0; i < 4; i++) {
ands[2][i] = BitwiseAnd(64);
ands[2][i].a <== masks[i];
ands[2][i].b <== z[i];
z_masked[i] <== ands[2][i].out;
}

// out = z_masked[0] | z_masked[1] | z_masked[2] | z_masked[3]
component or_multiple = OrMultiple(4, 64);
or_multiple.inputs <== z_masked;
out <== or_multiple.out;
}

// todo: verify this is what was actually meant
template REV64(){
signal input in[64];
signal output out[64];

for (var i = 0; i < 64; i++) {
out[i] <== in[63 - i];
}
}
17 changes: 14 additions & 3 deletions circuits/aes-gcm/hashes.circom
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,29 @@ template GHASH(n_msg_bits) {
// }


template POLYVAL(n_msg_bits)
{
template POLYVAL(n_msg_bits) {
signal input msg[n_msg_bits];
// Hash Key
signal input H[128];
// signal input T[2][64]; // TODO
signal output out[128];

for (var i = 0; i < 128; i++) {
out[i] <== 1;
}

}

// test re-implementation of POLYVAL avoiding 128-bit arrays for a possible speed-up
template POLYVAL_2_64(blocks) {
signal input msg[blocks][64];
// Hash Key
signal input H[2][64];
signal output out[2][64];

// for (var i = 0; i < 128; i++) {
// out[i] <== 1;
// }
}
// {
// var msg_len = n_bits/8;
// signal input in[n_bits];
Expand Down
52 changes: 52 additions & 0 deletions circuits/aes-gcm/helper_functions.circom
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ template BitwiseAnd(n) {
}
}

template BitwiseOr(n) {
signal input a[n];
signal input b[n];
signal output out[n];

for (var i=0; i<n; i++) {
out[i] <== a[i] + b[i] - a[i]*b[i];
}
}

template IntAnd(n)
{
signal input a;
Expand Down Expand Up @@ -229,6 +239,47 @@ template SumMultiple(n) {
sum <== sums[n-1];
}

// compute the OR of n inputs, each m bits wide
template OrMultiple(n, m) {
signal input inputs[n][m];
signal output out[m];

signal mids[n][m];
mids[0] <== inputs[0];

component ors[n-1];
for(var i=0; i<n-1; i++) {
ors[i] = BitwiseOr(m);
ors[i].a <== mids[i];
ors[i].b <== inputs[i+1];
mids[i+1] <== ors[i].out;
}

out <== mids[n-1];
}

// compute the XOR of n inputs, each m bits wide
template XorMultiple(n, m) {
signal input inputs[n][m];
signal output out[m];

signal mids[n][m];
mids[0] <== inputs[0];

component xors[n-1];
for(var i=0; i<n-1; i++) {
xors[i] = BitwiseXor(m);
xors[i].a <== mids[i];
xors[i].b <== inputs[i+1];
mids[i+1] <== xors[i].out;
}

out <== mids[n-1];
}

// select the index-th element from an array of total elements
// via the argument:
// Sum_0^n (IsEqual(index, i) * in[i])
template IndexSelector(total) {
signal input in[total];
signal input index;
Expand All @@ -249,6 +300,7 @@ template IndexSelector(total) {
out <== calcTotal.sum;
}

// reverse the bit order in an n-bit array
template ReverseBitsArray(n) {
signal input in[n];
signal output out[n];
Expand Down
Loading