arnaucube
arnaucube/plonky2-crypto-gadgetsPublic Self-hosted repository
ghash & GF(2^128) mul circuits done
arnaucube git@arnaucube.com
Fri, 13 Feb 2026 12:45:50 +0000
commit

bf8f5dd77ea85e34d14bc18386768f178154f214

parent

5a6d5ddf1dc58b33f589ec237f06ae993f64f6b4

2 files changed, 89 insertions(+), 11 deletions(-)

jump to
M src/circuit_gcm.rs → src/circuit_gcm.rs

@@ -83,16 +83,14 @@ builder: &mut CircuitBuilder<F, D>,

x: [ByteTarget; 16], y: [ByteTarget; 16], ) -> [ByteTarget; 16] { - // WIP TODO - let zero_byte = builder.zero_byte(); let one_bool = builder._true(); - // R: 11100001 || 0^120 + // R: 10000111 || 0^120 (in little-endian) let mut r = [zero_byte; 16]; r[0][0] = one_bool; - r[0][1] = one_bool; - r[0][2] = one_bool; + r[0][5] = one_bool; + r[0][6] = one_bool; r[0][7] = one_bool; let mut z = [zero_byte; 16];

@@ -100,7 +98,6 @@ let mut v = y;

for i in 0..128 { let byte_index = i / 8; let bit_index = 7 - (i % 8); - // dbg!(byte_index, bit_index); let xi = x[byte_index][bit_index]; // set z = if xi==1: z^v, else: z

@@ -112,7 +109,7 @@ BoolTarget::new_unsafe(builder.select(xi, z_xor_v.target, z[b][k].target));

} } - let lsb = v[15][7].clone(); + let lsb = v[15][0].clone(); // (little-endian) v = right_shift_one_target(builder, &v); // if lsb==1: v=v^R, else: v

@@ -347,10 +344,10 @@ #[test]

fn test_gf_mul() -> Result<()> { // AES-GCM-128 test_gf_mul_opt::<4, 4, 10, 16>()?; - // test_gf_mul_opt::<4, 4, 10, 32>()?; - // - // // AES-GCM-256 - // test_gf_mul_opt::<8, 4, 14, 16>()?; + test_gf_mul_opt::<4, 4, 10, 32>()?; + + // AES-GCM-256 + test_gf_mul_opt::<8, 4, 14, 16>()?; Ok(()) }

@@ -364,6 +361,16 @@ let x: [u8; 16] = [111; 16];

let y: [u8; 16] = [222; 16]; let expected = gf_2_128_mul(x, y); + let x_bits: [[bool; 8]; 16] = + array::from_fn(|b| crate::circuit_aes::le_bits_from_byte(x[b])); + let y_bits: [[bool; 8]; 16] = + array::from_fn(|b| crate::circuit_aes::le_bits_from_byte(y[b])); + + // sanity check + let expected2 = crate::native_gcm::gf_2_128_mul_circuit_version(x_bits, y_bits); + let expected_bits: [[bool; 8]; 16] = + array::from_fn(|b| crate::circuit_aes::le_bits_from_byte(expected[b])); + assert_eq!(expected2, expected_bits); // Circuit declaration let config = CircuitConfig::standard_recursion_config();
M src/native_gcm.rs → src/native_gcm.rs

@@ -139,21 +139,92 @@

let mut z = [0u8; 16]; let mut v = y; for i in 0..128 { + dbg!(i); // xi: i-th bit of x let byte_index = i / 8; let bit_index = 7 - (i % 8); let xi = (x[byte_index] >> bit_index) & 1; + // dbg!(z, v); if xi == 1 { z = xor_blocks(z, v); } + // dbg!(&z); let lsb = v[15] & 1; right_shift_one(&mut v); + dbg!(lsb == 1); if lsb == 1 { v = xor_blocks(v, R); } } z +} + +// auxiliary version for testing the circuit logic +pub(crate) fn gf_2_128_mul_circuit_version( + x: [[bool; 8]; 16], + y: [[bool; 8]; 16], +) -> [[bool; 8]; 16] { + let zero_byte = [false; 8]; + + // R: 10000111 || 0^120 (in little-endian) + let mut r = [zero_byte; 16]; + r[0][0] = true; + r[0][5] = true; + r[0][6] = true; + r[0][7] = true; + + let mut z = [zero_byte; 16]; + let mut v = y; + for i in 0..128 { + dbg!(i); + let byte_index = i / 8; + let bit_index = 7 - (i % 8); + let xi = x[byte_index][bit_index]; + + // set z = if xi==1: z^v, else: z + for b in 0..16 { + for k in 0..8 { + let z_xor_v = z[b][k] ^ v[b][k]; + z[b][k] = if xi { z_xor_v } else { z[b][k] }; + } + } + + // let lsb = v[15][7].clone(); + let lsb = v[15][0].clone(); // (little-endian) + v = right_shift_one_circuit_version(&v); + + // if lsb==1: v=v^R, else: v + for b in 0..16 { + for k in 0..8 { + let v_xor_r = v[b][k] ^ r[b][k]; + v[b][k] = if lsb { v_xor_r } else { v[b][k] }; + } + } + } + z +} +// method for debugging +fn bools_to_u8_le(bits: [bool; 8]) -> u8 { + bits.iter() + .enumerate() + .fold(0u8, |acc, (i, &bit)| if bit { acc | (1 << i) } else { acc }) +} +fn right_shift_one_circuit_version(v: &[[bool; 8]; 16]) -> [[bool; 8]; 16] { + let mut r: [[bool; 8]; 16] = v.clone(); + let mut carry = false; + for i in 0..16 { + let current = v[i]; + let next_carry = current[0]; + let mut shifted = [false; 8]; + for j in 0..7 { + shifted[j] = current[j + 1]; + } + shifted[7] = carry; + r[i] = shifted; + carry = next_carry; + } + r } pub(crate) fn right_shift_one(block: &mut [u8; 16]) {