Skip to content

Commit 65f823f

Browse files
authored
Documentation restructure (#105)
* clean up aes directory dependencies -> only one utils.circom * address todo to reduce redundant constraint and input value * removing log fixes witness-calc * rename files * rename files * fix CI path * remove unused dependency paths
1 parent 9ef4926 commit 65f823f

19 files changed

+459
-626
lines changed

.github/workflows/circom.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ jobs:
4848
4949
- name: Build witness for aes-gcm
5050
run: |
51-
build-circuit circuits/aes-gcm-fold/aes-gcm-fold.circom aes-fold.bin -l node_modules
51+
build-circuit circuits/aes-gctr-fold/aes-gctr-fold.circom aes-fold.bin -l node_modules
5252

circuits/aes-gcm-fold/aes-gcm-fold.circom

-5
This file was deleted.

circuits/aes-gcm/aes-gcm.circom

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ pragma circom 2.1.9;
22

33
include "ghash.circom";
44
include "aes/cipher.circom";
5-
include "circomlib/circuits/bitify.circom";
65
include "utils.circom";
76
include "gctr.circom";
87

circuits/aes-gcm/aes-gcm-fold.circom circuits/aes-gcm/aes-gctr-fold.circom

+21-18
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
11
pragma circom 2.1.9;
22

3-
include "./aes-gcm-foldable.circom";
3+
include "./aes-gctr-foldable.circom";
44
include "./utils.circom";
55

6-
// Compute AES-GCM
7-
template AESGCMFOLD(INPUT_LEN) {
6+
// Compute AES-GCTR
7+
template AESGCTRFOLD(INPUT_LEN) {
88
assert(INPUT_LEN % 16 == 0);
9-
10-
var DATA_BYTES = (INPUT_LEN * 2) + 5;
11-
9+
var DATA_BYTES = (INPUT_LEN * 2) + 4;
1210
signal input key[16];
1311
signal input iv[12];
1412
signal input aad[16];
1513
signal input plainText[16];
1614

1715
// step_in[0..INPUT_LEN] => accumulate plaintext blocks
1816
// step_in[INPUT_LEN..INPUT_LEN*2] => accumulate ciphertext blocks
19-
// step_in[INPUT_LEN*2..INPUT_LEN*2+4] => lastCounter
20-
// step_in[INPUT_LEN*2+5] => foldedBlocks // TODO(WJ 2024-10-24): technically not needed if can read 4 bytes as a 32 bit number
17+
// step_in[INPUT_LEN*2..INPUT_LEN*2+4] => accumulate counter
2118
signal input step_in[DATA_BYTES];
2219
signal output step_out[DATA_BYTES];
23-
signal counter <== step_in[INPUT_LEN*2 + 4];
20+
signal counter;
21+
22+
// We extract the number from the 4 byte word counter
23+
component last_counter_bits = BytesToBits(4);
24+
for(var i = 0; i < 4; i ++) {
25+
last_counter_bits.in[i] <== step_in[INPUT_LEN*2 + i];
26+
}
27+
component last_counter_num = Bits2Num(32);
28+
// pass in reverse order
29+
for (var i = 0; i< 32; i++){
30+
last_counter_num.in[i] <== last_counter_bits.out[31 - i];
31+
}
32+
33+
counter <== last_counter_num.out - 1;
2434

2535
// write new plain text block.
2636
signal plainTextAccumulator[DATA_BYTES];
@@ -31,7 +41,7 @@ template AESGCMFOLD(INPUT_LEN) {
3141
writeToIndex.out ==> plainTextAccumulator;
3242

3343
// folds one block
34-
component aes = AESGCMFOLDABLE();
44+
component aes = AESGCTRFOLDABLE();
3545
aes.key <== key;
3646
aes.iv <== iv;
3747
aes.aad <== aad;
@@ -55,12 +65,5 @@ template AESGCMFOLD(INPUT_LEN) {
5565
writeCounter.array_to_write_to <== cipherTextAccumulator;
5666
writeCounter.array_to_write_at_index <== aes.counter;
5767
writeCounter.index <== INPUT_LEN*2;
58-
writeCounter.out ==> counterAccumulator;
59-
60-
// accumulate number of folded blocks
61-
component writeNumberOfFoldedBlocks = WriteToIndex(DATA_BYTES, 1);
62-
writeNumberOfFoldedBlocks.array_to_write_to <== counterAccumulator;
63-
writeNumberOfFoldedBlocks.array_to_write_at_index <== [step_in[INPUT_LEN*2 + 4] + 1];
64-
writeNumberOfFoldedBlocks.index <== INPUT_LEN*2 + 4;
65-
writeNumberOfFoldedBlocks.out ==> step_out;
68+
writeCounter.out ==> step_out;
6669
}

circuits/aes-gcm/aes-gcm-foldable.circom circuits/aes-gcm/aes-gctr-foldable.circom

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
pragma circom 2.1.9;
22

33
include "aes/cipher.circom";
4-
include "circomlib/circuits/bitify.circom";
5-
include "circomlib/circuits/mux2.circom";
6-
include "circomlib/circuits/comparators.circom";
7-
include "circomlib/circuits/gates.circom";
84
include "utils.circom";
95
include "gctr.circom";
106

11-
/// AES-GCM with 128 bit key authenticated encryption according to: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf
7+
/// AES-GCTR with 128 bit key encryption according to: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf
128
///
139
/// Parameters:
1410
/// l: length of the plaintext
@@ -21,10 +17,9 @@ include "gctr.circom";
2117
///
2218
/// Outputs:
2319
/// cipherText: encrypted ciphertext
24-
/// authTag: authentication tag
2520
///
2621
/// This folds a single block without authentication via ghash.
27-
template AESGCMFOLDABLE() {
22+
template AESGCTRFOLDABLE() {
2823
// Inputs
2924
signal input key[16]; // 128-bit key
3025
signal input iv[12]; // IV length is 96 bits (12 bytes)

circuits/aes-gcm/aes/cipher.circom

+65-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ include "key_expansion.circom";
55
include "circomlib/circuits/comparators.circom";
66
include "circomlib/circuits/bitify.circom";
77
include "circomlib/circuits/gates.circom";
8-
include "transformations.circom";
98
include "mix_columns.circom";
109

1110
// Cipher Process
@@ -94,4 +93,69 @@ template Cipher(){
9493
}
9594

9695
cipher <== addRoundKey[10].newState;
96+
}
97+
98+
// XORs a cipher state: 4x4 byte array
99+
template AddCipher(){
100+
signal input state[4][4];
101+
signal input cipher[4][4];
102+
signal output newState[4][4];
103+
104+
component xorbyte[4][4];
105+
106+
for (var i = 0; i < 4; i++) {
107+
for (var j = 0; j < 4; j++) {
108+
xorbyte[i][j] = XorByte();
109+
xorbyte[i][j].a <== state[i][j];
110+
xorbyte[i][j].b <== cipher[i][j];
111+
newState[i][j] <== xorbyte[i][j].out;
112+
}
113+
}
114+
}
115+
116+
// ShiftRows: Performs circular left shift on each row
117+
// 0, 1, 2, 3 shifts for rows 0, 1, 2, 3 respectively
118+
template ShiftRows(){
119+
signal input state[4][4];
120+
signal output newState[4][4];
121+
122+
component shiftWord[4];
123+
124+
for (var i = 0; i < 4; i++) {
125+
// Rotate: Performs circular left shift on each row
126+
shiftWord[i] = Rotate(i, 4);
127+
shiftWord[i].bytes <== state[i];
128+
newState[i] <== shiftWord[i].rotated;
129+
}
130+
}
131+
132+
// Applies S-box substitution to each byte
133+
template SubBlock(){
134+
signal input state[4][4];
135+
signal output newState[4][4];
136+
component sbox[4];
137+
138+
for (var i = 0; i < 4; i++) {
139+
sbox[i] = SubstituteWord();
140+
sbox[i].bytes <== state[i];
141+
newState[i] <== sbox[i].substituted;
142+
}
143+
}
144+
145+
// AddRoundKey: XORs the state with transposed the round key
146+
template AddRoundKey(){
147+
signal input state[4][4];
148+
signal input roundKey[4][4];
149+
signal output newState[4][4];
150+
151+
component xorbyte[4][4];
152+
153+
for (var i = 0; i < 4; i++) {
154+
for (var j = 0; j < 4; j++) {
155+
xorbyte[i][j] = XorByte();
156+
xorbyte[i][j].a <== state[i][j];
157+
xorbyte[i][j].b <== roundKey[j][i];
158+
newState[i][j] <== xorbyte[i][j].out;
159+
}
160+
}
97161
}

circuits/aes-gcm/aes/ff.circom

+77
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pragma circom 2.1.9;
44
include "circomlib/circuits/bitify.circom";
55
include "../utils.circom";
66

7+
// All this is for a properly constrained sbox since we don't have lookup arguments
78
// Finite field addition, the signal variable plus a compile-time constant
89
template FieldAddConst(c) {
910
signal input in[8];
@@ -203,4 +204,80 @@ template AffineTransform() {
203204
lc += offset[i];
204205
outBits[i] <== IsOdd(3)(lc);
205206
}
207+
}
208+
209+
// XTimes2: Multiplies by 2 in GF(2^8)
210+
template XTimes2(){
211+
signal input in[8];
212+
signal output out[8];
213+
214+
component xtimeConstant = Num2Bits(8);
215+
xtimeConstant.in <== 0x1b;
216+
217+
component xor[7];
218+
219+
component isZero = IsZero();
220+
isZero.in <== in[7];
221+
222+
out[0] <== 1-isZero.out;
223+
for (var i = 0; i < 7; i++) {
224+
xor[i] = XOR();
225+
xor[i].a <== in[i];
226+
xor[i].b <== xtimeConstant.out[i+1] * (1-isZero.out);
227+
out[i+1] <== xor[i].out;
228+
}
229+
}
230+
231+
// XTimes: Multiplies by n in GF(2^8)
232+
// This uses a fast multiplication algorithm that uses the XTimes2 component
233+
// Number of constaints is always constant
234+
template XTimes(n){
235+
signal input in[8];
236+
signal output out[8];
237+
238+
component bits = Num2Bits(8);
239+
bits.in <== n;
240+
241+
component XTimes2[7];
242+
243+
XTimes2[0] = XTimes2();
244+
XTimes2[0].in <== in;
245+
246+
for (var i = 1; i < 7; i++) {
247+
XTimes2[i] = XTimes2();
248+
XTimes2[i].in <== XTimes2[i-1].out;
249+
}
250+
251+
component xor[8];
252+
component mul[8];
253+
signal inter[8][8];
254+
255+
mul[0] = MulByte();
256+
mul[0].a <== bits.out[0];
257+
mul[0].b <== in;
258+
inter[0] <== mul[0].c;
259+
260+
for (var i = 1; i < 8; i++) {
261+
mul[i] = MulByte();
262+
mul[i].a <== bits.out[i];
263+
mul[i].b <== XTimes2[i-1].out;
264+
265+
xor[i] = XorBits();
266+
xor[i].a <== inter[i-1];
267+
xor[i].b <== mul[i].c;
268+
inter[i] <== xor[i].out;
269+
}
270+
271+
out <== inter[7];
272+
}
273+
274+
// Multiplies a byte by an array of bits
275+
template MulByte(){
276+
signal input a;
277+
signal input b[8];
278+
signal output c[8];
279+
280+
for (var i = 0; i < 8; i++) {
281+
c[i] <== a * b[i];
282+
}
206283
}

circuits/aes-gcm/aes/key_expansion.circom

+59-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// from: https://github.com/crema-labs/aes-circom/tree/main/circuits
22
pragma circom 2.1.9;
33

4-
include "utils.circom";
4+
include "../utils.circom";
5+
include "sbox128.circom";
6+
57

68
// Key Expansion Process
79
//
@@ -107,23 +109,72 @@ template NextRound(){
107109
];
108110
rcon.index <== round-1;
109111
component xorWord[4 + 1];
110-
xorWord[0] = XorWord();
111-
xorWord[0].bytes1 <== substituteWord[0].substituted;
112-
xorWord[0].bytes2 <== rcon.out;
112+
xorWord[0] = XORBLOCK(4);
113+
xorWord[0].a <== substituteWord[0].substituted;
114+
xorWord[0].b <== rcon.out;
113115

114116
for (var i = 0; i < 4; i++) {
115-
xorWord[i+1] = XorWord();
117+
xorWord[i+1] = XORBLOCK(4);
116118
if (i == 0) {
117-
xorWord[i+1].bytes1 <== xorWord[0].out;
119+
xorWord[i+1].a <== xorWord[0].out;
118120
} else {
119-
xorWord[i+1].bytes1 <== nextKey[i-1];
121+
xorWord[i+1].a <== nextKey[i-1];
120122
}
121-
xorWord[i+1].bytes2 <== key[i];
123+
xorWord[i+1].b <== key[i];
122124

123125
for (var j = 0; j < 4; j++) {
124126
nextKey[i][j] <== xorWord[i+1].out[j];
125127
}
126128
}
127129
}
128130

131+
// Outputs a round constant for a given round number
132+
template RCon(round) {
133+
signal output out[4];
134+
135+
assert(round > 0 && round <= 10);
129136

137+
var rcon[10][4] = [
138+
[0x01, 0x00, 0x00, 0x00],
139+
[0x02, 0x00, 0x00, 0x00],
140+
[0x04, 0x00, 0x00, 0x00],
141+
[0x08, 0x00, 0x00, 0x00],
142+
[0x10, 0x00, 0x00, 0x00],
143+
[0x20, 0x00, 0x00, 0x00],
144+
[0x40, 0x00, 0x00, 0x00],
145+
[0x80, 0x00, 0x00, 0x00],
146+
[0x1b, 0x00, 0x00, 0x00],
147+
[0x36, 0x00, 0x00, 0x00]
148+
];
149+
150+
out <== rcon[round-1];
151+
}
152+
153+
// Rotates an array of bytes to the left by a specified rotation
154+
template Rotate(rotation, length) {
155+
assert(rotation < length);
156+
signal input bytes[length];
157+
signal output rotated[length];
158+
159+
for(var i = 0; i < length - rotation; i++) {
160+
rotated[i] <== bytes[i + rotation];
161+
}
162+
163+
for(var i = length - rotation; i < length; i++) {
164+
rotated[i] <== bytes[i - length + rotation];
165+
}
166+
}
167+
168+
// Substitutes each byte in a word using the S-Box
169+
template SubstituteWord() {
170+
signal input bytes[4];
171+
signal output substituted[4];
172+
173+
component sbox[4];
174+
175+
for(var i = 0; i < 4; i++) {
176+
sbox[i] = SBox128();
177+
sbox[i].in <== bytes[i];
178+
substituted[i] <== sbox[i].out;
179+
}
180+
}

0 commit comments

Comments
 (0)