You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

750 lines
19 KiB

use super::bullet::BulletReductionProof;
use super::commitments::{Commitments, MultiCommitGens};
use super::errors::ProofVerifyError;
use super::group::CompressedGroup;
use super::math::Math;
use super::scalar::Scalar;
use super::transcript::{AppendToTranscript, ProofTranscript};
use merlin::Transcript;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct KnowledgeProof {
alpha: CompressedGroup,
z1: Scalar,
z2: Scalar,
}
impl KnowledgeProof {
fn protocol_name() -> &'static [u8] {
b"knowledge proof"
}
pub fn prove(
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
random_tape: &mut Transcript,
x: &Scalar,
r: &Scalar,
) -> (KnowledgeProof, CompressedGroup) {
transcript.append_protocol_name(KnowledgeProof::protocol_name());
// produce two random Scalars
let t1 = random_tape.challenge_scalar(b"t1");
let t2 = random_tape.challenge_scalar(b"t2");
let C = x.commit(&r, gens_n).compress();
C.append_to_transcript(b"C", transcript);
let alpha = t1.commit(&t2, gens_n).compress();
alpha.append_to_transcript(b"alpha", transcript);
let c = transcript.challenge_scalar(b"c");
let z1 = x * &c + &t1;
let z2 = r * &c + &t2;
(KnowledgeProof { alpha, z1, z2 }, C)
}
pub fn verify(
&self,
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
C: &CompressedGroup,
) -> Result<(), ProofVerifyError> {
transcript.append_protocol_name(KnowledgeProof::protocol_name());
C.append_to_transcript(b"C", transcript);
self.alpha.append_to_transcript(b"alpha", transcript);
let c = transcript.challenge_scalar(b"c");
let lhs = self.z1.commit(&self.z2, gens_n).compress();
let rhs = (&c * C.decompress().expect("Could not decompress C")
+ self
.alpha
.decompress()
.expect("Could not decompress self.alpha"))
.compress();
if lhs == rhs {
Ok(())
} else {
Err(ProofVerifyError)
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EqualityProof {
alpha: CompressedGroup,
z: Scalar,
}
impl EqualityProof {
fn protocol_name() -> &'static [u8] {
b"equality proof"
}
pub fn prove(
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
random_tape: &mut Transcript,
v1: &Scalar,
s1: &Scalar,
v2: &Scalar,
s2: &Scalar,
) -> (EqualityProof, CompressedGroup, CompressedGroup) {
transcript.append_protocol_name(EqualityProof::protocol_name());
// produce a random Scalar
let r = random_tape.challenge_scalar(b"r");
let C1 = v1.commit(&s1, gens_n).compress();
C1.append_to_transcript(b"C1", transcript);
let C2 = v2.commit(&s2, gens_n).compress();
C2.append_to_transcript(b"C2", transcript);
let alpha = (&r * gens_n.h).compress();
alpha.append_to_transcript(b"alpha", transcript);
let c = transcript.challenge_scalar(b"c");
let z = &c * (s1 - s2) + &r;
(EqualityProof { alpha, z }, C1, C2)
}
pub fn verify(
&self,
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
C1: &CompressedGroup,
C2: &CompressedGroup,
) -> Result<(), ProofVerifyError> {
transcript.append_protocol_name(EqualityProof::protocol_name());
C1.append_to_transcript(b"C1", transcript);
C2.append_to_transcript(b"C2", transcript);
self.alpha.append_to_transcript(b"alpha", transcript);
let c = transcript.challenge_scalar(b"c");
let rhs = {
let C = &C1.decompress().unwrap() - &C2.decompress().unwrap();
(&c * C + &self.alpha.decompress().unwrap()).compress()
};
let lhs = (&self.z * gens_n.h).compress();
if lhs == rhs {
Ok(())
} else {
Err(ProofVerifyError)
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ProductProof {
alpha: CompressedGroup,
beta: CompressedGroup,
delta: CompressedGroup,
z: [Scalar; 5],
}
impl ProductProof {
fn protocol_name() -> &'static [u8] {
b"product proof"
}
pub fn prove(
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
random_tape: &mut Transcript,
x: &Scalar,
rX: &Scalar,
y: &Scalar,
rY: &Scalar,
z: &Scalar,
rZ: &Scalar,
) -> (
ProductProof,
CompressedGroup,
CompressedGroup,
CompressedGroup,
) {
transcript.append_protocol_name(ProductProof::protocol_name());
// produce five random Scalar
let b1 = random_tape.challenge_scalar(b"b1");
let b2 = random_tape.challenge_scalar(b"b2");
let b3 = random_tape.challenge_scalar(b"b3");
let b4 = random_tape.challenge_scalar(b"b4");
let b5 = random_tape.challenge_scalar(b"b5");
let X = x.commit(&rX, gens_n).compress();
X.append_to_transcript(b"X", transcript);
let Y = y.commit(&rY, gens_n).compress();
Y.append_to_transcript(b"Y", transcript);
let Z = z.commit(&rZ, gens_n).compress();
Z.append_to_transcript(b"Z", transcript);
let alpha = b1.commit(&b2, gens_n).compress();
alpha.append_to_transcript(b"alpha", transcript);
let beta = b3.commit(&b4, gens_n).compress();
beta.append_to_transcript(b"beta", transcript);
let delta = {
let gens_X = &MultiCommitGens {
n: 1,
G: vec![X.decompress().unwrap()],
h: gens_n.h,
};
b3.commit(&b5, gens_X).compress()
};
delta.append_to_transcript(b"delta", transcript);
let c = transcript.challenge_scalar(b"c");
let z1 = &b1 + &c * x;
let z2 = &b2 + &c * rX;
let z3 = &b3 + &c * y;
let z4 = &b4 + &c * rY;
let z5 = &b5 + &c * (rZ - rX * y);
let z = [z1, z2, z3, z4, z5];
(
ProductProof {
alpha,
beta,
delta,
z,
},
X,
Y,
Z,
)
}
fn check_equality(
P: &CompressedGroup,
X: &CompressedGroup,
c: &Scalar,
gens_n: &MultiCommitGens,
z1: &Scalar,
z2: &Scalar,
) -> bool {
let lhs = (P.decompress().unwrap() + c * X.decompress().unwrap()).compress();
let rhs = z1.commit(&z2, gens_n).compress();
if lhs == rhs {
true
} else {
false
}
}
pub fn verify(
&self,
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
X: &CompressedGroup,
Y: &CompressedGroup,
Z: &CompressedGroup,
) -> Result<(), ProofVerifyError> {
transcript.append_protocol_name(ProductProof::protocol_name());
X.append_to_transcript(b"X", transcript);
Y.append_to_transcript(b"Y", transcript);
Z.append_to_transcript(b"Z", transcript);
self.alpha.append_to_transcript(b"alpha", transcript);
self.beta.append_to_transcript(b"beta", transcript);
self.delta.append_to_transcript(b"delta", transcript);
let z1 = self.z[0];
let z2 = self.z[1];
let z3 = self.z[2];
let z4 = self.z[3];
let z5 = self.z[4];
let c = transcript.challenge_scalar(b"c");
if ProductProof::check_equality(&self.alpha, &X, &c, &gens_n, &z1, &z2)
&& ProductProof::check_equality(&self.beta, &Y, &c, &gens_n, &z3, &z4)
&& ProductProof::check_equality(
&self.delta,
&Z,
&c,
&MultiCommitGens {
n: 1,
G: vec![X.decompress().unwrap()],
h: gens_n.h,
},
&z3,
&z5,
)
{
Ok(())
} else {
Err(ProofVerifyError)
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DotProductProof {
delta: CompressedGroup,
beta: CompressedGroup,
z: Vec<Scalar>,
z_delta: Scalar,
z_beta: Scalar,
}
impl DotProductProof {
fn protocol_name() -> &'static [u8] {
b"dot product proof"
}
pub fn compute_dotproduct(a: &Vec<Scalar>, b: &Vec<Scalar>) -> Scalar {
assert_eq!(a.len(), b.len());
(0..a.len()).map(|i| &a[i] * &b[i]).sum()
}
pub fn prove(
gens_1: &MultiCommitGens,
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
random_tape: &mut Transcript,
x: &Vec<Scalar>,
r_x: &Scalar,
a: &Vec<Scalar>,
y: &Scalar,
r_y: &Scalar,
) -> (DotProductProof, CompressedGroup, CompressedGroup) {
transcript.append_protocol_name(DotProductProof::protocol_name());
let n = x.len();
assert_eq!(x.len(), a.len());
assert_eq!(gens_n.n, a.len());
assert_eq!(gens_1.n, 1);
// produce randomness for the proofs
let d = random_tape.challenge_vector(b"d", n);
let r_delta = random_tape.challenge_scalar(b"r_delta");
let r_beta = random_tape.challenge_scalar(b"r_beta");
let Cx = x.commit(&r_x, gens_n).compress();
Cx.append_to_transcript(b"Cx", transcript);
let Cy = y.commit(&r_y, gens_1).compress();
Cy.append_to_transcript(b"Cy", transcript);
let delta = d.commit(&r_delta, gens_n).compress();
delta.append_to_transcript(b"delta", transcript);
let dotproduct_a_d = DotProductProof::compute_dotproduct(&a, &d);
let beta = dotproduct_a_d.commit(&r_beta, gens_1).compress();
beta.append_to_transcript(b"beta", transcript);
let c = transcript.challenge_scalar(b"c");
let z = (0..d.len())
.map(|i| c * x[i] + d[i])
.collect::<Vec<Scalar>>();
let z_delta = c * r_x + r_delta;
let z_beta = c * r_y + r_beta;
(
DotProductProof {
delta,
beta,
z,
z_delta,
z_beta,
},
Cx,
Cy,
)
}
pub fn verify(
&self,
gens_1: &MultiCommitGens,
gens_n: &MultiCommitGens,
transcript: &mut Transcript,
a: &Vec<Scalar>,
Cx: &CompressedGroup,
Cy: &CompressedGroup,
) -> Result<(), ProofVerifyError> {
assert_eq!(gens_n.n, a.len());
assert_eq!(gens_1.n, 1);
transcript.append_protocol_name(DotProductProof::protocol_name());
Cx.append_to_transcript(b"Cx", transcript);
Cy.append_to_transcript(b"Cy", transcript);
self.delta.append_to_transcript(b"delta", transcript);
self.beta.append_to_transcript(b"beta", transcript);
let c = transcript.challenge_scalar(b"c");
let mut result = &c * Cx.decompress().unwrap() + self.delta.decompress().unwrap()
== self.z.commit(&self.z_delta, gens_n);
let dotproduct_z_a = DotProductProof::compute_dotproduct(&self.z, &a);
result &= &c * Cy.decompress().unwrap() + self.beta.decompress().unwrap()
== dotproduct_z_a.commit(&self.z_beta, gens_1);
if result {
Ok(())
} else {
Err(ProofVerifyError)
}
}
}
pub struct DotProductProofGens {
n: usize,
pub gens_n: MultiCommitGens,
pub gens_1: MultiCommitGens,
}
impl DotProductProofGens {
pub fn new(n: usize, label: &[u8]) -> Self {
let (gens_n, gens_1) = MultiCommitGens::new(n + 1, label).split_at_mut(n);
DotProductProofGens { n, gens_n, gens_1 }
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DotProductProofLog {
bullet_reduction_proof: BulletReductionProof,
delta: CompressedGroup,
beta: CompressedGroup,
z1: Scalar,
z2: Scalar,
}
impl DotProductProofLog {
fn protocol_name() -> &'static [u8] {
b"dot product proof (log)"
}
pub fn compute_dotproduct(a: &Vec<Scalar>, b: &Vec<Scalar>) -> Scalar {
assert_eq!(a.len(), b.len());
(0..a.len()).map(|i| &a[i] * &b[i]).sum()
}
pub fn prove(
gens: &DotProductProofGens,
transcript: &mut Transcript,
random_tape: &mut Transcript,
x: &Vec<Scalar>,
r_x: &Scalar,
a: &Vec<Scalar>,
y: &Scalar,
r_y: &Scalar,
) -> (DotProductProofLog, CompressedGroup, CompressedGroup) {
transcript.append_protocol_name(DotProductProofLog::protocol_name());
let n = x.len();
assert_eq!(x.len(), a.len());
assert_eq!(gens.n, n);
// produce randomness for generating a proof
let d = random_tape.challenge_scalar(b"d");
let r_delta = random_tape.challenge_scalar(b"r_delta");
let r_beta = random_tape.challenge_scalar(b"r_delta");
let blinds_vec = {
let v1 = random_tape.challenge_vector(b"blinds_vec_1", 2 * n.log2());
let v2 = random_tape.challenge_vector(b"blinds_vec_2", 2 * n.log2());
(0..v1.len())
.map(|i| (v1[i], v2[i]))
.collect::<Vec<(Scalar, Scalar)>>()
};
let Cx = x.commit(&r_x, &gens.gens_n).compress();
Cx.append_to_transcript(b"Cx", transcript);
let Cy = y.commit(&r_y, &gens.gens_1).compress();
Cy.append_to_transcript(b"Cy", transcript);
let r_Gamma = r_x + r_y;
let (bullet_reduction_proof, _Gamma_hat, x_hat, a_hat, g_hat, rhat_Gamma) =
BulletReductionProof::prove(
transcript,
&gens.gens_1.G[0],
&gens.gens_n.G,
&gens.gens_n.h,
x,
a,
&r_Gamma,
&blinds_vec,
);
let y_hat = x_hat * a_hat;
let delta = {
let gens_hat = MultiCommitGens {
n: 1,
G: vec![g_hat],
h: gens.gens_1.h,
};
d.commit(&r_delta, &gens_hat).compress()
};
delta.append_to_transcript(b"delta", transcript);
let beta = d.commit(&r_beta, &gens.gens_1).compress();
beta.append_to_transcript(b"beta", transcript);
let c = transcript.challenge_scalar(b"c");
let z1 = d + c * y_hat;
let z2 = a_hat * (c * rhat_Gamma + r_beta) + r_delta;
(
DotProductProofLog {
bullet_reduction_proof,
delta,
beta,
z1,
z2,
},
Cx,
Cy,
)
}
pub fn verify(
&self,
n: usize,
gens: &DotProductProofGens,
transcript: &mut Transcript,
a: &Vec<Scalar>,
Cx: &CompressedGroup,
Cy: &CompressedGroup,
) -> Result<(), ProofVerifyError> {
assert_eq!(gens.n, n);
assert_eq!(a.len(), n);
transcript.append_protocol_name(DotProductProofLog::protocol_name());
Cx.append_to_transcript(b"Cx", transcript);
Cy.append_to_transcript(b"Cy", transcript);
let Gamma = Cx.decompress().unwrap() + Cy.decompress().unwrap();
let (g_hat, Gamma_hat, a_hat) = self
.bullet_reduction_proof
.verify(n, a, transcript, &Gamma, &gens.gens_n.G)
.unwrap();
self.delta.append_to_transcript(b"delta", transcript);
self.beta.append_to_transcript(b"beta", transcript);
let c = transcript.challenge_scalar(b"c");
let c_s = &c;
let beta_s = self.beta.decompress().unwrap();
let a_hat_s = &a_hat;
let delta_s = self.delta.decompress().unwrap();
let z1_s = &self.z1;
let z2_s = &self.z2;
let lhs = ((Gamma_hat * c_s + beta_s) * a_hat_s + delta_s).compress();
let rhs = ((g_hat + &gens.gens_1.G[0] * a_hat_s) * z1_s + gens.gens_1.h * z2_s).compress();
assert_eq!(lhs, rhs);
if lhs == rhs {
Ok(())
} else {
Err(ProofVerifyError)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::OsRng;
#[test]
fn check_knowledgeproof() {
let mut csprng: OsRng = OsRng;
let gens_1 = MultiCommitGens::new(1, b"test-knowledgeproof");
let x = Scalar::random(&mut csprng);
let r = Scalar::random(&mut csprng);
let mut random_tape = {
let mut csprng: OsRng = OsRng;
let mut tape = Transcript::new(b"proof");
tape.append_scalar(b"init_randomness", &Scalar::random(&mut csprng));
tape
};
let mut prover_transcript = Transcript::new(b"example");
let (proof, committed_value) =
KnowledgeProof::prove(&gens_1, &mut prover_transcript, &mut random_tape, &x, &r);
let mut verifier_transcript = Transcript::new(b"example");
assert!(proof
.verify(&gens_1, &mut verifier_transcript, &committed_value)
.is_ok());
}
#[test]
fn check_equalityproof() {
let mut csprng: OsRng = OsRng;
let gens_1 = MultiCommitGens::new(1, b"test-equalityproof");
let v1 = Scalar::random(&mut csprng);
let v2 = v1;
let s1 = Scalar::random(&mut csprng);
let s2 = Scalar::random(&mut csprng);
let mut random_tape = {
let mut csprng: OsRng = OsRng;
let mut tape = Transcript::new(b"proof");
tape.append_scalar(b"init_randomness", &Scalar::random(&mut csprng));
tape
};
let mut prover_transcript = Transcript::new(b"example");
let (proof, C1, C2) = EqualityProof::prove(
&gens_1,
&mut prover_transcript,
&mut random_tape,
&v1,
&s1,
&v2,
&s2,
);
let mut verifier_transcript = Transcript::new(b"example");
assert!(proof
.verify(&gens_1, &mut verifier_transcript, &C1, &C2)
.is_ok());
}
#[test]
fn check_productproof() {
let mut csprng: OsRng = OsRng;
let gens_1 = MultiCommitGens::new(1, b"test-productproof");
let x = Scalar::random(&mut csprng);
let rX = Scalar::random(&mut csprng);
let y = Scalar::random(&mut csprng);
let rY = Scalar::random(&mut csprng);
let z = x * y;
let rZ = Scalar::random(&mut csprng);
let mut random_tape = {
let mut csprng: OsRng = OsRng;
let mut tape = Transcript::new(b"proof");
tape.append_scalar(b"init_randomness", &Scalar::random(&mut csprng));
tape
};
let mut prover_transcript = Transcript::new(b"example");
let (proof, X, Y, Z) = ProductProof::prove(
&gens_1,
&mut prover_transcript,
&mut random_tape,
&x,
&rX,
&y,
&rY,
&z,
&rZ,
);
let mut verifier_transcript = Transcript::new(b"example");
assert!(proof
.verify(&gens_1, &mut verifier_transcript, &X, &Y, &Z)
.is_ok());
}
#[test]
fn check_dotproductproof() {
let mut csprng: OsRng = OsRng;
let n = 1024;
let gens_1 = MultiCommitGens::new(1, b"test-two");
let gens_1024 = MultiCommitGens::new(n, b"test-1024");
let mut x: Vec<Scalar> = Vec::new();
let mut a: Vec<Scalar> = Vec::new();
for _ in 0..n {
x.push(Scalar::random(&mut csprng));
a.push(Scalar::random(&mut csprng));
}
let y = DotProductProofLog::compute_dotproduct(&x, &a);
let r_x = Scalar::random(&mut csprng);
let r_y = Scalar::random(&mut csprng);
let mut random_tape = {
let mut csprng: OsRng = OsRng;
let mut tape = Transcript::new(b"proof");
tape.append_scalar(b"init_randomness", &Scalar::random(&mut csprng));
tape
};
let mut prover_transcript = Transcript::new(b"example");
let (proof, Cx, Cy) = DotProductProof::prove(
&gens_1,
&gens_1024,
&mut prover_transcript,
&mut random_tape,
&x,
&r_x,
&a,
&y,
&r_y,
);
let mut verifier_transcript = Transcript::new(b"example");
assert!(proof
.verify(&gens_1, &gens_1024, &mut verifier_transcript, &a, &Cx, &Cy)
.is_ok());
}
#[test]
fn check_dotproductproof_log() {
let mut csprng: OsRng = OsRng;
let n = 1024;
let gens = DotProductProofGens::new(n, b"test-1024");
let x: Vec<Scalar> = (0..n).map(|_i| Scalar::random(&mut csprng)).collect();
let a: Vec<Scalar> = (0..n).map(|_i| Scalar::random(&mut csprng)).collect();
let y = DotProductProof::compute_dotproduct(&x, &a);
let r_x = Scalar::random(&mut csprng);
let r_y = Scalar::random(&mut csprng);
let mut random_tape = {
let mut csprng: OsRng = OsRng;
let mut tape = Transcript::new(b"proof");
tape.append_scalar(b"init_randomness", &Scalar::random(&mut csprng));
tape
};
let mut prover_transcript = Transcript::new(b"example");
let (proof, Cx, Cy) = DotProductProofLog::prove(
&gens,
&mut prover_transcript,
&mut random_tape,
&x,
&r_x,
&a,
&y,
&r_y,
);
let mut verifier_transcript = Transcript::new(b"example");
assert!(proof
.verify(n, &gens, &mut verifier_transcript, &a, &Cx, &Cy)
.is_ok());
}
}