arnaucube
arnaucube/plonky2-crypto-gadgetsPublic Self-hosted repository
plonky2-crypto-gadgets/poseidon-cipher/src/lib.rs
poseidon-cipher/src/lib.rsRaw
  1//! For LICENSE check out https://github.com/0xPARC/plonky2-crypto-gadgets/blob/main/LICENSE
  2//!
  3//! Implements Poseidon encryption as described at
  4//! https://drive.google.com/file/d/1EVrP3DzoGbmzkRmYnyEDcIQcXVU7GlOd/view .
  5
  6#![allow(incomplete_features)]
  7#![feature(generic_const_exprs)]
  8#![allow(dead_code)] // TMP
  9#![allow(non_snake_case)]
 10
 11use std::array;
 12
 13use num::bigint::BigUint;
 14use num_bigint::RandBigInt;
 15use plonky2::{
 16    field::{
 17        extension::{FieldExtension, quintic::QuinticExtension},
 18        goldilocks_field::GoldilocksField as F,
 19        types::{Field, Field64},
 20    },
 21    hash::{hashing::hash_n_to_m_no_pad, poseidon::PoseidonPermutation},
 22};
 23use pod2::backends::plonky2::primitives::ec::curve::{GROUP_ORDER, Point};
 24use rand::rngs::OsRng;
 25
 26pub mod circuit;
 27
 28// ECField
 29type Fq = QuinticExtension<F>;
 30
 31pub fn new_key() -> (BigUint, Point) {
 32    let k: BigUint = OsRng.gen_biguint_below(&GROUP_ORDER);
 33    let K = &k * Point::generator();
 34    (k, K)
 35}
 36pub fn expanded_key(K: Point) -> Point {
 37    let r: BigUint = OsRng.gen_biguint_below(&GROUP_ORDER);
 38    &r * K
 39}
 40
 41pub fn encrypt(ks: Point, msg: &[Fq], nonce: [F; 2]) -> Vec<Fq> {
 42    let mut m = msg.to_vec(); // pad m
 43    m.resize(m.len().next_multiple_of(3), Fq::ZERO);
 44    assert!(m.len() < F::ORDER as usize);
 45
 46    // bind the msg length and nonce into the initial state
 47    let n_l = Fq::from_basefield_array([
 48        nonce[0],
 49        nonce[1],
 50        F::from_canonical_u64(msg.len() as u64),
 51        F::ZERO,
 52        F::ZERO,
 53    ]);
 54    let mut s: [Fq; 4] = [Fq::ZERO, ks.x, ks.u, n_l];
 55
 56    let mut ct: Vec<Fq> = vec![Fq::ZERO; m.len() + 1];
 57    for i in 0..m.len() / 3 {
 58        s = hash_state(s);
 59
 60        // absorb
 61        s[1] += m[i * 3];
 62        s[2] += m[i * 3 + 1];
 63        s[3] += m[i * 3 + 2];
 64
 65        // release
 66        ct[i * 3] = s[1];
 67        ct[i * 3 + 1] = s[2];
 68        ct[i * 3 + 2] = s[3];
 69    }
 70    s = hash_state(s);
 71    ct[m.len()] = s[1];
 72    ct
 73}
 74
 75pub fn decrypt(ks: Point, ct: &[Fq], nonce: [F; 2], l: usize) -> Vec<Fq> {
 76    let n_l = Fq::from_basefield_array([
 77        nonce[0],
 78        nonce[1],
 79        F::from_canonical_u64(l as u64),
 80        F::ZERO,
 81        F::ZERO,
 82    ]);
 83    let mut s: [Fq; 4] = [Fq::ZERO, ks.x, ks.u, n_l];
 84
 85    let mut m: Vec<Fq> = vec![Fq::ZERO; ct.len() - 1];
 86    for i in 0..ct.len() / 3 {
 87        s = hash_state(s);
 88
 89        // release
 90        m[3 * i] = ct[3 * i] - s[1];
 91        m[3 * i + 1] = ct[3 * i + 1] - s[2];
 92        m[3 * i + 2] = ct[3 * i + 2] - s[3];
 93
 94        // modify state
 95        s[1] = ct[3 * i];
 96        s[2] = ct[3 * i + 1];
 97        s[3] = ct[3 * i + 2];
 98    }
 99
100    if l > 3 {
101        if l % 3 == 2 {
102            assert_eq!(m[m.len() - 1], Fq::ZERO);
103        } else if l % 3 == 1 {
104            assert_eq!(m[m.len() - 1], Fq::ZERO);
105            assert_eq!(m[m.len() - 2], Fq::ZERO);
106        }
107    }
108    s = hash_state(s);
109    assert_eq!(ct[ct.len() - 1], s[1]);
110    m[0..l].to_vec()
111}
112
113fn hash_state(s: [Fq; 4]) -> [Fq; 4] {
114    let elems: [F; 4 * 5] = array::from_fn(|i| s[i / 5].0[i % 5]);
115    // Note: here we're using plonky2's normal poseidon, not duplex-sponge
116    let h = hash_n_to_m_no_pad::<F, PoseidonPermutation<_>>(&elems, 4 * 5);
117    array::from_fn(|i| Fq::from_basefield_array(array::from_fn::<_, 5, _>(|j| h[j + 5 * i])))
118}
119
120#[cfg(test)]
121mod tests {
122    use plonky2::field::types::Sample;
123
124    use super::*;
125
126    #[test]
127    fn test_encrypt_decrypt() {
128        test_encrypt_decrypt_op(9);
129        test_encrypt_decrypt_op(10);
130        test_encrypt_decrypt_op(11);
131        test_encrypt_decrypt_op(12);
132        test_encrypt_decrypt_op(128);
133        test_encrypt_decrypt_op(129);
134        test_encrypt_decrypt_op(1024);
135        test_encrypt_decrypt_op(1023);
136        test_encrypt_decrypt_op(1025);
137    }
138    fn test_encrypt_decrypt_op(msg_len: usize) {
139        let mut rng = rand::thread_rng();
140
141        let (_k, K) = new_key();
142        let ks = expanded_key(K);
143        let nonce: [F; 2] = [F::rand(), F::rand()];
144        let msg: Vec<Fq> = (0..msg_len).map(|_| Fq::sample(&mut rng)).collect();
145        let l = msg.len();
146
147        let ct = encrypt(ks, &msg, nonce);
148
149        let m = decrypt(ks, &ct, nonce, l);
150        assert_eq!(m, msg);
151        assert_ne!(&msg, &ct[..msg_len]);
152    }
153}