zk_paillier/zkproofs/
correct_ciphertext.rs

1use std::iter;
2
3use serde::{Deserialize, Serialize};
4
5use curv::arithmetic::traits::{Modulo, Samplable};
6use curv::BigInt;
7use paillier::traits::{Add, Mul};
8use paillier::EncryptWithChosenRandomness;
9use paillier::Paillier;
10use paillier::{EncryptionKey, Randomness, RawCiphertext, RawPlaintext};
11
12use super::errors::IncorrectProof;
13
14/// This proof shows that a paillier ciphertext was constructed correctly
15///
16/// The proof is taken from https://www.brics.dk/RS/00/14/BRICS-RS-00-14.pdf 9.1.3
17/// Given a ciphertext c and a prover encryption key , a prover wants to prove that it knows (x,r) such that c = Enc(x,r)
18/// 1) P picks x',r' at random, and computes c' = Enc(x', r')
19/// 2) P computes z1 = x' + ex , z2 = r' *r^e  (e is a varifier challenge)
20/// 3) P sends, c' , z1,z2
21/// 4) V accepts if 1) Enc(z1,z2 ) = c' * c^e
22#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
23pub struct CiphertextProof {
24    pub z1: BigInt,
25    pub z2: BigInt,
26    pub c_prime: BigInt,
27}
28
29#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
30pub struct CiphertextWitness {
31    pub x: BigInt,
32    pub r: BigInt,
33}
34
35#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
36pub struct CiphertextStatement {
37    pub ek: EncryptionKey,
38    pub c: BigInt,
39}
40
41impl CiphertextProof {
42    pub fn prove(witness: &CiphertextWitness, statement: &CiphertextStatement) -> Self {
43        let x_prime = BigInt::sample_below(&statement.ek.n);
44        let r_prime = BigInt::sample_below(&statement.ek.n);
45        let c_prime = Paillier::encrypt_with_chosen_randomness(
46            &statement.ek,
47            RawPlaintext::from(x_prime.clone()),
48            &Randomness(r_prime.clone()),
49        )
50        .0
51        .into_owned();
52
53        let e = super::compute_digest(
54            iter::once(&statement.ek.n)
55                .chain(iter::once(&statement.c))
56                .chain(iter::once(&c_prime)),
57        );
58
59        let z1 = &x_prime + &witness.x * &e;
60        let r_e = BigInt::mod_pow(&witness.r, &e, &statement.ek.nn);
61        let z2 = BigInt::mod_mul(&r_prime, &r_e, &statement.ek.nn);
62
63        CiphertextProof { z1, z2, c_prime }
64    }
65
66    pub fn verify(&self, statement: &CiphertextStatement) -> Result<(), IncorrectProof> {
67        let e = super::compute_digest(
68            iter::once(&statement.ek.n)
69                .chain(iter::once(&statement.c))
70                .chain(iter::once(&self.c_prime)),
71        );
72
73        let c_z = Paillier::encrypt_with_chosen_randomness(
74            &statement.ek,
75            RawPlaintext::from(self.z1.clone()),
76            &Randomness(self.z2.clone()),
77        )
78        .0
79        .into_owned();
80
81        let c_e = Paillier::mul(
82            &statement.ek,
83            RawPlaintext::from(e),
84            RawCiphertext::from(statement.c.clone()),
85        );
86        let c_z_test = Paillier::add(
87            &statement.ek,
88            c_e,
89            RawCiphertext::from(self.c_prime.clone()),
90        )
91        .0
92        .into_owned();
93
94        match c_z == c_z_test {
95            true => Ok(()),
96            false => Err(IncorrectProof),
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use curv::arithmetic::traits::*;
104    use curv::BigInt;
105    use paillier::core::Randomness;
106    use paillier::traits::EncryptWithChosenRandomness;
107    use paillier::traits::KeyGeneration;
108    use paillier::Paillier;
109    use paillier::RawPlaintext;
110
111    use crate::zkproofs::correct_ciphertext::CiphertextProof;
112    use crate::zkproofs::correct_ciphertext::CiphertextStatement;
113    use crate::zkproofs::correct_ciphertext::CiphertextWitness;
114
115    #[test]
116    fn test_ciphertext_proof() {
117        let (ek, _) = Paillier::keypair().keys();
118        let x = BigInt::sample_below(&ek.n);
119        let r = BigInt::sample_below(&ek.n);
120
121        let c = Paillier::encrypt_with_chosen_randomness(
122            &ek,
123            RawPlaintext::from(x.clone()),
124            &Randomness(r.clone()),
125        )
126        .0
127        .into_owned();
128
129        let witness = CiphertextWitness { x, r };
130
131        let statement = CiphertextStatement { ek, c };
132
133        let proof = CiphertextProof::prove(&witness, &statement);
134        let verify = proof.verify(&statement);
135        assert!(verify.is_ok());
136    }
137
138    #[test]
139    #[should_panic]
140    fn test_bad_ciphertext_proof() {
141        let (ek, _) = Paillier::keypair().keys();
142        let x = BigInt::sample_below(&ek.n);
143        let r = BigInt::sample_below(&ek.n);
144
145        let c = Paillier::encrypt_with_chosen_randomness(
146            &ek,
147            RawPlaintext::from(x.clone()),
148            &Randomness(r.clone()),
149        )
150        .0
151        .into_owned();
152
153        let witness = CiphertextWitness {
154            x,
155            r: r + BigInt::one(),
156        };
157
158        let statement = CiphertextStatement { ek, c };
159
160        let proof = CiphertextProof::prove(&witness, &statement);
161        let verify = proof.verify(&statement);
162        assert!(verify.is_ok());
163    }
164}