mirror of
https://github.com/tlsnotary/authdecode.git
synced 2026-01-10 07:18:06 -05:00
Split out R_binary and subslice revelation
This commit is contained in:
436
src/lib.rs
436
src/lib.rs
@@ -1,23 +1,17 @@
|
||||
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 num_traits::identities::Zero;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use ark_poly::polynomial::univariate::DensePolynomial;
|
||||
use ark_poly_commit::PolynomialCommitment;
|
||||
|
||||
mod r_binary;
|
||||
mod r_decode;
|
||||
pub mod subslice_revelation;
|
||||
|
||||
const PT_LABEL: &str = "PT";
|
||||
const QUOTIENT_LABEL: &str = "q";
|
||||
const BINARY_CHAL_LABEL: &str = "binchal";
|
||||
// Label for referring to the plaintext polynomial in the AUTHDECODE protocol
|
||||
pub(crate) const PT_LABEL: &str = "PT";
|
||||
|
||||
pub struct Plaintext<F: FftField> {
|
||||
polyn: DensePolynomial<F>,
|
||||
bytes: Vec<u8>,
|
||||
pub polyn: DensePolynomial<F>,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct PtCom<F, PC>
|
||||
@@ -28,417 +22,3 @@ where
|
||||
com: PC::Commitment,
|
||||
r: PC::Randomness,
|
||||
}
|
||||
|
||||
impl<F: FftField> Plaintext<F> {
|
||||
pub fn commit<PC, R>(&self, mut rng: R, ck: &PC::CommitterKey) -> PtCom<F, PC>
|
||||
where
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
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(crate) struct BinarynessProof<F, PC>
|
||||
where
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
{
|
||||
/// The proof of correct evaluation of `pt_chal_eval` and `quotient_chal_eval`
|
||||
eval_proof: PC::BatchProof,
|
||||
/// The point `q(c)` for some challenge point `c`, where `q` is the "quotient polynomial"
|
||||
/// define in the protocol
|
||||
quotient_com: LabeledCommitment<PC::Commitment>,
|
||||
/// The point `p(c)` for some challenge point `c`
|
||||
pt_chal_eval: F,
|
||||
/// The point `q(c)` for some challenge point `c`
|
||||
quotient_chal_eval: F,
|
||||
}
|
||||
|
||||
/// Proves that the given plaintext `pt_polyn` has only binary coefficients. `chal` is the
|
||||
/// challenge point given by the verifier.
|
||||
pub(crate) fn prove_binary<D, F, PC, R>(
|
||||
mut rng: R,
|
||||
domain: D,
|
||||
ck: &PC::CommitterKey,
|
||||
chal: F,
|
||||
pt_polyn: &DensePolynomial<F>,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
) -> BinarynessProof<F, PC>
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
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();
|
||||
// Sanity check: make sure z is divisible by the vanishing polynoimal
|
||||
assert!(_r.is_zero());
|
||||
|
||||
// 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::from(1337u16);
|
||||
|
||||
let pc = pt_polyn.evaluate(&c);
|
||||
let qc = q.evaluate(&c);
|
||||
|
||||
// Sanity check: make sure qc * vc == zc
|
||||
assert_eq!(qc * domain.evaluate_vanishing_polynomial(c), z.evaluate(&c));
|
||||
|
||||
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<F> = [
|
||||
(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];
|
||||
|
||||
let batch_proof = PC::batch_open(
|
||||
&ck,
|
||||
&[pt_labeled_polyn, labeled_q],
|
||||
&[pt_labeled_com, q_com.clone()],
|
||||
&query_set,
|
||||
chal,
|
||||
rands,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
BinarynessProof {
|
||||
eval_proof: batch_proof,
|
||||
quotient_com: q_com,
|
||||
pt_chal_eval: pc,
|
||||
quotient_chal_eval: qc,
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that the plaintext committed to by `pt_com` has only binary coefficients. `chal` is
|
||||
/// the challenge point given by the verifier. `q`, `pc`, and `qc` are all given by the prover.
|
||||
pub(crate) fn verif_binary<D, F, PC, R>(
|
||||
mut rng: R,
|
||||
domain: D,
|
||||
deg: usize,
|
||||
vk: &PC::VerifierKey,
|
||||
chal: F,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
proof: &BinarynessProof<F, PC>,
|
||||
) -> bool
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
// Destructure the proof
|
||||
let BinarynessProof {
|
||||
eval_proof,
|
||||
quotient_com,
|
||||
pt_chal_eval,
|
||||
quotient_chal_eval,
|
||||
} = proof;
|
||||
|
||||
// TODO: Make this a a real challenge point
|
||||
let c = F::from(1337u16);
|
||||
|
||||
// Wrap the commitment
|
||||
let pt_labeled_com = LabeledCommitment::new(PT_LABEL.to_string(), pt_com.com.clone(), None);
|
||||
|
||||
// A map telling the verifier to verifier the values of p(c) and q(c)
|
||||
let query_set: QuerySet<F> = [
|
||||
(PT_LABEL.to_string(), (BINARY_CHAL_LABEL.to_string(), c)),
|
||||
(
|
||||
QUOTIENT_LABEL.to_string(),
|
||||
(BINARY_CHAL_LABEL.to_string(), c),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// A map from the challenge point c to the evaluated points p(c) and q(c)
|
||||
let evals: BTreeMap<(String, F), F> = [
|
||||
((PT_LABEL.to_string(), c), *pt_chal_eval),
|
||||
((QUOTIENT_LABEL.to_string(), c), *quotient_chal_eval),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// Check that pc and qc are the correct evaluations
|
||||
let mut res = PC::batch_check(
|
||||
&vk,
|
||||
&[pt_labeled_com, quotient_com.clone()],
|
||||
&query_set,
|
||||
&evals,
|
||||
eval_proof,
|
||||
chal,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Make the polynomial 1ⁿ = (1,1, ..., 1)
|
||||
let ones = vec![F::one(); deg];
|
||||
let ones_polyn = Evaluations::from_vec_and_domain(ones, domain).interpolate();
|
||||
let oc = ones_polyn.evaluate(&c);
|
||||
|
||||
let vc = domain.evaluate_vanishing_polynomial(c);
|
||||
|
||||
// Check that vc · qc == pc · (pc - 1c). This proves that p is binary
|
||||
res &= vc * quotient_chal_eval == *pt_chal_eval * (*pt_chal_eval - oc);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Proves the value of `pt[i]` for every `i` in `slice_idxs`
|
||||
pub fn prove_subslice<D, F, PC>(
|
||||
dom: D,
|
||||
ck: &PC::CommitterKey,
|
||||
chal: F,
|
||||
pt: &Plaintext<F>,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
slice_idxs: RangeInclusive<usize>,
|
||||
) -> PC::BatchProof
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
{
|
||||
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<F> = 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();
|
||||
|
||||
// Prove the value of `pt` at all the indices in the query set
|
||||
PC::batch_open(
|
||||
&ck,
|
||||
&[labeled_polyn],
|
||||
&[labeled_com],
|
||||
&query_set,
|
||||
chal,
|
||||
&[rand],
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Verifies `pt[i] == subslice_vals[i- l]` for all `i` in `slice_idxs`, and where `l =
|
||||
/// slice_idxs.len()`.
|
||||
pub fn verify_subslice_proof<D, F, PC, R>(
|
||||
mut rng: R,
|
||||
dom: D,
|
||||
vk: &PC::VerifierKey,
|
||||
proof: &PC::BatchProof,
|
||||
chal: F,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
subslice_vals: Vec<F>,
|
||||
slice_idxs: RangeInclusive<usize>,
|
||||
) -> bool
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
// Wrap the commitment
|
||||
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<F> = 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();
|
||||
|
||||
// Check the batch opening proof
|
||||
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_poly_commit::marlin::marlin_pc::MarlinKZG10;
|
||||
|
||||
// Some alternate polynomial commitment schemes
|
||||
//use ark_vesta::{Affine as G, Fr as F};
|
||||
//type PC = SonicKZG10<E, DensePolynomial<F>>;
|
||||
type PC = MarlinKZG10<E, DensePolynomial<F>>;
|
||||
//type PC = InnerProductArgPC<G, blake2::Blake2s, DensePolynomial<F>>;
|
||||
|
||||
// Tests the R_binary relation
|
||||
#[test]
|
||||
fn test_prove_binary() {
|
||||
let log_pt_bitlen = 15;
|
||||
let pt_bitlen = 1 << log_pt_bitlen;
|
||||
|
||||
// Set up the RNG, the polynomial evaluation domain, and the SRS for commitments
|
||||
let mut rng = test_rng();
|
||||
let domain = D::new(pt_bitlen).unwrap();
|
||||
let universal_params = PC::setup(pt_bitlen * 4, None, &mut rng).unwrap();
|
||||
let (ck, vk) = PC::trim(&universal_params, pt_bitlen, 2, None).unwrap();
|
||||
|
||||
// Make a random plaintext and turn it into a polynomial
|
||||
let pt_bits: Vec<bool> = core::iter::repeat_with(|| rng.gen::<bool>())
|
||||
.take(pt_bitlen)
|
||||
.collect();
|
||||
let pt_polyn = Evaluations::from_vec_and_domain(
|
||||
pt_bits.clone().into_iter().map(F::from).collect(),
|
||||
domain,
|
||||
)
|
||||
.interpolate();
|
||||
|
||||
// Commit to the polynomial
|
||||
let pt_com = {
|
||||
let labeled_polyn =
|
||||
LabeledPolynomial::new(PT_LABEL.to_string(), pt_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(),
|
||||
}
|
||||
};
|
||||
|
||||
// Sample a random challenge point
|
||||
let chal = F::rand(&mut rng);
|
||||
|
||||
// Compute an proof of binaryness and time it
|
||||
let start = std::time::Instant::now();
|
||||
let binaryness_proof =
|
||||
prove_binary::<_, _, PC, _>(&mut rng, domain, &ck, chal, &pt_polyn, &pt_com);
|
||||
let proof_time = start.elapsed().as_millis();
|
||||
println!("Proving R_binary on 2^{log_pt_bitlen} bits: {proof_time}ms");
|
||||
|
||||
// Verify the binaryness proof
|
||||
assert!(verif_binary(
|
||||
&mut rng,
|
||||
domain,
|
||||
pt_bitlen,
|
||||
&vk,
|
||||
chal,
|
||||
&pt_com,
|
||||
&binaryness_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<u8> = core::iter::repeat_with(|| rng.gen::<u8>())
|
||||
.take(pt_bytelen)
|
||||
.collect();
|
||||
let pt_vals: Vec<F> = 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<F, PC> = 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 proof_time = start.elapsed().as_millis();
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let verif = verify_subslice_proof(
|
||||
&mut rng,
|
||||
domain,
|
||||
&vk,
|
||||
&proof,
|
||||
chal,
|
||||
&pt_com,
|
||||
pt_vals.clone(),
|
||||
slice.clone(),
|
||||
);
|
||||
let verif_time = start.elapsed().as_millis();
|
||||
assert!(verif);
|
||||
|
||||
println!(
|
||||
"Opening {slice_size}/2^{log_pt_bytelen} elems: \
|
||||
{proof_time}ms / {verif_time}ms (proof/verif)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
261
src/r_binary.rs
Normal file
261
src/r_binary.rs
Normal file
@@ -0,0 +1,261 @@
|
||||
use crate::{PtCom, PT_LABEL};
|
||||
|
||||
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 num_traits::identities::Zero;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
// Labels for referring to polynomials and scalars in the R_binary protocol
|
||||
const QUOTIENT_LABEL: &str = "q";
|
||||
const BINARY_CHAL_LABEL: &str = "binchal";
|
||||
|
||||
pub(crate) struct BinarynessProof<F, PC>
|
||||
where
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
{
|
||||
/// The proof of correct evaluation of `pt_chal_eval` and `quotient_chal_eval`
|
||||
eval_proof: PC::BatchProof,
|
||||
/// The point `q(c)` for some challenge point `c`, where `q` is the "quotient polynomial"
|
||||
/// define in the protocol
|
||||
quotient_com: LabeledCommitment<PC::Commitment>,
|
||||
/// The point `p(c)` for some challenge point `c`
|
||||
pt_chal_eval: F,
|
||||
/// The point `q(c)` for some challenge point `c`
|
||||
quotient_chal_eval: F,
|
||||
}
|
||||
|
||||
/// Proves that the given plaintext `pt_polyn` has only binary coefficients. `chal` is the
|
||||
/// challenge point given by the verifier.
|
||||
pub(crate) fn prove_binary<D, F, PC, R>(
|
||||
mut rng: R,
|
||||
domain: D,
|
||||
ck: &PC::CommitterKey,
|
||||
chal: F,
|
||||
pt_polyn: &DensePolynomial<F>,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
) -> BinarynessProof<F, PC>
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
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();
|
||||
// Sanity check: make sure z is divisible by the vanishing polynoimal
|
||||
assert!(_r.is_zero());
|
||||
|
||||
// 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::from(1337u16);
|
||||
|
||||
let pc = pt_polyn.evaluate(&c);
|
||||
let qc = q.evaluate(&c);
|
||||
|
||||
// Sanity check: make sure qc * vc == zc
|
||||
assert_eq!(qc * domain.evaluate_vanishing_polynomial(c), z.evaluate(&c));
|
||||
|
||||
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<F> = [
|
||||
(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];
|
||||
|
||||
let batch_proof = PC::batch_open(
|
||||
&ck,
|
||||
&[pt_labeled_polyn, labeled_q],
|
||||
&[pt_labeled_com, q_com.clone()],
|
||||
&query_set,
|
||||
chal,
|
||||
rands,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
BinarynessProof {
|
||||
eval_proof: batch_proof,
|
||||
quotient_com: q_com,
|
||||
pt_chal_eval: pc,
|
||||
quotient_chal_eval: qc,
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that the plaintext committed to by `pt_com` has only binary coefficients. `chal` is
|
||||
/// the challenge point given by the verifier. `q`, `pc`, and `qc` are all given by the prover.
|
||||
pub(crate) fn verif_binary<D, F, PC, R>(
|
||||
mut rng: R,
|
||||
domain: D,
|
||||
deg: usize,
|
||||
vk: &PC::VerifierKey,
|
||||
chal: F,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
proof: &BinarynessProof<F, PC>,
|
||||
) -> bool
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
// Destructure the proof
|
||||
let BinarynessProof {
|
||||
eval_proof,
|
||||
quotient_com,
|
||||
pt_chal_eval,
|
||||
quotient_chal_eval,
|
||||
} = proof;
|
||||
|
||||
// TODO: Make this a a real challenge point
|
||||
let c = F::from(1337u16);
|
||||
|
||||
// Wrap the commitment
|
||||
let pt_labeled_com = LabeledCommitment::new(PT_LABEL.to_string(), pt_com.com.clone(), None);
|
||||
|
||||
// A map telling the verifier to verifier the values of p(c) and q(c)
|
||||
let query_set: QuerySet<F> = [
|
||||
(PT_LABEL.to_string(), (BINARY_CHAL_LABEL.to_string(), c)),
|
||||
(
|
||||
QUOTIENT_LABEL.to_string(),
|
||||
(BINARY_CHAL_LABEL.to_string(), c),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// A map from the challenge point c to the evaluated points p(c) and q(c)
|
||||
let evals: BTreeMap<(String, F), F> = [
|
||||
((PT_LABEL.to_string(), c), *pt_chal_eval),
|
||||
((QUOTIENT_LABEL.to_string(), c), *quotient_chal_eval),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
// Check that pc and qc are the correct evaluations
|
||||
let mut res = PC::batch_check(
|
||||
&vk,
|
||||
&[pt_labeled_com, quotient_com.clone()],
|
||||
&query_set,
|
||||
&evals,
|
||||
eval_proof,
|
||||
chal,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Make the polynomial 1ⁿ = (1,1, ..., 1)
|
||||
let ones = vec![F::one(); deg];
|
||||
let ones_polyn = Evaluations::from_vec_and_domain(ones, domain).interpolate();
|
||||
let oc = ones_polyn.evaluate(&c);
|
||||
|
||||
let vc = domain.evaluate_vanishing_polynomial(c);
|
||||
|
||||
// Check that vc · qc == pc · (pc - 1c). This proves that p is binary
|
||||
res &= vc * quotient_chal_eval == *pt_chal_eval * (*pt_chal_eval - oc);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[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_poly_commit::marlin::marlin_pc::MarlinKZG10;
|
||||
|
||||
// Some alternate polynomial commitment schemes
|
||||
//use ark_vesta::{Affine as G, Fr as F};
|
||||
//type PC = SonicKZG10<E, DensePolynomial<F>>;
|
||||
type PC = MarlinKZG10<E, DensePolynomial<F>>;
|
||||
//type PC = InnerProductArgPC<G, blake2::Blake2s, DensePolynomial<F>>;
|
||||
|
||||
// Tests the R_binary relation
|
||||
#[test]
|
||||
fn test_prove_binary() {
|
||||
let log_pt_bitlen = 15;
|
||||
let pt_bitlen = 1 << log_pt_bitlen;
|
||||
|
||||
// Set up the RNG, the polynomial evaluation domain, and the SRS for commitments
|
||||
let mut rng = test_rng();
|
||||
let domain = D::new(pt_bitlen).unwrap();
|
||||
let universal_params = PC::setup(pt_bitlen * 4, None, &mut rng).unwrap();
|
||||
let (ck, vk) = PC::trim(&universal_params, pt_bitlen, 2, None).unwrap();
|
||||
|
||||
// Make a random plaintext and turn it into a polynomial
|
||||
let pt_bits: Vec<bool> = core::iter::repeat_with(|| rng.gen::<bool>())
|
||||
.take(pt_bitlen)
|
||||
.collect();
|
||||
let pt_polyn = Evaluations::from_vec_and_domain(
|
||||
pt_bits.clone().into_iter().map(F::from).collect(),
|
||||
domain,
|
||||
)
|
||||
.interpolate();
|
||||
|
||||
// Commit to the polynomial
|
||||
let pt_com = {
|
||||
let labeled_polyn =
|
||||
LabeledPolynomial::new(PT_LABEL.to_string(), pt_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(),
|
||||
}
|
||||
};
|
||||
|
||||
// Sample a random challenge point
|
||||
let chal = F::rand(&mut rng);
|
||||
|
||||
// Compute an proof of binaryness and time it
|
||||
let start = std::time::Instant::now();
|
||||
let binaryness_proof =
|
||||
prove_binary::<_, _, PC, _>(&mut rng, domain, &ck, chal, &pt_polyn, &pt_com);
|
||||
let proof_time = start.elapsed().as_millis();
|
||||
println!("Proving R_binary on 2^{log_pt_bitlen} bits: {proof_time}ms");
|
||||
|
||||
// Verify the binaryness proof
|
||||
assert!(verif_binary(
|
||||
&mut rng,
|
||||
domain,
|
||||
pt_bitlen,
|
||||
&vk,
|
||||
chal,
|
||||
&pt_com,
|
||||
&binaryness_proof,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
use crate::{prove_binary, BinarynessProof, PtCom};
|
||||
use crate::{
|
||||
r_binary::{prove_binary, BinarynessProof},
|
||||
PtCom,
|
||||
};
|
||||
|
||||
use ark_ff::FftField;
|
||||
use ark_poly::{polynomial::univariate::DensePolynomial, EvaluationDomain, Polynomial};
|
||||
use ark_poly_commit::{LabeledCommitment, LabeledPolynomial, PolynomialCommitment};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
// String to identify the zero-labels polynomial
|
||||
const ZEROLABELS_LABEL: &str = "W₀";
|
||||
|
||||
pub struct ActiveLabelsCom<F, PC>
|
||||
@@ -21,8 +25,11 @@ where
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
{
|
||||
/// The point `W₀(c)` for some challenge point `c`, where W₀ is the zero-labels polyn
|
||||
zero_labels_eval: F,
|
||||
/// The proof that `W₀(c)` was evaluated correctly
|
||||
zero_labels_eval_proof: PC::Proof,
|
||||
/// We call down to R_binary in R_decode. This is that proof
|
||||
binaryness_proof: BinarynessProof<F, PC>,
|
||||
}
|
||||
|
||||
|
||||
202
src/subslice_revelation.rs
Normal file
202
src/subslice_revelation.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
use crate::PtCom;
|
||||
|
||||
use core::ops::RangeInclusive;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ark_ff::FftField;
|
||||
use ark_poly::{polynomial::univariate::DensePolynomial, EvaluationDomain};
|
||||
use ark_poly_commit::{LabeledCommitment, LabeledPolynomial, PolynomialCommitment, QuerySet};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
const PT_LABEL: &str = "PT";
|
||||
|
||||
pub struct Plaintext<F: FftField> {
|
||||
polyn: DensePolynomial<F>,
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<F: FftField> Plaintext<F> {
|
||||
pub fn commit<PC, R>(&self, mut rng: R, ck: &PC::CommitterKey) -> PtCom<F, PC>
|
||||
where
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Proves the value of `pt[i]` for every `i` in `slice_idxs`
|
||||
pub fn prove_subslice<D, F, PC>(
|
||||
dom: D,
|
||||
ck: &PC::CommitterKey,
|
||||
chal: F,
|
||||
pt: &Plaintext<F>,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
slice_idxs: RangeInclusive<usize>,
|
||||
) -> PC::BatchProof
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
{
|
||||
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<F> = 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();
|
||||
|
||||
// Prove the value of `pt` at all the indices in the query set
|
||||
PC::batch_open(
|
||||
&ck,
|
||||
&[labeled_polyn],
|
||||
&[labeled_com],
|
||||
&query_set,
|
||||
chal,
|
||||
&[rand],
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Verifies `pt[i] == subslice_vals[i- l]` for all `i` in `slice_idxs`, and where `l =
|
||||
/// slice_idxs.len()`.
|
||||
pub fn verify_subslice_proof<D, F, PC, R>(
|
||||
mut rng: R,
|
||||
dom: D,
|
||||
vk: &PC::VerifierKey,
|
||||
proof: &PC::BatchProof,
|
||||
chal: F,
|
||||
pt_com: &PtCom<F, PC>,
|
||||
subslice_vals: Vec<F>,
|
||||
slice_idxs: RangeInclusive<usize>,
|
||||
) -> bool
|
||||
where
|
||||
D: EvaluationDomain<F>,
|
||||
F: FftField,
|
||||
PC: PolynomialCommitment<F, DensePolynomial<F>>,
|
||||
R: CryptoRng + RngCore,
|
||||
{
|
||||
// Wrap the commitment
|
||||
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<F> = 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();
|
||||
|
||||
// Check the batch opening proof
|
||||
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, Polynomial, Radix2EvaluationDomain as D};
|
||||
use ark_std::test_rng;
|
||||
use rand::Rng;
|
||||
|
||||
use ark_bls12_381::{Bls12_381 as E, Fr as F};
|
||||
use ark_poly_commit::marlin::marlin_pc::MarlinKZG10;
|
||||
|
||||
// Some alternate polynomial commitment schemes
|
||||
//use ark_vesta::{Affine as G, Fr as F};
|
||||
//type PC = SonicKZG10<E, DensePolynomial<F>>;
|
||||
type PC = MarlinKZG10<E, DensePolynomial<F>>;
|
||||
//type PC = InnerProductArgPC<G, blake2::Blake2s, DensePolynomial<F>>;
|
||||
|
||||
#[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<u8> = core::iter::repeat_with(|| rng.gen::<u8>())
|
||||
.take(pt_bytelen)
|
||||
.collect();
|
||||
let pt_vals: Vec<F> = 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<F, PC> = 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 proof_time = start.elapsed().as_millis();
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let verif = verify_subslice_proof(
|
||||
&mut rng,
|
||||
domain,
|
||||
&vk,
|
||||
&proof,
|
||||
chal,
|
||||
&pt_com,
|
||||
pt_vals.clone(),
|
||||
slice.clone(),
|
||||
);
|
||||
let verif_time = start.elapsed().as_millis();
|
||||
assert!(verif);
|
||||
|
||||
println!(
|
||||
"Opening {slice_size}/2^{log_pt_bytelen} elems: \
|
||||
{proof_time}ms / {verif_time}ms (proof/verif)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user