commit b1d1eb8cdecb657a3e37c25acba10f31123bc129 Author: Michael Rosenberg Date: Thu Aug 11 03:09:51 2022 -0400 Initial commit; subslice proofs passing tests diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..57ff472 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "authdecode" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ark-ff = "0.3" +ark-poly = "0.3" +ark-poly-commit = "0.3" +commitment_dlog = { git = "https://github.com/o1-labs/proof-systems.git", rev = "c803ba53108b6081e920022a547f3069e07f7dc3" } +rand_core = "0.6" + +[dev-dependencies] +ark-bls12-381 = "0.3" +ark-ec = "0.3" +ark-std = "0.3" +ark-vesta = "0.3" +blake2 = "0.9" +rand = "0.8" + +[features] +default = ["parallel", "rayon"] +parallel = ["ark-ff/parallel", "ark-poly/parallel", "ark-poly-commit/parallel"] +rayon = ["ark-ff/rayon", "ark-poly/rayon", "ark-poly-commit/rayon"] +std = ["ark-ff/std", "ark-poly/std", "ark-poly-commit/std"] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..af61fd7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,303 @@ +use core::ops::RangeInclusive; +use std::collections::BTreeMap; + +use ark_ff::FftField; +use ark_poly::{ + polynomial::univariate::DensePolynomial, EvaluationDomain, Evaluations, Polynomial, +}; +use ark_poly_commit::{LabeledCommitment, LabeledPolynomial, PolynomialCommitment, QuerySet}; +use rand_core::{CryptoRng, RngCore}; + +const PT_LABEL: &str = "PT"; +const QUOTIENT_LABEL: &str = "q"; +const BINARY_CHAL_LABEL: &str = "binchal"; + +pub struct Plaintext { + polyn: DensePolynomial, + bytes: Vec, +} + +pub struct PtCom +where + F: FftField, + PC: PolynomialCommitment>, +{ + com: PC::Commitment, + r: PC::Randomness, +} + +impl Plaintext { + pub fn commit(&self, mut rng: R, ck: &PC::CommitterKey) -> PtCom + where + PC: PolynomialCommitment>, + R: RngCore + CryptoRng, + { + let labeled_polyn = + LabeledPolynomial::new(PT_LABEL.to_string(), self.polyn.clone(), None, None); + let (coms, rands) = PC::commit(ck, &[labeled_polyn], Some(&mut rng)).unwrap(); + PtCom { + com: coms[0].commitment().clone(), + r: rands[0].clone(), + } + } +} + +pub fn prove_binary( + mut rng: R, + domain: D, + ck: &PC::CommitterKey, + chal: F, + pt: &Plaintext, + pt_com: &PtCom, +) -> PC::BatchProof +where + D: EvaluationDomain, + F: FftField, + PC: PolynomialCommitment>, + R: RngCore + CryptoRng, +{ + // Make the polynomial 1ⁿ = (1,1, ..., 1) + let ones = vec![F::one(); pt.polyn.coeffs.len()]; + let ones_polyn = Evaluations::from_vec_and_domain(ones, domain).interpolate(); + + // Construct p-1ⁿ + let pt_minus_ones = &pt.polyn - &ones_polyn; + + // Multiply: z = p·(p-1ⁿ) + let z = &pt.polyn * &pt_minus_ones; + // Compute and commit to q = z / v where v is the polyn that vanishes on the support of `p` + let (q, _r) = z.divide_by_vanishing_poly(domain).unwrap(); + + // Commit to q + let labeled_q = LabeledPolynomial::new(QUOTIENT_LABEL.to_string(), q.clone(), None, None); + let (q_coms, q_rands) = PC::commit(ck, &[labeled_q.clone()], Some(&mut rng)).unwrap(); + let q_com = q_coms[0].clone(); + let q_rand = q_rands[0].clone(); + + // TODO: Make this a a real challenge point + let c = F::rand(&mut rng); + + let pt_labeled_polyn = + LabeledPolynomial::new(PT_LABEL.to_string(), pt.polyn.clone(), None, None); + let pt_labeled_com = LabeledCommitment::new(PT_LABEL.to_string(), pt_com.com.clone(), None); + + // A map telling the prover to prove the values of p(c) and q(c) + let query_set: QuerySet = [ + (PT_LABEL.to_string(), (BINARY_CHAL_LABEL.to_string(), c)), + ( + QUOTIENT_LABEL.to_string(), + (BINARY_CHAL_LABEL.to_string(), c), + ), + ] + .into_iter() + .collect(); + + // The openings to the commitments to p and q + let rands = &[pt_com.r.clone(), q_rand]; + + PC::batch_open( + &ck, + &[pt_labeled_polyn, labeled_q], + &[pt_labeled_com, q_com], + &query_set, + chal, + rands, + None, + ) + .unwrap() +} + +pub fn prove_subslice( + dom: D, + ck: &PC::CommitterKey, + chal: F, + pt: &Plaintext, + pt_com: &PtCom, + slice_idxs: RangeInclusive, +) -> PC::BatchProof +where + D: EvaluationDomain, + F: FftField, + PC: PolynomialCommitment>, +{ + let labeled_polyn = LabeledPolynomial::new(PT_LABEL.to_string(), pt.polyn.clone(), None, None); + let labeled_com = LabeledCommitment::new(PT_LABEL.to_string(), pt_com.com.clone(), None); + + // A map of "p" -> ("i", ωⁱ). This tells the prover that query i is the i-th byte in the + // plaintext + let query_set: QuerySet = slice_idxs + .clone() + .map(|i| (PT_LABEL.to_string(), (format!("{i}"), dom.element(i)))) + .collect(); + + // The randomness associated with the pt commitment + let rand = pt_com.r.clone(); + + PC::batch_open( + &ck, + &[labeled_polyn], + &[labeled_com], + &query_set, + chal, + &[rand], + None, + ) + .unwrap() +} + +pub fn check_subslice_proof( + mut rng: R, + dom: D, + vk: &PC::VerifierKey, + proof: &PC::BatchProof, + chal: F, + pt_com: &PtCom, + subslice_vals: Vec, + slice_idxs: RangeInclusive, +) -> bool +where + D: EvaluationDomain, + F: FftField, + PC: PolynomialCommitment>, + R: CryptoRng + RngCore, +{ + let labeled_com = LabeledCommitment::new(PT_LABEL.to_string(), pt_com.com.clone(), None); + + // A map of "p" -> ("i", ωⁱ). This tells the verifier that query i is the i-th byte in the + // plaintext + let query_set: QuerySet = slice_idxs + .clone() + .map(|i| (PT_LABEL.to_string(), (format!("{i}"), dom.element(i)))) + .collect(); + + // A map of (label, "i") -> pt[i]. This tells the verifier the value of the i-th plaintext byte + let evals: BTreeMap<(String, F), F> = slice_idxs + .clone() + .map(|i| (PT_LABEL.to_string(), dom.element(i))) + .zip(subslice_vals.into_iter()) + .collect(); + + PC::batch_check( + &vk, + &[labeled_com], + &query_set, + &evals, + &proof, + chal, + &mut rng, + ) + .unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_ff::UniformRand; + use ark_poly::{EvaluationDomain, Evaluations, Radix2EvaluationDomain as D}; + use ark_std::test_rng; + use rand::Rng; + + use ark_bls12_381::{Bls12_381 as E, Fr as F}; + use ark_ec::PairingEngine; + use ark_poly_commit::{ + ipa_pc::InnerProductArgPC, marlin::marlin_pc::MarlinKZG10, sonic_pc::SonicKZG10, + }; + //use ark_vesta::{Affine as G, Fr as F}; + //type PC = SonicKZG10>; + type PC = MarlinKZG10>; + //type PC = InnerProductArgPC>; + + #[test] + fn test_prove_binary() { + let pt_bytelen = 1 << 15; + + // Set up the RNG, the polynomial evaluation domain, and the SRS for commitments + let mut rng = test_rng(); + let domain = D::new(pt_bytelen).unwrap(); + let universal_params = PC::setup(pt_bytelen * 4, None, &mut rng).unwrap(); + let (ck, vk) = PC::trim(&universal_params, pt_bytelen, 2, None).unwrap(); + + // Make a random plaintext and commit to it + let pt_bytes: Vec = core::iter::repeat_with(|| rng.gen::()) + .take(pt_bytelen) + .collect(); + let pt_polyn = Evaluations::from_vec_and_domain( + pt_bytes.clone().into_iter().map(F::from).collect(), + domain, + ) + .interpolate(); + let pt = Plaintext { + polyn: pt_polyn, + bytes: pt_bytes, + }; + let pt_com: PtCom = pt.commit(&mut rng, &ck); + + let chal = F::rand(&mut rng); + + prove_binary(&mut rng, domain, &ck, chal, &pt, &pt_com); + + // TODO: Check binary proof + } + + #[test] + fn test_open_subslice() { + for log_pt_bytelen in 10..16 { + let pt_bytelen = 1 << log_pt_bytelen; + + // Set up the RNG, the polynomial evaluation domain, and the SRS for commitments + let mut rng = test_rng(); + let domain = D::new(pt_bytelen).unwrap(); + let universal_params = PC::setup(pt_bytelen * 4, None, &mut rng).unwrap(); + let (ck, vk) = PC::trim(&universal_params, pt_bytelen, 2, None).unwrap(); + + // Make a random plaintext and commit to it + let pt_bytes: Vec = core::iter::repeat_with(|| rng.gen::()) + .take(pt_bytelen) + .collect(); + let pt_vals: Vec = pt_bytes.iter().cloned().map(F::from).collect(); + let pt_polyn = Evaluations::from_vec_and_domain( + pt_bytes.clone().into_iter().map(F::from).collect(), + domain, + ) + .interpolate(); + + // Sanity check: make sure pt_vals is actually pt_polyn(i) for all i + pt_vals + .iter() + .enumerate() + .for_each(|(i, v)| assert_eq!(*v, pt_polyn.evaluate(&domain.element(i)))); + + let pt = Plaintext { + polyn: pt_polyn, + bytes: pt_bytes, + }; + let pt_com: PtCom = pt.commit(&mut rng, &ck); + + for slice_size in 1..16 { + let chal = F::rand(&mut rng); + let slice = 0..=(slice_size - 1); + + let start = std::time::Instant::now(); + let proof = prove_subslice(domain, &ck, chal, &pt, &pt_com, slice.clone()); + let diff = start.elapsed(); + + println!( + "Proof for opening {slice_size}/2^{log_pt_bytelen} elems: {}ms", + diff.as_millis() + ); + + let verif = check_subslice_proof( + &mut rng, + domain, + &vk, + &proof, + chal, + &pt_com, + pt_vals.clone(), + slice.clone(), + ); + assert!(verif); + } + } + } +}