//! Implementation of [RedJubjub], a specialization of RedDSA to the Jubjub //! curve. //! //! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown}; use ff::{Field, PrimeField, PrimeFieldRepr}; use rand_core::RngCore; use std::io::{self, Read, Write}; use crate::util::hash_to_scalar; fn read_scalar(reader: R) -> io::Result { let mut s_repr = ::Repr::default(); s_repr.read_le(reader)?; E::Fs::from_repr(s_repr) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) } fn write_scalar(s: &E::Fs, writer: W) -> io::Result<()> { s.into_repr().write_le(writer) } fn h_star(a: &[u8], b: &[u8]) -> E::Fs { hash_to_scalar::(b"Zcash_RedJubjubH", a, b) } #[derive(Copy, Clone, Debug)] pub struct Signature { rbar: [u8; 32], sbar: [u8; 32], } pub struct PrivateKey(pub E::Fs); #[derive(Debug)] pub struct PublicKey(pub Point); impl Signature { pub fn read(mut reader: R) -> io::Result { let mut rbar = [0u8; 32]; let mut sbar = [0u8; 32]; reader.read_exact(&mut rbar)?; reader.read_exact(&mut sbar)?; Ok(Signature { rbar, sbar }) } pub fn write(&self, mut writer: W) -> io::Result<()> { writer.write_all(&self.rbar)?; writer.write_all(&self.sbar) } } impl PrivateKey { pub fn randomize(&self, alpha: E::Fs) -> Self { let mut tmp = self.0; tmp.add_assign(&alpha); PrivateKey(tmp) } pub fn read(reader: R) -> io::Result { let pk = read_scalar::(reader)?; Ok(PrivateKey(pk)) } pub fn write(&self, writer: W) -> io::Result<()> { write_scalar::(&self.0, writer) } pub fn sign( &self, msg: &[u8], rng: &mut R, p_g: FixedGenerators, params: &E::Params, ) -> Signature { // T = (l_H + 128) bits of randomness // For H*, l_H = 512 bits let mut t = [0u8; 80]; rng.fill_bytes(&mut t[..]); // r = H*(T || M) let r = h_star::(&t[..], msg); // R = r . P_G let r_g = params.generator(p_g).mul(r, params); let mut rbar = [0u8; 32]; r_g.write(&mut rbar[..]) .expect("Jubjub points should serialize to 32 bytes"); // S = r + H*(Rbar || M) . sk let mut s = h_star::(&rbar[..], msg); s.mul_assign(&self.0); s.add_assign(&r); let mut sbar = [0u8; 32]; write_scalar::(&s, &mut sbar[..]) .expect("Jubjub scalars should serialize to 32 bytes"); Signature { rbar, sbar } } } impl PublicKey { pub fn from_private(privkey: &PrivateKey, p_g: FixedGenerators, params: &E::Params) -> Self { let res = params.generator(p_g).mul(privkey.0, params).into(); PublicKey(res) } pub fn randomize(&self, alpha: E::Fs, p_g: FixedGenerators, params: &E::Params) -> Self { let res: Point = params.generator(p_g).mul(alpha, params).into(); let res = res.add(&self.0, params); PublicKey(res) } pub fn read(reader: R, params: &E::Params) -> io::Result { let p = Point::read(reader, params)?; Ok(PublicKey(p)) } pub fn write(&self, writer: W) -> io::Result<()> { self.0.write(writer) } pub fn verify( &self, msg: &[u8], sig: &Signature, p_g: FixedGenerators, params: &E::Params, ) -> bool { // c = H*(Rbar || M) let c = h_star::(&sig.rbar[..], msg); // Signature checks: // R != invalid let r = match Point::read(&sig.rbar[..], params) { Ok(r) => r, Err(_) => return false, }; // S < order(G) // (E::Fs guarantees its representation is in the field) let s = match read_scalar::(&sig.sbar[..]) { Ok(s) => s, Err(_) => return false, }; // 0 = h_G(-S . P_G + R + c . vk) self.0 .mul(c, params) .add(&r, params) .add( ¶ms.generator(p_g).mul(s, params).negate().into(), params, ) .mul_by_cofactor(params) .eq(&Point::zero()) } } pub struct BatchEntry<'a, E: JubjubEngine> { vk: PublicKey, msg: &'a [u8], sig: Signature, } // TODO: #82: This is a naive implementation currently, // and doesn't use multiexp. pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>( rng: &mut R, batch: &[BatchEntry<'a, E>], p_g: FixedGenerators, params: &E::Params, ) -> bool { let mut acc = Point::::zero(); for entry in batch { let mut r = match Point::::read(&entry.sig.rbar[..], params) { Ok(r) => r, Err(_) => return false, }; let mut s = match read_scalar::(&entry.sig.sbar[..]) { Ok(s) => s, Err(_) => return false, }; let mut c = h_star::(&entry.sig.rbar[..], entry.msg); let z = E::Fs::random(rng); s.mul_assign(&z); s.negate(); r = r.mul(z, params); c.mul_assign(&z); acc = acc.add(&r, params); acc = acc.add(&entry.vk.0.mul(c, params), params); acc = acc.add(¶ms.generator(p_g).mul(s, params).into(), params); } acc = acc.mul_by_cofactor(params).into(); acc.eq(&Point::zero()) } #[cfg(test)] mod tests { use pairing::bls12_381::Bls12; use rand_core::SeedableRng; use rand_xorshift::XorShiftRng; use crate::jubjub::{edwards, fs::Fs, JubjubBls12}; use super::*; #[test] fn test_batch_verify() { let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); let params = &JubjubBls12::new(); let p_g = FixedGenerators::SpendingKeyGenerator; let sk1 = PrivateKey::(Fs::random(rng)); let vk1 = PublicKey::from_private(&sk1, p_g, params); let msg1 = b"Foo bar"; let sig1 = sk1.sign(msg1, rng, p_g, params); assert!(vk1.verify(msg1, &sig1, p_g, params)); let sk2 = PrivateKey::(Fs::random(rng)); let vk2 = PublicKey::from_private(&sk2, p_g, params); let msg2 = b"Foo bar"; let sig2 = sk2.sign(msg2, rng, p_g, params); assert!(vk2.verify(msg2, &sig2, p_g, params)); let mut batch = vec![ BatchEntry { vk: vk1, msg: msg1, sig: sig1, }, BatchEntry { vk: vk2, msg: msg2, sig: sig2, }, ]; assert!(batch_verify(rng, &batch, p_g, params)); batch[0].sig = sig2; assert!(!batch_verify(rng, &batch, p_g, params)); } #[test] fn cofactor_check() { let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); let params = &JubjubBls12::new(); let zero = edwards::Point::zero(); let p_g = FixedGenerators::SpendingKeyGenerator; // Get a point of order 8 let p8 = loop { let r = edwards::Point::::rand(rng, params).mul(Fs::char(), params); let r2 = r.double(params); let r4 = r2.double(params); let r8 = r4.double(params); if r2 != zero && r4 != zero && r8 == zero { break r; } }; let sk = PrivateKey::(Fs::random(rng)); let vk = PublicKey::from_private(&sk, p_g, params); // TODO: This test will need to change when #77 is fixed let msg = b"Foo bar"; let sig = sk.sign(msg, rng, p_g, params); assert!(vk.verify(msg, &sig, p_g, params)); let vktorsion = PublicKey(vk.0.add(&p8, params)); assert!(vktorsion.verify(msg, &sig, p_g, params)); } #[test] fn round_trip_serialization() { let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); let p_g = FixedGenerators::SpendingKeyGenerator; let params = &JubjubBls12::new(); for _ in 0..1000 { let sk = PrivateKey::(Fs::random(rng)); let vk = PublicKey::from_private(&sk, p_g, params); let msg = b"Foo bar"; let sig = sk.sign(msg, rng, p_g, params); let mut sk_bytes = [0u8; 32]; let mut vk_bytes = [0u8; 32]; let mut sig_bytes = [0u8; 64]; sk.write(&mut sk_bytes[..]).unwrap(); vk.write(&mut vk_bytes[..]).unwrap(); sig.write(&mut sig_bytes[..]).unwrap(); let sk_2 = PrivateKey::::read(&sk_bytes[..]).unwrap(); let vk_2 = PublicKey::from_private(&sk_2, p_g, params); let mut vk_2_bytes = [0u8; 32]; vk_2.write(&mut vk_2_bytes[..]).unwrap(); assert!(vk_bytes == vk_2_bytes); let vk_2 = PublicKey::::read(&vk_bytes[..], params).unwrap(); let sig_2 = Signature::read(&sig_bytes[..]).unwrap(); assert!(vk.verify(msg, &sig_2, p_g, params)); assert!(vk_2.verify(msg, &sig, p_g, params)); assert!(vk_2.verify(msg, &sig_2, p_g, params)); } } #[test] fn random_signatures() { let rng = &mut XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, ]); let p_g = FixedGenerators::SpendingKeyGenerator; let params = &JubjubBls12::new(); for _ in 0..1000 { let sk = PrivateKey::(Fs::random(rng)); let vk = PublicKey::from_private(&sk, p_g, params); let msg1 = b"Foo bar"; let msg2 = b"Spam eggs"; let sig1 = sk.sign(msg1, rng, p_g, params); let sig2 = sk.sign(msg2, rng, p_g, params); assert!(vk.verify(msg1, &sig1, p_g, params)); assert!(vk.verify(msg2, &sig2, p_g, params)); assert!(!vk.verify(msg1, &sig2, p_g, params)); assert!(!vk.verify(msg2, &sig1, p_g, params)); let alpha = Fs::random(rng); let rsk = sk.randomize(alpha); let rvk = vk.randomize(alpha, p_g, params); let sig1 = rsk.sign(msg1, rng, p_g, params); let sig2 = rsk.sign(msg2, rng, p_g, params); assert!(rvk.verify(msg1, &sig1, p_g, params)); assert!(rvk.verify(msg2, &sig2, p_g, params)); assert!(!rvk.verify(msg1, &sig2, p_g, params)); assert!(!rvk.verify(msg2, &sig1, p_g, params)); } } }