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(crate) const TWO128: Fq =
42 QuinticExtension::<F>([F(F::ORDER - 1), F(F::ORDER - 1), F::ZERO, F::ZERO, F::ZERO]);
43
44pub fn encrypt(ks: Point, msg: &[Fq], nonce: [F; 2]) -> Vec<Fq> {
45 let mut m = msg.to_vec(); // pad m
46 m.resize(m.len().next_multiple_of(3), Fq::ZERO);
47 assert!(m.len() < F::ORDER as usize);
48
49 let l = Fq::from(F::from_canonical_u64(msg.len() as u64));
50 let nonce_5: [F; 5] = [nonce[0], nonce[1], F::ZERO, F::ZERO, F::ZERO];
51 let n = Fq::from_basefield_array(nonce_5);
52 let nl128: Fq = n + l * TWO128;
53 let mut s: [Fq; 4] = [Fq::ZERO, ks.x, ks.u, nl128];
54
55 let mut ct: Vec<Fq> = vec![Fq::ZERO; m.len() + 1];
56 for i in 0..m.len() / 3 {
57 s = hash_state(s);
58
59 // absorb
60 s[1] += m[i * 3];
61 s[2] += m[i * 3 + 1];
62 s[3] += m[i * 3 + 2];
63
64 // release
65 ct[i * 3] = s[1];
66 ct[i * 3 + 1] = s[2];
67 ct[i * 3 + 2] = s[3];
68 }
69 s = hash_state(s);
70 ct[m.len()] = s[1];
71 ct
72}
73
74pub fn decrypt(ks: Point, ct: &[Fq], nonce: [F; 2], l: usize) -> Vec<Fq> {
75 let l_fq = Fq::from(F::from_canonical_u64(l as u64));
76 let nonce_5: [F; 5] = [nonce[0], nonce[1], F::ZERO, F::ZERO, F::ZERO];
77 let n = Fq::from_basefield_array(nonce_5);
78 let nl128: Fq = n + l_fq * TWO128;
79 let mut s: [Fq; 4] = [Fq::ZERO, ks.x, ks.u, nl128];
80
81 let mut m: Vec<Fq> = vec![Fq::ZERO; ct.len() - 1];
82 for i in 0..ct.len() / 3 {
83 s = hash_state(s);
84
85 // release
86 m[3 * i] = ct[3 * i] - s[1];
87 m[3 * i + 1] = ct[3 * i + 1] - s[2];
88 m[3 * i + 2] = ct[3 * i + 2] - s[3];
89
90 // modify state
91 s[1] = ct[3 * i];
92 s[2] = ct[3 * i + 1];
93 s[3] = ct[3 * i + 2];
94 }
95
96 if l > 3 {
97 if l % 3 == 2 {
98 assert_eq!(m[m.len() - 1], Fq::ZERO);
99 } else if l % 3 == 1 {
100 assert_eq!(m[m.len() - 1], Fq::ZERO);
101 assert_eq!(m[m.len() - 2], Fq::ZERO);
102 }
103 }
104 s = hash_state(s);
105 assert_eq!(ct[ct.len() - 1], s[1]);
106 m[0..l].to_vec()
107}
108
109fn hash_state(s: [Fq; 4]) -> [Fq; 4] {
110 let elems: [F; 4 * 5] = array::from_fn(|i| s[i / 5].0[i % 5]);
111 // Note: here we're using plonky2's normal poseidon, not duplex-sponge
112 let h = hash_n_to_m_no_pad::<F, PoseidonPermutation<_>>(&elems, 4 * 5);
113 array::from_fn(|i| Fq::from_basefield_array(array::from_fn::<_, 5, _>(|j| h[j + 5 * i])))
114}
115
116#[cfg(test)]
117mod tests {
118 use plonky2::field::types::Sample;
119
120 use super::*;
121
122 #[test]
123 fn test_encrypt_decrypt() {
124 test_encrypt_decrypt_op(9);
125 test_encrypt_decrypt_op(10);
126 test_encrypt_decrypt_op(11);
127 test_encrypt_decrypt_op(12);
128 test_encrypt_decrypt_op(128);
129 test_encrypt_decrypt_op(129);
130 test_encrypt_decrypt_op(1024);
131 test_encrypt_decrypt_op(1023);
132 test_encrypt_decrypt_op(1025);
133 }
134 fn test_encrypt_decrypt_op(msg_len: usize) {
135 let mut rng = rand::thread_rng();
136
137 let (_k, K) = new_key();
138 let ks = expanded_key(K);
139 let nonce: [F; 2] = [F::rand(), F::rand()];
140 let msg: Vec<Fq> = (0..msg_len).map(|_| Fq::sample(&mut rng)).collect();
141 let l = msg.len();
142
143 let ct = encrypt(ks, &msg, nonce);
144
145 let m = decrypt(ks, &ct, nonce, l);
146 assert_eq!(m, msg);
147 assert_ne!(&msg, &ct[..msg_len]);
148 }
149}