diff --git a/.github/workflows/circom.yml b/.github/workflows/circom.yml index be811a6..f73f82e 100644 --- a/.github/workflows/circom.yml +++ b/.github/workflows/circom.yml @@ -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 diff --git a/circuits/aes-gcm-fold/aes-gcm-fold.circom b/circuits/aes-gcm-fold/aes-gcm-fold.circom deleted file mode 100644 index 490b3cc..0000000 --- a/circuits/aes-gcm-fold/aes-gcm-fold.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../aes-gcm/aes-gcm-fold.circom"; - -component main = AESGCMFOLD(16); diff --git a/circuits/aes-gcm/aes-gcm.circom b/circuits/aes-gcm/aes-gcm.circom index d7705cc..3ffcd9f 100644 --- a/circuits/aes-gcm/aes-gcm.circom +++ b/circuits/aes-gcm/aes-gcm.circom @@ -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"; diff --git a/circuits/aes-gcm/aes-gcm-fold.circom b/circuits/aes-gcm/aes-gctr-fold.circom similarity index 66% rename from circuits/aes-gcm/aes-gcm-fold.circom rename to circuits/aes-gcm/aes-gctr-fold.circom index f26d0cb..2498ba5 100644 --- a/circuits/aes-gcm/aes-gcm-fold.circom +++ b/circuits/aes-gcm/aes-gctr-fold.circom @@ -1,14 +1,12 @@ 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]; @@ -16,11 +14,23 @@ template AESGCMFOLD(INPUT_LEN) { // 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]; @@ -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; @@ -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; } \ No newline at end of file diff --git a/circuits/aes-gcm/aes-gcm-foldable.circom b/circuits/aes-gcm/aes-gctr-foldable.circom similarity index 84% rename from circuits/aes-gcm/aes-gcm-foldable.circom rename to circuits/aes-gcm/aes-gctr-foldable.circom index 626e716..2ccbcb8 100644 --- a/circuits/aes-gcm/aes-gcm-foldable.circom +++ b/circuits/aes-gcm/aes-gctr-foldable.circom @@ -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 @@ -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) diff --git a/circuits/aes-gcm/aes/cipher.circom b/circuits/aes-gcm/aes/cipher.circom index 7686d54..3c715af 100644 --- a/circuits/aes-gcm/aes/cipher.circom +++ b/circuits/aes-gcm/aes/cipher.circom @@ -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 @@ -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; + } + } } \ No newline at end of file diff --git a/circuits/aes-gcm/aes/ff.circom b/circuits/aes-gcm/aes/ff.circom index 71fcbbc..ccb3219 100644 --- a/circuits/aes-gcm/aes/ff.circom +++ b/circuits/aes-gcm/aes/ff.circom @@ -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]; @@ -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]; + } } \ No newline at end of file diff --git a/circuits/aes-gcm/aes/key_expansion.circom b/circuits/aes-gcm/aes/key_expansion.circom index bfac4a3..ed81005 100644 --- a/circuits/aes-gcm/aes/key_expansion.circom +++ b/circuits/aes-gcm/aes/key_expansion.circom @@ -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 // @@ -107,18 +109,18 @@ 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]; @@ -126,4 +128,53 @@ template NextRound(){ } } +// 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; + } +} \ No newline at end of file diff --git a/circuits/aes-gcm/aes/mix_columns.circom b/circuits/aes-gcm/aes/mix_columns.circom index 53e586e..47a34d9 100644 --- a/circuits/aes-gcm/aes/mix_columns.circom +++ b/circuits/aes-gcm/aes/mix_columns.circom @@ -1,11 +1,10 @@ // from: https://github.com/crema-labs/aes-circom/tree/main/circuits pragma circom 2.1.9; -include "transformations.circom"; +include "key_expansion.circom"; include "circomlib/circuits/comparators.circom"; include "circomlib/circuits/bitify.circom"; include "circomlib/circuits/gates.circom"; -include "tbox.circom"; // MixColumns: Applies the equation for each column: // [s'0,c] [2 3 1 1][s0,c] @@ -197,4 +196,15 @@ template S3() { } out <== b2n.out; +} + +template TBox(index) { + signal input subindex; + signal output out; + + if (index == 0) { + out <== FieldMul2()(subindex); + } else if (index == 1) { + out <== FieldMul3()(subindex); + } } \ No newline at end of file diff --git a/circuits/aes-gcm/aes/tbox.circom b/circuits/aes-gcm/aes/tbox.circom deleted file mode 100644 index 5430cc3..0000000 --- a/circuits/aes-gcm/aes/tbox.circom +++ /dev/null @@ -1,15 +0,0 @@ -// from: https://github.com/crema-labs/aes-circom/tree/main/circuits -pragma circom 2.1.9; - -include "./ff.circom"; - -template TBox(index) { - signal input subindex; - signal output out; - - if (index == 0) { - out <== FieldMul2()(subindex); - } else if (index == 1) { - out <== FieldMul3()(subindex); - } -} \ No newline at end of file diff --git a/circuits/aes-gcm/aes/transformations.circom b/circuits/aes-gcm/aes/transformations.circom deleted file mode 100644 index b7ce0d0..0000000 --- a/circuits/aes-gcm/aes/transformations.circom +++ /dev/null @@ -1,120 +0,0 @@ -// from: https://github.com/crema-labs/aes-circom/tree/main/circuits -pragma circom 2.1.9; - -include "circomlib/circuits/comparators.circom"; -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/gates.circom"; -include "utils.circom"; - -// 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; - } - } -} - -// 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]; -} - diff --git a/circuits/aes-gcm/aes/utils.circom b/circuits/aes-gcm/aes/utils.circom deleted file mode 100644 index 3f162f0..0000000 --- a/circuits/aes-gcm/aes/utils.circom +++ /dev/null @@ -1,201 +0,0 @@ -// from: https://github.com/crema-labs/aes-circom/tree/main/circuits -pragma circom 2.1.9; - -include "sbox128.circom"; -include "circomlib/circuits/comparators.circom"; -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/gates.circom"; -include "../utils.circom"; - -// 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; - } -} - -// 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]; -} - - -// XORs two words (arrays of 4 bytes each) -template XorWord() { - signal input bytes1[4]; - signal input bytes2[4]; - - component n2b[4 * 2]; - component b2n[4]; - component xor[4][8]; - - signal output out[4]; - - for(var i = 0; i < 4; i++) { - n2b[2 * i] = Num2Bits(8); - n2b[2 * i + 1] = Num2Bits(8); - n2b[2 * i].in <== bytes1[i]; - n2b[2 * i + 1].in <== bytes2[i]; - b2n[i] = Bits2Num(8); - - for (var j = 0; j < 8; j++) { - xor[i][j] = XOR(); - xor[i][j].a <== n2b[2 * i].out[j]; - xor[i][j].b <== n2b[2 * i + 1].out[j]; - b2n[i].in[j] <== xor[i][j].out; - } - - out[i] <== b2n[i].out; - } -} - -// 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]; - } -} - - -//convert stream of plain text to blocks of 16 bytes -template ToBlocks(l){ - signal input stream[l]; - - var n = l\16; - if(l%16 > 0){ - n = n + 1; - } - signal output blocks[n][4][4]; - - var i, j, k; - - for (var idx = 0; idx < l; idx++) { - blocks[i][k][j] <== stream[idx]; - k = k + 1; - if (k == 4){ - k = 0; - j = j + 1; - if (j == 4){ - j = 0; - i = i + 1; - } - } - } - - if (l%16 > 0){ - blocks[i][k][j] <== 1; - k = k + 1; - } -} - -// converts a stream to row wise blocks of 16 bytes -template ToBlocksRowWise(l){ - signal input stream[l]; - - var n = l\16; - if(l%16 > 0){ - n = n + 1; - } - signal output blocks[n][4][4]; - - var i, j, k; - for (var idx = 0; idx < l; idx++) { - blocks[i][k][j] <== stream[idx]; - j = (j + 1); - if (j == 4){ - k = k + 1; - j = 0; - if (k == 4){ - j = 0; - i = i + 1; - } - } - } - - if (l%16 > 0){ - blocks[i][k][j] <== 1; - j = j + 1; - } -} - -// convert blocks of 16 bytes to stream of bytes -template ToStream(n,l){ - signal input blocks[n][4][4]; - - signal output stream[l]; - - var i, j, k; - - while(i*16 + j*4 + k < l){ - stream[i*16 + j*4 + k] <== blocks[i][k][j]; - k = k + 1; - if (k == 4){ - k = 0; - j = j + 1; - if (j == 4){ - j = 0; - i = i + 1; - } - } - } -} - -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; - } - } -} \ No newline at end of file diff --git a/circuits/aes-gcm/ghash_gmul.circom b/circuits/aes-gcm/ghash_gmul.circom index be3e7a3..017f1a0 100644 --- a/circuits/aes-gcm/ghash_gmul.circom +++ b/circuits/aes-gcm/ghash_gmul.circom @@ -1,9 +1,7 @@ pragma circom 2.1.9; -include "circomlib/circuits/comparators.circom"; // isZero include "utils.circom"; // bitwise right shift include "circomlib/circuits/mux1.circom"; // multiplexer -include "aes/utils.circom"; // xorbyte // Algorithm 1: X •Y // Input: diff --git a/circuits/aes-gcm/utils.circom b/circuits/aes-gcm/utils.circom index 1827956..01167c3 100644 --- a/circuits/aes-gcm/utils.circom +++ b/circuits/aes-gcm/utils.circom @@ -418,3 +418,56 @@ template WriteToIndex(m, n) { accum_index += writeSelector[i-1].out; } } + +//convert stream of plain text to blocks of 16 bytes +template ToBlocks(l){ + signal input stream[l]; + + var n = l\16; + if(l%16 > 0){ + n = n + 1; + } + signal output blocks[n][4][4]; + + var i, j, k; + + for (var idx = 0; idx < l; idx++) { + blocks[i][k][j] <== stream[idx]; + k = k + 1; + if (k == 4){ + k = 0; + j = j + 1; + if (j == 4){ + j = 0; + i = i + 1; + } + } + } + + if (l%16 > 0){ + blocks[i][k][j] <== 1; + k = k + 1; + } +} + +// convert blocks of 16 bytes to stream of bytes +template ToStream(n,l){ + signal input blocks[n][4][4]; + + signal output stream[l]; + + var i, j, k; + + while(i*16 + j*4 + k < l){ + stream[i*16 + j*4 + k] <== blocks[i][k][j]; + k = k + 1; + if (k == 4){ + k = 0; + j = j + 1; + if (j == 4){ + j = 0; + i = i + 1; + } + } + } +} \ No newline at end of file diff --git a/circuits/aes-gctr-fold/aes-gctr-fold.circom b/circuits/aes-gctr-fold/aes-gctr-fold.circom new file mode 100644 index 0000000..fd67238 --- /dev/null +++ b/circuits/aes-gctr-fold/aes-gctr-fold.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../aes-gcm/aes-gctr-fold.circom"; + +component main = AESGCTRFOLD(16); diff --git a/circuits/test/aes-gcm/aes-gcm-fold.test.ts b/circuits/test/aes-gcm/aes-gctr-fold.test.ts similarity index 86% rename from circuits/test/aes-gcm/aes-gcm-fold.test.ts rename to circuits/test/aes-gcm/aes-gctr-fold.test.ts index d8ef052..1f0650e 100644 --- a/circuits/test/aes-gcm/aes-gcm-fold.test.ts +++ b/circuits/test/aes-gcm/aes-gctr-fold.test.ts @@ -2,13 +2,13 @@ import { assert } from "chai"; import { WitnessTester } from "circomkit"; import { circomkit, hexBytesToBigInt, hexToBytes } from "../common"; -describe("aes-gcm-fold", () => { +describe("aes-gctr-fold", () => { let circuit_one_block: WitnessTester<["key", "iv", "plainText", "aad", "step_in"], ["step_out"]>; it("all correct for self generated single zero pt block case", async () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/aes-gcm-fold", - template: "AESGCMFOLD", + file: "aes-gcm/aes-gctr-fold", + template: "AESGCTRFOLD", params: [16], // input len is 16 bytes }); @@ -18,23 +18,20 @@ describe("aes-gcm-fold", () => { let aad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; let ct = [0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78]; - const counter = [0x00, 0x00, 0x00, 0x01]; - const foldedBlocks = [0x00]; - const step_in = new Array(32).fill(0x00).concat(counter).concat(foldedBlocks); - - let expected = plainText.concat(ct).concat([0x00, 0x00, 0x00, 0x02]).concat([0x01]); + const counter = [0x00, 0x00, 0x00, 0x01]; + const step_in = new Array(32).fill(0x00).concat(counter); + let expected = plainText.concat(ct).concat([0x00, 0x00, 0x00, 0x02]); const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText, aad: aad, step_in: step_in }, ["step_out"]) assert.deepEqual(witness.step_out, expected.map(BigInt)); }); it("all correct for self generated single non zero pt block", async () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/aes-gcm-fold", - template: "AESGCMFOLD", + file: "aes-gcm/aes-gctr-fold", + template: "AESGCTRFOLD", params: [16], // input len is 16 bytes }); - let key = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; let plainText = [0x74, 0x65, 0x73, 0x74, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]; @@ -43,10 +40,9 @@ describe("aes-gcm-fold", () => { let ct = [0x29, 0x29, 0xd2, 0xbb, 0x1a, 0xe9, 0x48, 0x04, 0x40, 0x2b, 0x8e, 0x77, 0x6e, 0x0d, 0x33, 0x56]; const counter = [0x00, 0x00, 0x00, 0x01]; - const foldedBlocks = [0x00]; - const step_in = new Array(32).fill(0x00).concat(counter).concat(foldedBlocks); + const step_in = new Array(32).fill(0x00).concat(counter); - let expected = plainText.concat(ct).concat([0x00, 0x00, 0x00, 0x02]).concat([0x01]); + let expected = plainText.concat(ct).concat([0x00, 0x00, 0x00, 0x02]); const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText, aad: aad, step_in: step_in }, ["step_out"]) assert.deepEqual(witness.step_out, expected.map(BigInt)); @@ -56,8 +52,8 @@ describe("aes-gcm-fold", () => { it("all correct for self generated two block case first fold", async () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/aes-gcm-fold", - template: "AESGCMFOLD", + file: "aes-gcm/aes-gctr-fold", + template: "AESGCTRFOLD", params: [32], // input len is 32 bytes }); @@ -71,9 +67,8 @@ describe("aes-gcm-fold", () => { let ct_part2 = [0x26, 0x75, 0x65, 0x30, 0x71, 0x3e, 0x4c, 0x06, 0x5a, 0xf1, 0xd3, 0xc4, 0xf5, 0x6e, 0x02, 0x04]; const counter = [0x00, 0x00, 0x00, 0x01]; - const foldedBlocks = [0x00]; - const step_in = new Array(64).fill(0x00).concat(counter).concat(foldedBlocks); - let expected = plainText1.concat(zero_block).concat(ct_part1).concat(zero_block).concat([0x00, 0x00, 0x00, 0x02]).concat([0x01]); + const step_in = new Array(64).fill(0x00).concat(counter); + let expected = plainText1.concat(zero_block).concat(ct_part1).concat(zero_block).concat([0x00, 0x00, 0x00, 0x02]); const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText1, aad: aad, step_in: step_in }, ["step_out"]) assert.deepEqual(witness.step_out, expected.map(BigInt)); @@ -81,8 +76,8 @@ describe("aes-gcm-fold", () => { it("all correct for self generated two block case second fold", async () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/aes-gcm-fold", - template: "AESGCMFOLD", + file: "aes-gcm/aes-gctr-fold", + template: "AESGCTRFOLD", params: [32], // input len is 32 bytes }); @@ -96,9 +91,8 @@ describe("aes-gcm-fold", () => { let ct_part2 = [0x26, 0x75, 0x65, 0x30, 0x71, 0x3e, 0x4c, 0x06, 0x5a, 0xf1, 0xd3, 0xc4, 0xf5, 0x6e, 0x02, 0x04]; const counter = [0x00, 0x00, 0x00, 0x02]; - const foldedBlocks = [0x01]; - const step_in = plainText1.concat(zero_block).concat(ct_part1).concat(zero_block).concat(counter).concat(foldedBlocks); - let expected = plainText1.concat(plainText2).concat(ct_part1).concat(ct_part2).concat([0x00, 0x00, 0x00, 0x03]).concat([0x02]); + const step_in = plainText1.concat(zero_block).concat(ct_part1).concat(zero_block).concat(counter); + let expected = plainText1.concat(plainText2).concat(ct_part1).concat(ct_part2).concat([0x00, 0x00, 0x00, 0x03]); const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText2, aad: aad, step_in: step_in }, ["step_out"]) assert.deepEqual(witness.step_out, expected.map(BigInt)); diff --git a/circuits/test/aes/transformations.test.ts b/circuits/test/aes/transformations.test.ts index 70044f4..87ff8d3 100644 --- a/circuits/test/aes/transformations.test.ts +++ b/circuits/test/aes/transformations.test.ts @@ -51,24 +51,6 @@ describe("AES Key Expansion Components", () => { await generatePassCase(10, [0x36, 0x00, 0x00, 0x00]); }); }); - - describe("XorWord", () => { - let circuit: WitnessTester<["bytes1", "bytes2"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`XorWord`, { - file: "aes-gcm/aes/key_expansion", - template: "XorWord", - }); - console.log("XorWord #constraints:", await circuit.getConstraintCount()); - }); - - it("should XOR correctly", async () => { - await circuit.expectPass( - { bytes1: [0x0a, 0x0b, 0x0c, 0x0d], bytes2: [0x01, 0x02, 0x03, 0x04] }, - { out: [0x0b, 0x09, 0x0f, 0x09] } - ); - }); - }); }); describe("XTimes2", () => { diff --git a/circuits/test/aes/utils.test.ts b/circuits/test/aes/utils.test.ts deleted file mode 100644 index bfcbb0b..0000000 --- a/circuits/test/aes/utils.test.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../common"; - - -describe("ToBlocks", () => { - let circuit: WitnessTester<["stream"], ["blocks"]>; - it("should convert stream to block", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "aes-gcm/aes/utils", - template: "ToBlocks", - params: [16], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34], - }, - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x34], - ], - ], - } - ); - }); - it("should pad 1 in block", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "aes-gcm/aes/utils", - template: "ToBlocks", - params: [15], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2], - }, - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - ], - } - ); - }); - it("should pad 0's in block", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "aes-gcm/aes/utils", - template: "ToBlocks", - params: [14], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d], - }, - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0x01], - [0xe0, 0x37, 0x07, 0x00], - ], - ], - } - ); - }); - it("should generate enough blocks", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "aes-gcm/aes/utils", - template: "ToBlocks", - params: [17], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x42, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34, 0x12], - }, - { - blocks: [ - [ - [0x32, 0x42, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x34], - ], - [ - [0x12, 0x00, 0x00, 0x00], - [0x01, 0x00, 0x00, 0x00], - [0x00, 0x00, 0x00, 0x00], - [0x00, 0x00, 0x00, 0x00], - ], - ], - } - ); - }); -}); - - -describe("ToStream", () => { - let circuit: WitnessTester<["blocks"], ["stream"]>; - it("should convert blocks to stream#1", async () => { - circuit = await circomkit.WitnessTester(`ToStream`, { - file: "aes-gcm/aes/utils", - template: "ToStream", - params: [1, 16], - }); - await circuit.expectPass( - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x34], - ], - ], - }, - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34], - } - ); - }); - it("should convert blocks to stream#2", async () => { - circuit = await circomkit.WitnessTester(`ToStream`, { - file: "aes-gcm/aes/utils", - template: "ToStream", - params: [1, 15], - }); - await circuit.expectPass( - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - ], - }, - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2], - } - ); - }); - it("should convert multiple blocks to stream", async () => { - circuit = await circomkit.WitnessTester(`ToStream`, { - file: "aes-gcm/aes/utils", - template: "ToStream", - params: [2, 18], - }); - await circuit.expectPass( - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - ], - }, - { - stream: [ - 0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x01, 0x32, 0x88, - ], - } - ); - }); -}); diff --git a/circuits/test/common/utils.test.ts b/circuits/test/common/utils.test.ts index f651dd4..74eeacb 100644 --- a/circuits/test/common/utils.test.ts +++ b/circuits/test/common/utils.test.ts @@ -180,11 +180,11 @@ describe("selectors", () => { }); }); -describe("toBlocks", () => { - it("test toBlocks", async () => { - let circuit: WitnessTester<["stream"], ["blocks"]>; +describe("ToBlocks", () => { + let circuit: WitnessTester<["stream"], ["blocks"]>; + it("should convert stream to block", async () => { circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "aes-gcm/aes/utils", + file: "aes-gcm/utils", template: "ToBlocks", params: [16], }); @@ -201,34 +201,157 @@ describe("toBlocks", () => { [0xe0, 0x37, 0x07, 0x34], ], ], - } + } + ); + }); + it("should pad 1 in block", async () => { + circuit = await circomkit.WitnessTester(`ToBlocks`, { + file: "aes-gcm/utils", + template: "ToBlocks", + params: [15], + }); + await circuit.expectPass( + { + stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2], + }, + { + blocks: [ + [ + [0x32, 0x43, 0xf6, 0xa8], + [0x88, 0x5a, 0x30, 0x8d], + [0x31, 0x31, 0x98, 0xa2], + [0xe0, 0x37, 0x07, 0x01], + ], + ], + } + ); + }); + it("should pad 0's in block", async () => { + circuit = await circomkit.WitnessTester(`ToBlocks`, { + file: "aes-gcm/utils", + template: "ToBlocks", + params: [14], + }); + await circuit.expectPass( + { + stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d], + }, + { + blocks: [ + [ + [0x32, 0x43, 0xf6, 0xa8], + [0x88, 0x5a, 0x30, 0x8d], + [0x31, 0x31, 0x98, 0x01], + [0xe0, 0x37, 0x07, 0x00], + ], + ], + } + ); + }); + it("should generate enough blocks", async () => { + circuit = await circomkit.WitnessTester(`ToBlocks`, { + file: "aes-gcm/utils", + template: "ToBlocks", + params: [17], + }); + await circuit.expectPass( + { + stream: [0x32, 0x88, 0x31, 0xe0, 0x42, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34, 0x12], + }, + { + blocks: [ + [ + [0x32, 0x42, 0xf6, 0xa8], + [0x88, 0x5a, 0x30, 0x8d], + [0x31, 0x31, 0x98, 0xa2], + [0xe0, 0x37, 0x07, 0x34], + ], + [ + [0x12, 0x00, 0x00, 0x00], + [0x01, 0x00, 0x00, 0x00], + [0x00, 0x00, 0x00, 0x00], + [0x00, 0x00, 0x00, 0x00], + ], + ], + } ); }); }); -describe("toBlocksRowWise", () => { - it("test toBlocksRowWise", async () => { - let circuit: WitnessTester<["stream"], ["blocks"]>; - circuit = await circomkit.WitnessTester(`ToBlocksRowWise`, { - file: "aes-gcm/aes/utils", - template: "ToBlocksRowWise", - params: [16], - }); - let expected = [ - [ - [0x31, 0x31, 0x31, 0x31], - [0x31, 0x31, 0x31, 0x31], - [0x31, 0x31, 0x31, 0x31], - [0x00, 0x00, 0x00, 0x01], - ], - ]; +describe("ToStream", () => { + let circuit: WitnessTester<["blocks"], ["stream"]>; + it("should convert blocks to stream#1", async () => { + circuit = await circomkit.WitnessTester(`ToStream`, { + file: "aes-gcm/utils", + template: "ToStream", + params: [1, 16], + }); await circuit.expectPass( { - stream: [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x00, 0x00, 0x00, 0x01], + blocks: [ + [ + [0x32, 0x43, 0xf6, 0xa8], + [0x88, 0x5a, 0x30, 0x8d], + [0x31, 0x31, 0x98, 0xa2], + [0xe0, 0x37, 0x07, 0x34], + ], + ], }, { - blocks: expected + stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34], + } + ); + }); + it("should convert blocks to stream#2", async () => { + circuit = await circomkit.WitnessTester(`ToStream`, { + file: "aes-gcm/utils", + template: "ToStream", + params: [1, 15], + }); + await circuit.expectPass( + { + blocks: [ + [ + [0x32, 0x43, 0xf6, 0xa8], + [0x88, 0x5a, 0x30, 0x8d], + [0x31, 0x31, 0x98, 0xa2], + [0xe0, 0x37, 0x07, 0x01], + ], + ], + }, + { + stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2], + } + ); + }); + it("should convert multiple blocks to stream", async () => { + circuit = await circomkit.WitnessTester(`ToStream`, { + file: "aes-gcm/utils", + template: "ToStream", + params: [2, 18], + }); + await circuit.expectPass( + { + blocks: [ + [ + [0x32, 0x43, 0xf6, 0xa8], + [0x88, 0x5a, 0x30, 0x8d], + [0x31, 0x31, 0x98, 0xa2], + [0xe0, 0x37, 0x07, 0x01], + ], + [ + [0x32, 0x43, 0xf6, 0xa8], + [0x88, 0x5a, 0x30, 0x8d], + [0x31, 0x31, 0x98, 0xa2], + [0xe0, 0x37, 0x07, 0x01], + ], + ], + }, + { + stream: [ + 0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x01, 0x32, 0x88, + ], } ); });