#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::expression::Expression;
use crate::field::{Element, Field};
use crate::gadget_builder::GadgetBuilder;
use crate::gadget_traits::{BlockCipher, Permutation};
use crate::lcg::LCG;
use crate::MonomialPermutation;
pub struct MiMCBlockCipher<F: Field> {
round_constants: Vec<Element<F>>,
round_permutation: MonomialPermutation<F>,
}
impl<F: Field> MiMCBlockCipher<F> {
fn new(round_constants: &[Element<F>]) -> Self {
let round_permutation = MonomialPermutation::new(Element::from(3u8));
let round_constants = round_constants.to_vec();
MiMCBlockCipher { round_permutation, round_constants }
}
}
impl<F: Field> Default for MiMCBlockCipher<F> {
fn default() -> Self {
let mut round_constants = Vec::new();
let mut lcg = LCG::new();
for _r in 0..mimc_recommended_rounds::<F>() {
round_constants.push(lcg.next_element());
}
MiMCBlockCipher::new(&round_constants)
}
}
impl<F: Field> BlockCipher<F> for MiMCBlockCipher<F> {
fn encrypt(&self, builder: &mut GadgetBuilder<F>, key: &Expression<F>, input: &Expression<F>)
-> Expression<F> {
let mut current = input.clone();
current += key;
current = self.round_permutation.permute(builder, ¤t);
for round_constant in self.round_constants.iter() {
current += key + Expression::from(round_constant);
current = self.round_permutation.permute(builder, ¤t);
}
current + key
}
fn decrypt(&self, builder: &mut GadgetBuilder<F>, key: &Expression<F>, output: &Expression<F>)
-> Expression<F> {
let mut current = output.clone();
current -= key;
for round_constant in self.round_constants.iter().rev() {
current = self.round_permutation.inverse(builder, ¤t);
current -= key + Expression::from(round_constant);
}
current = self.round_permutation.inverse(builder, ¤t);
current - key
}
}
pub struct MiMCPermutation<F: Field> {
cipher: MiMCBlockCipher<F>
}
impl<F: Field> Permutation<F> for MiMCPermutation<F> {
fn permute(&self, builder: &mut GadgetBuilder<F>, x: &Expression<F>) -> Expression<F> {
self.cipher.encrypt(builder, &Expression::zero(), x)
}
fn inverse(&self, builder: &mut GadgetBuilder<F>, x: &Expression<F>) -> Expression<F> {
self.cipher.decrypt(builder, &Expression::zero(), x)
}
}
fn mimc_recommended_rounds<F: Field>() -> usize {
let n = Element::<F>::max_bits();
(n as f64 / 3f64.log2()).ceil() as usize
}
#[cfg(test)]
mod tests {
use crate::expression::Expression;
use crate::field::Element;
use crate::gadget_builder::GadgetBuilder;
use crate::gadget_traits::BlockCipher;
use crate::mimc::MiMCBlockCipher;
use crate::test_util::{F11, F7};
#[test]
fn mimc_encrypt_and_decrypt() {
let mut builder = GadgetBuilder::<F11>::new();
let key_wire = builder.wire();
let input_wire = builder.wire();
let key = Expression::from(key_wire);
let input = Expression::from(input_wire);
let mimc = MiMCBlockCipher::default();
let encrypted = mimc.encrypt(&mut builder, &key, &input);
let decrypted = mimc.decrypt(&mut builder, &key, &encrypted);
let gadget = builder.build();
let mut values = values!(key_wire => 2u8.into(), input_wire => 3u8.into());
assert!(gadget.execute(&mut values));
assert_eq!(input.evaluate(&values), decrypted.evaluate(&values));
}
#[test]
fn mimc_f11() {
let constants = &[Element::from(5u8), Element::from(7u8)];
let mut builder = GadgetBuilder::<F11>::new();
let key_wire = builder.wire();
let input_wire = builder.wire();
let key = Expression::from(key_wire);
let input = Expression::from(input_wire);
let mimc = MiMCBlockCipher::new(constants);
let mimc_output = mimc.encrypt(&mut builder, &key, &input);
let gadget = builder.build();
let mut values = values!(key_wire => 3u8.into(), input_wire => 6u8.into());
assert!(gadget.execute(&mut values));
assert_eq!(Element::from(2u8), mimc_output.evaluate(&values));
}
#[test]
#[should_panic]
fn mimc_f7_incompatible() {
MiMCBlockCipher::<F7>::default();
}
}