Skip to content

Commit

Permalink
Documentation restructure (#105)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
0xJepsen authored Oct 29, 2024
1 parent 9ef4926 commit 65f823f
Show file tree
Hide file tree
Showing 19 changed files with 459 additions and 626 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/circom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ jobs:
- name: Build witness for aes-gcm
run: |
build-circuit circuits/aes-gcm-fold/aes-gcm-fold.circom aes-fold.bin -l node_modules
build-circuit circuits/aes-gctr-fold/aes-gctr-fold.circom aes-fold.bin -l node_modules
5 changes: 0 additions & 5 deletions circuits/aes-gcm-fold/aes-gcm-fold.circom

This file was deleted.

1 change: 0 additions & 1 deletion circuits/aes-gcm/aes-gcm.circom
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ pragma circom 2.1.9;

include "ghash.circom";
include "aes/cipher.circom";
include "circomlib/circuits/bitify.circom";
include "utils.circom";
include "gctr.circom";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
pragma circom 2.1.9;

include "./aes-gcm-foldable.circom";
include "./aes-gctr-foldable.circom";
include "./utils.circom";

// Compute AES-GCM
template AESGCMFOLD(INPUT_LEN) {
// Compute AES-GCTR
template AESGCTRFOLD(INPUT_LEN) {
assert(INPUT_LEN % 16 == 0);

var DATA_BYTES = (INPUT_LEN * 2) + 5;

var DATA_BYTES = (INPUT_LEN * 2) + 4;
signal input key[16];
signal input iv[12];
signal input aad[16];
signal input plainText[16];

// step_in[0..INPUT_LEN] => accumulate plaintext blocks
// step_in[INPUT_LEN..INPUT_LEN*2] => accumulate ciphertext blocks
// step_in[INPUT_LEN*2..INPUT_LEN*2+4] => lastCounter
// 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
// step_in[INPUT_LEN*2..INPUT_LEN*2+4] => accumulate counter
signal input step_in[DATA_BYTES];
signal output step_out[DATA_BYTES];
signal counter <== step_in[INPUT_LEN*2 + 4];
signal counter;

// We extract the number from the 4 byte word counter
component last_counter_bits = BytesToBits(4);
for(var i = 0; i < 4; i ++) {
last_counter_bits.in[i] <== step_in[INPUT_LEN*2 + i];
}
component last_counter_num = Bits2Num(32);
// pass in reverse order
for (var i = 0; i< 32; i++){
last_counter_num.in[i] <== last_counter_bits.out[31 - i];
}

counter <== last_counter_num.out - 1;

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

// folds one block
component aes = AESGCMFOLDABLE();
component aes = AESGCTRFOLDABLE();
aes.key <== key;
aes.iv <== iv;
aes.aad <== aad;
Expand All @@ -55,12 +65,5 @@ template AESGCMFOLD(INPUT_LEN) {
writeCounter.array_to_write_to <== cipherTextAccumulator;
writeCounter.array_to_write_at_index <== aes.counter;
writeCounter.index <== INPUT_LEN*2;
writeCounter.out ==> counterAccumulator;

// accumulate number of folded blocks
component writeNumberOfFoldedBlocks = WriteToIndex(DATA_BYTES, 1);
writeNumberOfFoldedBlocks.array_to_write_to <== counterAccumulator;
writeNumberOfFoldedBlocks.array_to_write_at_index <== [step_in[INPUT_LEN*2 + 4] + 1];
writeNumberOfFoldedBlocks.index <== INPUT_LEN*2 + 4;
writeNumberOfFoldedBlocks.out ==> step_out;
writeCounter.out ==> step_out;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
pragma circom 2.1.9;

include "aes/cipher.circom";
include "circomlib/circuits/bitify.circom";
include "circomlib/circuits/mux2.circom";
include "circomlib/circuits/comparators.circom";
include "circomlib/circuits/gates.circom";
include "utils.circom";
include "gctr.circom";

/// AES-GCM with 128 bit key authenticated encryption according to: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf
/// AES-GCTR with 128 bit key encryption according to: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf
///
/// Parameters:
/// l: length of the plaintext
Expand All @@ -21,10 +17,9 @@ include "gctr.circom";
///
/// Outputs:
/// cipherText: encrypted ciphertext
/// authTag: authentication tag
///
/// This folds a single block without authentication via ghash.
template AESGCMFOLDABLE() {
template AESGCTRFOLDABLE() {
// Inputs
signal input key[16]; // 128-bit key
signal input iv[12]; // IV length is 96 bits (12 bytes)
Expand Down
66 changes: 65 additions & 1 deletion circuits/aes-gcm/aes/cipher.circom
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ include "key_expansion.circom";
include "circomlib/circuits/comparators.circom";
include "circomlib/circuits/bitify.circom";
include "circomlib/circuits/gates.circom";
include "transformations.circom";
include "mix_columns.circom";

// Cipher Process
Expand Down Expand Up @@ -94,4 +93,69 @@ template Cipher(){
}

cipher <== addRoundKey[10].newState;
}

// XORs a cipher state: 4x4 byte array
template AddCipher(){
signal input state[4][4];
signal input cipher[4][4];
signal output newState[4][4];

component xorbyte[4][4];

for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
xorbyte[i][j] = XorByte();
xorbyte[i][j].a <== state[i][j];
xorbyte[i][j].b <== cipher[i][j];
newState[i][j] <== xorbyte[i][j].out;
}
}
}

// ShiftRows: Performs circular left shift on each row
// 0, 1, 2, 3 shifts for rows 0, 1, 2, 3 respectively
template ShiftRows(){
signal input state[4][4];
signal output newState[4][4];

component shiftWord[4];

for (var i = 0; i < 4; i++) {
// Rotate: Performs circular left shift on each row
shiftWord[i] = Rotate(i, 4);
shiftWord[i].bytes <== state[i];
newState[i] <== shiftWord[i].rotated;
}
}

// Applies S-box substitution to each byte
template SubBlock(){
signal input state[4][4];
signal output newState[4][4];
component sbox[4];

for (var i = 0; i < 4; i++) {
sbox[i] = SubstituteWord();
sbox[i].bytes <== state[i];
newState[i] <== sbox[i].substituted;
}
}

// AddRoundKey: XORs the state with transposed the round key
template AddRoundKey(){
signal input state[4][4];
signal input roundKey[4][4];
signal output newState[4][4];

component xorbyte[4][4];

for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
xorbyte[i][j] = XorByte();
xorbyte[i][j].a <== state[i][j];
xorbyte[i][j].b <== roundKey[j][i];
newState[i][j] <== xorbyte[i][j].out;
}
}
}
77 changes: 77 additions & 0 deletions circuits/aes-gcm/aes/ff.circom
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma circom 2.1.9;
include "circomlib/circuits/bitify.circom";
include "../utils.circom";

// All this is for a properly constrained sbox since we don't have lookup arguments
// Finite field addition, the signal variable plus a compile-time constant
template FieldAddConst(c) {
signal input in[8];
Expand Down Expand Up @@ -203,4 +204,80 @@ template AffineTransform() {
lc += offset[i];
outBits[i] <== IsOdd(3)(lc);
}
}

// XTimes2: Multiplies by 2 in GF(2^8)
template XTimes2(){
signal input in[8];
signal output out[8];

component xtimeConstant = Num2Bits(8);
xtimeConstant.in <== 0x1b;

component xor[7];

component isZero = IsZero();
isZero.in <== in[7];

out[0] <== 1-isZero.out;
for (var i = 0; i < 7; i++) {
xor[i] = XOR();
xor[i].a <== in[i];
xor[i].b <== xtimeConstant.out[i+1] * (1-isZero.out);
out[i+1] <== xor[i].out;
}
}

// XTimes: Multiplies by n in GF(2^8)
// This uses a fast multiplication algorithm that uses the XTimes2 component
// Number of constaints is always constant
template XTimes(n){
signal input in[8];
signal output out[8];

component bits = Num2Bits(8);
bits.in <== n;

component XTimes2[7];

XTimes2[0] = XTimes2();
XTimes2[0].in <== in;

for (var i = 1; i < 7; i++) {
XTimes2[i] = XTimes2();
XTimes2[i].in <== XTimes2[i-1].out;
}

component xor[8];
component mul[8];
signal inter[8][8];

mul[0] = MulByte();
mul[0].a <== bits.out[0];
mul[0].b <== in;
inter[0] <== mul[0].c;

for (var i = 1; i < 8; i++) {
mul[i] = MulByte();
mul[i].a <== bits.out[i];
mul[i].b <== XTimes2[i-1].out;

xor[i] = XorBits();
xor[i].a <== inter[i-1];
xor[i].b <== mul[i].c;
inter[i] <== xor[i].out;
}

out <== inter[7];
}

// Multiplies a byte by an array of bits
template MulByte(){
signal input a;
signal input b[8];
signal output c[8];

for (var i = 0; i < 8; i++) {
c[i] <== a * b[i];
}
}
67 changes: 59 additions & 8 deletions circuits/aes-gcm/aes/key_expansion.circom
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// from: https://github.com/crema-labs/aes-circom/tree/main/circuits
pragma circom 2.1.9;

include "utils.circom";
include "../utils.circom";
include "sbox128.circom";


// Key Expansion Process
//
Expand Down Expand Up @@ -107,23 +109,72 @@ template NextRound(){
];
rcon.index <== round-1;
component xorWord[4 + 1];
xorWord[0] = XorWord();
xorWord[0].bytes1 <== substituteWord[0].substituted;
xorWord[0].bytes2 <== rcon.out;
xorWord[0] = XORBLOCK(4);
xorWord[0].a <== substituteWord[0].substituted;
xorWord[0].b <== rcon.out;

for (var i = 0; i < 4; i++) {
xorWord[i+1] = XorWord();
xorWord[i+1] = XORBLOCK(4);
if (i == 0) {
xorWord[i+1].bytes1 <== xorWord[0].out;
xorWord[i+1].a <== xorWord[0].out;
} else {
xorWord[i+1].bytes1 <== nextKey[i-1];
xorWord[i+1].a <== nextKey[i-1];
}
xorWord[i+1].bytes2 <== key[i];
xorWord[i+1].b <== key[i];

for (var j = 0; j < 4; j++) {
nextKey[i][j] <== xorWord[i+1].out[j];
}
}
}

// Outputs a round constant for a given round number
template RCon(round) {
signal output out[4];

assert(round > 0 && round <= 10);

var rcon[10][4] = [
[0x01, 0x00, 0x00, 0x00],
[0x02, 0x00, 0x00, 0x00],
[0x04, 0x00, 0x00, 0x00],
[0x08, 0x00, 0x00, 0x00],
[0x10, 0x00, 0x00, 0x00],
[0x20, 0x00, 0x00, 0x00],
[0x40, 0x00, 0x00, 0x00],
[0x80, 0x00, 0x00, 0x00],
[0x1b, 0x00, 0x00, 0x00],
[0x36, 0x00, 0x00, 0x00]
];

out <== rcon[round-1];
}

// Rotates an array of bytes to the left by a specified rotation
template Rotate(rotation, length) {
assert(rotation < length);
signal input bytes[length];
signal output rotated[length];

for(var i = 0; i < length - rotation; i++) {
rotated[i] <== bytes[i + rotation];
}

for(var i = length - rotation; i < length; i++) {
rotated[i] <== bytes[i - length + rotation];
}
}

// Substitutes each byte in a word using the S-Box
template SubstituteWord() {
signal input bytes[4];
signal output substituted[4];

component sbox[4];

for(var i = 0; i < 4; i++) {
sbox[i] = SBox128();
sbox[i].in <== bytes[i];
substituted[i] <== sbox[i].out;
}
}
Loading

0 comments on commit 65f823f

Please sign in to comment.