// -*- coding: utf-8; mode: rust; -*- // // To the extent possible under law, the authors have waived all // copyright and related or neighboring rights to zkp, // using the Creative Commons "CC0" public domain dedication. See // for full // details. // // Authors: // - Henry de Valence extern crate rand; use rand::{thread_rng, CryptoRng, RngCore}; extern crate curve25519_dalek; use curve25519_dalek::constants as dalek_constants; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; #[macro_use] extern crate zkp; pub use zkp::Transcript; define_proof! {sig_proof, "Sig", (x), (A), (B) : A = (x * B) } define_proof! {vrf_proof, "VRF", (x), (A, G, H), (B) : A = (x * B), G = (x * H) } /// Defines how the construction interacts with the transcript. trait TranscriptProtocol { fn append_message_example(&mut self, message: &[u8]); fn hash_to_group(self) -> RistrettoPoint; } impl TranscriptProtocol for Transcript { fn append_message_example(&mut self, message: &[u8]) { self.append_message(b"msg", message); } fn hash_to_group(mut self) -> RistrettoPoint { let mut bytes = [0u8; 64]; self.challenge_bytes(b"output", &mut bytes); RistrettoPoint::from_uniform_bytes(&bytes) } } #[derive(Clone)] pub struct SecretKey(Scalar); impl SecretKey { fn new(rng: &mut R) -> SecretKey { SecretKey(Scalar::random(rng)) } } #[derive(Copy, Clone)] pub struct PublicKey(RistrettoPoint, CompressedRistretto); impl<'a> From<&'a SecretKey> for PublicKey { fn from(sk: &'a SecretKey) -> PublicKey { let pk = &sk.0 * &dalek_constants::RISTRETTO_BASEPOINT_TABLE; PublicKey(pk, pk.compress()) } } pub struct KeyPair { sk: SecretKey, pk: PublicKey, } impl From for KeyPair { fn from(sk: SecretKey) -> KeyPair { let pk = PublicKey::from(&sk); KeyPair { sk, pk } } } pub struct Signature(sig_proof::BatchableProof); pub struct VrfOutput(CompressedRistretto); pub struct VrfProof(vrf_proof::CompactProof); impl KeyPair { fn public_key(&self) -> PublicKey { self.pk } fn sign(&self, message: &[u8], sig_transcript: &mut Transcript) -> Signature { sig_transcript.append_message_example(message); let (proof, _points) = sig_proof::prove_batchable( sig_transcript, sig_proof::ProveAssignments { x: &self.sk.0, A: &self.pk.0, B: &dalek_constants::RISTRETTO_BASEPOINT_POINT, }, ); Signature(proof) } #[allow(non_snake_case)] fn vrf( &self, mut function_transcript: Transcript, message: &[u8], proof_transcript: &mut Transcript, ) -> (VrfOutput, VrfProof) { // Use function_transcript to hash the message to a point H function_transcript.append_message_example(message); let H = function_transcript.hash_to_group(); // Compute the VRF output G and form a proof let G = &H * &self.sk.0; let (proof, points) = vrf_proof::prove_compact( proof_transcript, vrf_proof::ProveAssignments { x: &self.sk.0, A: &self.pk.0, B: &dalek_constants::RISTRETTO_BASEPOINT_POINT, G: &G, H: &H, }, ); (VrfOutput(points.G), VrfProof(proof)) } } impl Signature { fn verify( &self, message: &[u8], pubkey: &PublicKey, sig_transcript: &mut Transcript, ) -> Result<(), ()> { sig_transcript.append_message_example(message); sig_proof::verify_batchable( &self.0, sig_transcript, sig_proof::VerifyAssignments { A: &pubkey.1, B: &dalek_constants::RISTRETTO_BASEPOINT_COMPRESSED, }, ) .map_err(|_discard_error_info_in_test_code| ()) } } impl VrfOutput { #[allow(non_snake_case)] fn verify( &self, mut function_transcript: Transcript, message: &[u8], pubkey: &PublicKey, proof_transcript: &mut Transcript, proof: &VrfProof, ) -> Result<(), ()> { // Use function_transcript to hash the message to a point H function_transcript.append_message_example(message); let H = function_transcript.hash_to_group().compress(); vrf_proof::verify_compact( &proof.0, proof_transcript, vrf_proof::VerifyAssignments { A: &pubkey.1, B: &dalek_constants::RISTRETTO_BASEPOINT_COMPRESSED, G: &self.0, H: &H, }, ) .map_err(|_discard_error_info_in_test_code| ()) } } #[test] fn create_and_verify_sig() { let domain_sep = b"My Sig Application"; let msg1 = b"Test Message 1"; let msg2 = b"Test Message 2"; let kp1 = KeyPair::from(SecretKey::new(&mut thread_rng())); let pk1 = kp1.public_key(); let kp2 = KeyPair::from(SecretKey::new(&mut thread_rng())); let pk2 = kp2.public_key(); let sig1 = kp1.sign(&msg1[..], &mut Transcript::new(domain_sep)); let sig2 = kp2.sign(&msg2[..], &mut Transcript::new(domain_sep)); // Check that each signature verifies assert!(sig1 .verify(msg1, &pk1, &mut Transcript::new(domain_sep),) .is_ok()); assert!(sig2 .verify(msg2, &pk2, &mut Transcript::new(domain_sep),) .is_ok()); // Check that verification with the wrong pubkey fails assert!(sig1 .verify(msg1, &pk2, &mut Transcript::new(domain_sep),) .is_err()); assert!(sig2 .verify(msg2, &pk1, &mut Transcript::new(domain_sep),) .is_err()); // Check that verification with the wrong message fails assert!(sig1 .verify(msg2, &pk1, &mut Transcript::new(domain_sep),) .is_err()); assert!(sig2 .verify(msg1, &pk2, &mut Transcript::new(domain_sep),) .is_err()); // Check that verification with the wrong domain separator fails assert!(sig1 .verify(msg1, &pk1, &mut Transcript::new(b"Wrong"),) .is_err()); assert!(sig2 .verify(msg2, &pk2, &mut Transcript::new(b"Wrong"),) .is_err()); } #[test] #[ignore] fn create_and_verify_bigsig() { let domain_sep = b"My Sig Application"; let mut large_msg = Vec::new(); large_msg.resize((u32::max_value() as usize) + 250, 1u8); let kp = KeyPair::from(SecretKey::new(&mut thread_rng())); let pk = kp.public_key(); let sig = kp.sign(&large_msg[..], &mut Transcript::new(domain_sep)); // Check that the signature verifies (& doesn't panic inside Merlin) assert!(sig .verify(&large_msg[..], &pk, &mut Transcript::new(domain_sep),) .is_ok()); } #[test] fn counterparty_signature_chain() { let domain_sep = b"Counterparty Example"; let msg1a = b"In this test, two counterparties exchange signatures."; let msg2a = b"However, the counterparties sign and verify messages"; let msg1b = b"using stateful transcript objects."; let msg2b = b"When party 1 signs, the party 1 transcript changes;"; let msg1c = b"when party 2 verifies, the party 2 transcript syncs."; let msg2c = b"In this way, the transcript states ratchet stateful signatures."; let kp1 = KeyPair::from(SecretKey::new(&mut thread_rng())); let pk1 = kp1.public_key(); let kp2 = KeyPair::from(SecretKey::new(&mut thread_rng())); let pk2 = kp2.public_key(); let mut trans1 = Transcript::new(domain_sep); let mut trans2 = Transcript::new(domain_sep); // Round a, Party 1 -----> Party 2 let sig1a = kp1.sign(&msg1a[..], &mut trans1); assert!(sig1a.verify(msg1a, &pk1, &mut trans2).is_ok()); // Round a, Party 2 -----> Party 1 let sig2a = kp2.sign(&msg2a[..], &mut trans2); assert!(sig2a.verify(msg2a, &pk2, &mut trans1).is_ok()); // Round b, Party 1 -----> Party 2 let sig1b = kp1.sign(&msg1b[..], &mut trans1); assert!(sig1b.verify(msg1b, &pk1, &mut trans2).is_ok()); // Round b, Party 2 -----> Party 1 let sig2b = kp2.sign(&msg2b[..], &mut trans2); assert!(sig2b.verify(msg2b, &pk2, &mut trans1).is_ok()); // Round c, Party 1 -----> Party 2 let sig1c = kp1.sign(&msg1c[..], &mut trans1); assert!(sig1c.verify(msg1c, &pk1, &mut trans2).is_ok()); // Round c, Party 2 -----> Party 1 let sig2c = kp2.sign(&msg2c[..], &mut trans2); assert!(sig2c.verify(msg2c, &pk2, &mut trans1).is_ok()); } #[test] fn create_and_verify_vrf() { let domain_sep = b"My VRF Application"; let msg1 = b"Test Message 1"; let msg2 = b"Test Message 2"; let kp1 = KeyPair::from(SecretKey::new(&mut thread_rng())); let pk1 = kp1.public_key(); let kp2 = KeyPair::from(SecretKey::new(&mut thread_rng())); let pk2 = kp2.public_key(); let (output1, proof1) = kp1.vrf( Transcript::new(domain_sep), &msg1[..], &mut Transcript::new(domain_sep), ); let (output2, proof2) = kp2.vrf( Transcript::new(domain_sep), &msg2[..], &mut Transcript::new(domain_sep), ); // Check that each VRF output was correctly produced assert!(output1 .verify( Transcript::new(domain_sep), msg1, &pk1, &mut Transcript::new(domain_sep), &proof1, ) .is_ok()); assert!(output2 .verify( Transcript::new(domain_sep), msg2, &pk2, &mut Transcript::new(domain_sep), &proof2, ) .is_ok()); // Check that verification with the wrong pubkey fails assert!(output1 .verify( Transcript::new(domain_sep), msg1, &pk2, // swap pubkey &mut Transcript::new(domain_sep), &proof1, ) .is_err()); assert!(output2 .verify( Transcript::new(domain_sep), msg2, &pk1, // swap pubkey &mut Transcript::new(domain_sep), &proof2, ) .is_err()); // Check that verification with the wrong output fails assert!(output2 // swap output .verify( Transcript::new(domain_sep), msg1, &pk1, &mut Transcript::new(domain_sep), &proof1, ) .is_err()); assert!(output1 // swap output .verify( Transcript::new(domain_sep), msg2, &pk2, &mut Transcript::new(domain_sep), &proof2, ) .is_err()); // Check that verification with the wrong domain separator fails assert!(output1 .verify( Transcript::new(domain_sep), msg1, &pk1, &mut Transcript::new(b"A different application"), // swap dom-sep &proof1, ) .is_err()); assert!(output2 .verify( Transcript::new(domain_sep), msg2, &pk2, &mut Transcript::new(b"A different application"), // swap dom-sep &proof2, ) .is_err()); }