1use ark_bls12_381::{Bls12_381, Fr as ConstraintF};
2use ark_crypto_primitives::snark::{CircuitSpecificSetupSNARK, SNARK};
3use ark_ec::{CurveGroup, Group};
4use ark_ed_on_bls12_381::{EdwardsAffine, EdwardsProjective, Fr as JubjubScalar};
5use ark_ff::{BigInteger, PrimeField};
6use ark_groth16::{prepare_verifying_key, Groth16, Proof, ProvingKey, VerifyingKey};
7use ark_r1cs_std::{
8 alloc::AllocVar,
9 boolean::Boolean,
10 eq::EqGadget,
11 groups::{curves::twisted_edwards::AffineVar, CurveVar},
12};
13use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
14use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError};
15use ark_std::rand::{CryptoRng, RngCore};
16use core::fmt;
17use sha2::{Digest, Sha512};
18
19type EdwardsVar =
20 AffineVar<ark_ed_on_bls12_381::JubjubConfig, ark_ed_on_bls12_381::constraints::FqVar>;
21type ZkGroth16 = Groth16<Bls12_381>;
22const SCALAR_BITS: usize = 256;
23
24#[derive(Debug)]
25pub enum Error {
26 Encoding(SerializationError),
27 Synthesis(SynthesisError),
28 Snark(ark_crypto_primitives::Error),
29}
30
31pub type Result<T> = core::result::Result<T, Error>;
32
33impl fmt::Display for Error {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 Self::Encoding(err) => write!(f, "invalid byte encoding: {err}"),
37 Self::Synthesis(err) => write!(f, "proving failed: {err}"),
38 Self::Snark(err) => write!(f, "snark error: {err}"),
39 }
40 }
41}
42
43impl std::error::Error for Error {}
44
45impl From<SerializationError> for Error {
46 fn from(err: SerializationError) -> Self {
47 Self::Encoding(err)
48 }
49}
50
51impl From<SynthesisError> for Error {
52 fn from(err: SynthesisError) -> Self {
53 Self::Synthesis(err)
54 }
55}
56
57impl From<ark_crypto_primitives::Error> for Error {
58 fn from(err: ark_crypto_primitives::Error) -> Self {
59 Self::Snark(err)
60 }
61}
62
63#[derive(Clone)]
64struct SignatureKnowledgeCircuit {
65 public_key: Option<EdwardsAffine>,
66 challenge_bits: Vec<bool>,
67 secret_key_bits: Option<Vec<bool>>,
68 nonce_bits: Option<Vec<bool>>,
69 response_bits: Option<Vec<bool>>,
70}
71
72impl ConstraintSynthesizer<ConstraintF> for SignatureKnowledgeCircuit {
73 fn generate_constraints(
74 self,
75 cs: ConstraintSystemRef<ConstraintF>,
76 ) -> core::result::Result<(), SynthesisError> {
77 let public_key = EdwardsVar::new_input(cs.clone(), || {
78 self.public_key.ok_or(SynthesisError::AssignmentMissing)
79 })?;
80
81 let challenge_bits = allocate_input_bits(cs.clone(), &self.challenge_bits)?;
82 let secret_key_bits = allocate_witness_bits(cs.clone(), self.secret_key_bits)?;
83 let nonce_bits = allocate_witness_bits(cs.clone(), self.nonce_bits)?;
84 let response_bits = allocate_witness_bits(cs, self.response_bits)?;
85
86 let base = EdwardsVar::constant(EdwardsProjective::generator());
87 let derived_public_key = base.scalar_mul_le(secret_key_bits.iter())?;
88 derived_public_key.enforce_equal(&public_key)?;
89
90 let nonce_point = base.scalar_mul_le(nonce_bits.iter())?;
91 let response_point = base.scalar_mul_le(response_bits.iter())?;
92 let challenged_public_key = public_key.scalar_mul_le(challenge_bits.iter())?;
93 let expected_response_point = nonce_point + challenged_public_key;
94
95 response_point.enforce_equal(&expected_response_point)?;
96 Ok(())
97 }
98}
99
100pub fn setup<R: RngCore + CryptoRng>(rng: &mut R) -> Result<(Vec<u8>, Vec<u8>)> {
102 let one = JubjubScalar::from_le_bytes_mod_order(&[1]);
103 let blank = SignatureKnowledgeCircuit {
104 public_key: Some(EdwardsProjective::generator().into_affine()),
105 challenge_bits: vec![false; SCALAR_BITS],
106 secret_key_bits: Some(scalar_bits_le(&one)),
107 nonce_bits: Some(scalar_bits_le(&one)),
108 response_bits: Some(scalar_bits_le(&one)),
109 };
110 let (pk, vk) = ZkGroth16::setup(blank, rng)?;
111 Ok((serialize_compressed(&pk)?, serialize_compressed(&vk)?))
112}
113
114pub fn derive_public_key(secret_key: &[u8]) -> Result<Vec<u8>> {
116 let secret = scalar_from_bytes(secret_key);
117 serialize_compressed(&(EdwardsProjective::generator() * secret).into_affine())
118}
119
120pub fn prove<R: RngCore + CryptoRng>(
125 proving_key: &[u8],
126 secret_key: &[u8],
127 message: &[u8],
128 nonce: &[u8],
129 rng: &mut R,
130) -> Result<Vec<u8>> {
131 let pk = ProvingKey::<Bls12_381>::deserialize_compressed(proving_key)?;
132 let secret = scalar_from_bytes(secret_key);
133 let nonce = scalar_from_bytes(nonce);
134 let public_key = (EdwardsProjective::generator() * secret).into_affine();
135 let challenge = challenge_scalar(&public_key, message)?;
136 let response = nonce + challenge * secret;
137
138 let circuit = SignatureKnowledgeCircuit {
139 public_key: Some(public_key),
140 challenge_bits: scalar_bits_le(&challenge),
141 secret_key_bits: Some(scalar_bits_le(&secret)),
142 nonce_bits: Some(scalar_bits_le(&nonce)),
143 response_bits: Some(scalar_bits_le(&response)),
144 };
145
146 let proof = ZkGroth16::prove(&pk, circuit, rng)?;
147 serialize_compressed(&proof)
148}
149
150pub fn verify(
152 verifying_key: &[u8],
153 proof: &[u8],
154 public_key: &[u8],
155 message: &[u8],
156) -> Result<bool> {
157 let vk = VerifyingKey::<Bls12_381>::deserialize_compressed(verifying_key)?;
158 let proof = Proof::<Bls12_381>::deserialize_compressed(proof)?;
159 let public_key = EdwardsAffine::deserialize_compressed(public_key)?;
160 let challenge = challenge_scalar(&public_key, message)?;
161 let public_inputs = groth16_public_inputs(&public_key, &challenge);
162 let pvk = prepare_verifying_key(&vk);
163 Ok(ZkGroth16::verify_with_processed_vk(
164 &pvk,
165 &public_inputs,
166 &proof,
167 )?)
168}
169
170fn allocate_input_bits(
171 cs: ConstraintSystemRef<ConstraintF>,
172 bits: &[bool],
173) -> core::result::Result<Vec<Boolean<ConstraintF>>, SynthesisError> {
174 bits.iter()
175 .map(|bit| Boolean::new_input(cs.clone(), || Ok(*bit)))
176 .collect()
177}
178
179fn allocate_witness_bits(
180 cs: ConstraintSystemRef<ConstraintF>,
181 bits: Option<Vec<bool>>,
182) -> core::result::Result<Vec<Boolean<ConstraintF>>, SynthesisError> {
183 let bits = bits.ok_or(SynthesisError::AssignmentMissing)?;
184 bits.into_iter()
185 .map(|bit| Boolean::new_witness(cs.clone(), || Ok(bit)))
186 .collect()
187}
188
189fn scalar_from_bytes(bytes: &[u8]) -> JubjubScalar {
190 JubjubScalar::from_le_bytes_mod_order(bytes)
191}
192
193fn challenge_scalar(public_key: &EdwardsAffine, message: &[u8]) -> Result<JubjubScalar> {
194 let public_key_bytes = serialize_compressed(public_key)?;
195 let mut hasher = Sha512::new();
196 hasher.update(b"zk402.phase1.challenge.v1");
197 hasher.update(public_key_bytes);
198 hasher.update(message);
199 Ok(JubjubScalar::from_le_bytes_mod_order(&hasher.finalize()))
200}
201
202fn scalar_bits_le(scalar: &JubjubScalar) -> Vec<bool> {
203 let mut bits = scalar.into_bigint().to_bits_le();
204 bits.resize(SCALAR_BITS, false);
205 bits
206}
207
208fn groth16_public_inputs(public_key: &EdwardsAffine, challenge: &JubjubScalar) -> Vec<ConstraintF> {
209 let mut inputs = vec![public_key.x, public_key.y];
210 inputs.extend(
211 scalar_bits_le(challenge)
212 .into_iter()
213 .map(|bit| ConstraintF::from_le_bytes_mod_order(&[u8::from(bit)])),
214 );
215 inputs
216}
217
218fn serialize_compressed<T: CanonicalSerialize>(value: &T) -> Result<Vec<u8>> {
219 let mut bytes = Vec::with_capacity(value.compressed_size());
220 value.serialize_compressed(&mut bytes)?;
221 Ok(bytes)
222}
223
224pub mod proof_system {
226 use super::*;
227 use zk402_core::{ProofSystem, ProofSystemError, PublicInputs, Witness};
228
229 pub struct Groth16ProofSystem;
230
231 impl ProofSystem for Groth16ProofSystem {
232 type ProvingKey = Vec<u8>;
233 type VerifyingKey = Vec<u8>;
234
235 fn prove(
236 pk: &Self::ProvingKey,
237 witness: &Witness,
238 public_inputs: &PublicInputs,
239 ) -> core::result::Result<Vec<u8>, ProofSystemError> {
240 let message = construct_message(public_inputs);
241 let mut rng = ark_std::rand::thread_rng();
242
243 super::prove(
244 pk,
245 &witness.secret_key,
246 &message,
247 &witness.nonce_scalar,
248 &mut rng,
249 )
250 .map_err(|e| ProofSystemError::ProvingFailed(e.to_string()))
251 }
252
253 fn verify(
254 vk: &Self::VerifyingKey,
255 proof: &[u8],
256 public_inputs: &PublicInputs,
257 ) -> core::result::Result<bool, ProofSystemError> {
258 let message = construct_message(public_inputs);
259
260 super::verify(vk, proof, &public_inputs.public_key, &message)
261 .map_err(|e| ProofSystemError::VerificationFailed(e.to_string()))
262 }
263 }
264
265 fn construct_message(inputs: &PublicInputs) -> Vec<u8> {
266 let mut message = Vec::new();
268 message.extend_from_slice(b"zk402.payment.v1:");
269 message.extend_from_slice(inputs.from.as_slice());
270 message.extend_from_slice(inputs.pay_to.as_slice());
271 message.extend_from_slice(inputs.amount.as_bytes());
272 message.extend_from_slice(inputs.asset.as_slice());
273 message.extend_from_slice(inputs.network.as_bytes());
274 message.extend_from_slice(&inputs.nonce);
275 message.extend_from_slice(&inputs.valid_after.to_le_bytes());
276 message.extend_from_slice(&inputs.valid_before.to_le_bytes());
277 message
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use ark_std::rand::{rngs::StdRng, SeedableRng};
284 use std::sync::OnceLock;
285
286 fn message(amount: &str, pay_to: &str) -> Vec<u8> {
287 format!("amount={amount};pay_to={pay_to}").into_bytes()
288 }
289
290 fn test_keys() -> &'static (Vec<u8>, Vec<u8>) {
291 static KEYS: OnceLock<(Vec<u8>, Vec<u8>)> = OnceLock::new();
292 KEYS.get_or_init(|| {
293 let mut rng = StdRng::seed_from_u64(1);
294 setup(&mut rng).unwrap()
295 })
296 }
297
298 fn proof_for(message: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
299 let mut rng = StdRng::seed_from_u64(1);
300 let secret_key = b"phase-one-test-secret-key";
301 let nonce = b"phase-one-test-nonce";
302 let public_key = derive_public_key(secret_key).unwrap();
303 let proof = prove(&test_keys().0, secret_key, message, nonce, &mut rng).unwrap();
304 (test_keys().1.clone(), public_key, proof)
305 }
306
307 #[test]
308 fn valid_signature_knowledge_proof_verifies() {
309 let msg = message("100", "0xabc");
310 let (vk, public_key, proof) = proof_for(&msg);
311
312 assert!(verify(&vk, &proof, &public_key, &msg).unwrap());
313 }
314
315 #[test]
316 fn tampered_amount_fails_to_verify() {
317 let (vk, public_key, proof) = proof_for(&message("100", "0xabc"));
318
319 assert!(!verify(&vk, &proof, &public_key, &message("101", "0xabc")).unwrap());
320 }
321
322 #[test]
323 fn tampered_pay_to_fails_to_verify() {
324 let (vk, public_key, proof) = proof_for(&message("100", "0xabc"));
325
326 assert!(!verify(&vk, &proof, &public_key, &message("100", "0xdef")).unwrap());
327 }
328}