wip
arnaucube git@arnaucube.com
Fri, 20 Feb 2026 17:53:05 +0000
3 files changed,
81 insertions(+),
42 deletions(-)
M
poseidon-cipher/Cargo.toml
→
poseidon-cipher/Cargo.toml
@@ -4,10 +4,8 @@ version = "0.1.0"
edition = "2024" [dependencies] -plonky2 = { git = "https://github.com/0xPARC/plonky2.git", rev = "109d517d09c210ae4c2cee381d3e3fbc04aa3812" } -anyhow = "1.0.56" -# plonky2 = { workspace = true } -# anyhow = { workspace = true } +plonky2 = { workspace = true } +anyhow = { workspace = true } num = { version = "0.4.3", features = ["num-bigint"] } pod2 = { git = "https://github.com/0xPARC/pod2", rev = "cdf227e3530247e07e375daed007cae7d443b4ae" } # for ecgfp5 num-bigint = { version = "0.4.6", features = ["rand"] }
M
poseidon-cipher/src/circuit.rs
→
poseidon-cipher/src/circuit.rs
@@ -1,13 +1,16 @@
//! For LICENSE check out https://github.com/0xPARC/plonky2-crypto-gadgets/blob/main/LICENSE +//! +//! Implements the Plonky2 circuits for Poseidon encryption as described at +//! https://drive.google.com/file/d/1EVrP3DzoGbmzkRmYnyEDcIQcXVU7GlOd/view . use std::array; use anyhow::Result; use plonky2::{ field::{ - extension::{FieldExtension, quintic::QuinticExtension}, + extension::{quintic::QuinticExtension, FieldExtension}, goldilocks_field::GoldilocksField as F, - types::Field, + types::{Field, Field64}, }, hash::poseidon::PoseidonHash, iop::{@@ -24,7 +27,7 @@ field::{CircuitBuilderNNF, OEFTarget},
}, }; -use crate::{Fq, two_128}; +use crate::{Fq, TWO128}; // Fq Target type FqT = OEFTarget<5, QuinticExtension<F>>;@@ -35,7 +38,7 @@ [(); L + 1]:,
{ ks: PointTarget, m: [FqT; L], - nonce: Target, + nonce: [Target; 2], ct: [FqT; L + 1], }@@ -45,25 +48,25 @@ [(); L + 1]:,
{ pub fn build(builder: &mut CircuitBuilder<F, D>) -> Self { assert!(L.is_multiple_of(3)); + assert!(L < F::ORDER as usize); let fqt_zero = const_fqt_zero(builder); // add targets let ks: PointTarget = builder.add_virtual_point_target(); let m: [FqT; L] = array::from_fn(|_| builder.add_virtual_nnf_target()); - let nonce: Target = builder.add_virtual_target(); + let nonce: [Target; 2] = builder.add_virtual_target_arr::<2>(); let mut ct: [FqT; L + 1] = array::from_fn(|_| fqt_zero.clone()); // build the circuit logic let f_zero = builder.constant(F::ZERO); - let n = FqT::new([nonce, f_zero, f_zero, f_zero, f_zero]); - let two128: Fq = two_128(); + let n = FqT::new([nonce[0], nonce[1], f_zero, f_zero, f_zero]); let l_two128: Fq = Fq::from_basefield_array([ F::from_canonical_u64(L as u64), F::ZERO, F::ZERO, F::ZERO, F::ZERO, - ]) * two128; + ]) * TWO128; let l_two128_target: FqT = const_fqt_from_fq(builder, l_two128); let nl128: FqT = builder.nnf_add(&n, &l_two128_target); let fq_zero: FqT = FqT::new([f_zero, f_zero, f_zero, f_zero, f_zero]);@@ -93,7 +96,7 @@ &self,
pw: &mut PartialWitness<F>, ks: Point, m: &[Fq], - nonce: F, + nonce: [F; 2], ct: &[Fq], ) -> Result<()> { assert_eq!(m.len(), L);@@ -106,7 +109,7 @@ for j in 0..5 {
pw.set_target(self.m[i].components[j], m[i].0[j])?; } } - pw.set_target(self.nonce, nonce)?; + pw.set_target_arr(&self.nonce, &nonce)?; #[allow(clippy::needless_range_loop)] for i in 0..L + 1 { for j in 0..5 {@@ -169,7 +172,7 @@ const L: usize = 129; // (multiple of 3)
let (_k, K) = crate::new_key(); let ks = crate::expanded_key(K); - let nonce = F::rand(); + let nonce: [F; 2] = [F::rand(), F::rand()]; let msg: Vec<Fq> = (0..L).map(|_| Fq::sample(&mut rng)).collect(); let l = msg.len();
M
poseidon-cipher/src/lib.rs
→
poseidon-cipher/src/lib.rs
@@ -1,4 +1,7 @@
//! For LICENSE check out https://github.com/0xPARC/plonky2-crypto-gadgets/blob/main/LICENSE +//! +//! Implements Poseidon encryption as described at +//! https://drive.google.com/file/d/1EVrP3DzoGbmzkRmYnyEDcIQcXVU7GlOd/view . #![allow(incomplete_features)] #![feature(generic_const_exprs)]@@ -11,13 +14,13 @@ use num::bigint::BigUint;
use num_bigint::RandBigInt; use plonky2::{ field::{ - extension::{FieldExtension, quintic::QuinticExtension}, + extension::{quintic::QuinticExtension, FieldExtension}, goldilocks_field::GoldilocksField as F, - types::Field, + types::{Field, Field64}, }, hash::{hashing::hash_n_to_hash_no_pad, poseidon::PoseidonPermutation}, }; -use pod2::backends::plonky2::primitives::ec::curve::{GROUP_ORDER, Point}; +use pod2::backends::plonky2::primitives::ec::curve::{Point, GROUP_ORDER}; use rand::rngs::OsRng; pub mod circuit;@@ -35,27 +38,53 @@ let r: BigUint = OsRng.gen_biguint_below(&GROUP_ORDER);
&r * K } -pub(crate) fn two_128() -> Fq { - // WIP TODO - let two32 = Fq::from(F::from_canonical_u64(2_u64.pow(32))); - // dbg!(2_u64.pow(32)); - // dbg!(F::from_canonical_u64(2_u64.pow(32))); - // dbg!(&two32); - let two64: Fq = two32 * two32; - // dbg!(&two64); - let two128: Fq = two64 * two64; - // dbg!(&two128); - two128 +pub(crate) const TWO128: Fq = + QuinticExtension::<F>([F(F::ORDER - 1), F(F::ORDER - 1), F::ZERO, F::ZERO, F::ZERO]); + +// /// 2^128 +// pub(crate) fn two_128() -> Fq { +// // WIP TODO +// let two32 = Fq::from(F::from_canonical_u64(2_u64.pow(32))); +// let two64: Fq = two32 * two32; +// let two128: Fq = two64 * two64; +// // note: two64 gets two32-1 +// // dbg!(2_u64.pow(32)); +// // dbg!(F::from_canonical_u64(2_u64.pow(32))); +// // dbg!(&two32); +// // dbg!(&two64); +// // dbg!(&two128); +// two128 +// } + +pub fn f_vec_to_fq_vec(f: &[F]) -> Vec<Fq> { + f.chunks(5) + .map(|c| { + let mut e: [F; 5] = [F::ZERO; 5]; + e[0..c.len()].copy_from_slice(c); + Fq::from_basefield_array(e) + }) + .collect() } -pub fn encrypt(ks: Point, msg: &[Fq], nonce: F) -> Vec<Fq> { - // pad m - let mut m = msg.to_vec(); +pub fn encrypt_f(ks: Point, msg_f: &[F], nonce: [F; 2]) -> Vec<F> { + let l_f = msg_f.len(); + dbg!(msg_f.len()); + dbg!(&msg_f); + let msg: Vec<Fq> = f_vec_to_fq_vec(msg_f); + dbg!(msg.len()); + dbg!(&msg); + let ct = encrypt(ks, &msg, nonce); + ct.into_iter().flat_map(|ct_i| ct_i.0).collect() +} +pub fn encrypt(ks: Point, msg: &[Fq], nonce: [F; 2]) -> Vec<Fq> { + let mut m = msg.to_vec(); // pad m m.resize(m.len().next_multiple_of(3), Fq::ZERO); + assert!(m.len() < F::ORDER as usize); let l = Fq::from(F::from_canonical_u64(msg.len() as u64)); - let n = Fq::from(nonce); - let nl128: Fq = n + l * two_128(); + let nonce_5: [F; 5] = [nonce[0], nonce[1], F::ZERO, F::ZERO, F::ZERO]; + let n = Fq::from_basefield_array(nonce_5); + let nl128: Fq = n + l * TWO128; let mut s: [Fq; 4] = [Fq::ZERO, ks.x, ks.u, nl128]; let mut ct: Vec<Fq> = vec![Fq::ZERO; m.len() + 1];@@ -77,10 +106,16 @@ ct[m.len()] = s[1];
ct } -pub fn decrypt(ks: Point, ct: &[Fq], nonce: F, l: usize) -> Vec<Fq> { +pub fn decrypt_f(ks: Point, ct_f: &[F], nonce: [F; 2], l: usize) -> Vec<F> { + let ct: Vec<Fq> = f_vec_to_fq_vec(ct_f); + let m = decrypt(ks, &ct, nonce, l); + m.to_vec().into_iter().flat_map(|e| e.0).collect() +} +pub fn decrypt(ks: Point, ct: &[Fq], nonce: [F; 2], l: usize) -> Vec<Fq> { let l_fq = Fq::from(F::from_canonical_u64(l as u64)); - let n = Fq::from(nonce); - let nl128: Fq = n + l_fq * two_128(); + let nonce_5: [F; 5] = [nonce[0], nonce[1], F::ZERO, F::ZERO, F::ZERO]; + let n = Fq::from_basefield_array(nonce_5); + let nl128: Fq = n + l_fq * TWO128; let mut s: [Fq; 4] = [Fq::ZERO, ks.x, ks.u, nl128]; let mut m: Vec<Fq> = vec![Fq::ZERO; ct.len() - 1];@@ -113,6 +148,7 @@ }
fn hash_state(s: [Fq; 4]) -> [Fq; 4] { let elems: [F; 4 * 5] = array::from_fn(|i| s[i / 5].0[i % 5]); + // Note: here we're using plonky2's normal poseidon, not duplex-sponge let h = hash_n_to_hash_no_pad::<F, PoseidonPermutation<_>>(&elems).elements; let mut h5: [F; 5] = [F::ZERO; 5]; h5[..4].copy_from_slice(&h);@@ -142,13 +178,15 @@ let mut rng = rand::thread_rng();
let (_k, K) = new_key(); let ks = expanded_key(K); - let nonce = F::rand(); - let msg: Vec<Fq> = (0..msg_len).map(|_| Fq::sample(&mut rng)).collect(); - let l = msg.len(); + let nonce: [F; 2] = [F::rand(), F::rand()]; + // let msg: Vec<Fq> = (0..msg_len).map(|_| Fq::sample(&mut rng)).collect(); + let msg: Vec<F> = (0..msg_len).map(|_| F::sample(&mut rng)).collect(); + let l = msg.len().div_ceil(5); + dbg!(msg.len(), l); - let ct = encrypt(ks, &msg, nonce); + let ct = encrypt_f(ks, &msg, nonce); - let m = decrypt(ks, &ct, nonce, l); + let m = decrypt_f(ks, &ct, nonce, l); assert_eq!(m, msg); } }