//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve. //! See section 5.4.6 of the Sapling protocol specification. use pairing::{Field, PrimeField, PrimeFieldRepr}; use rand::Rng; use std::io::{self, Read, Write}; use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point}; use util::{hash_to_scalar}; fn read_scalar(reader: R) -> io::Result { let mut s_repr = ::Repr::default(); s_repr.read_le(reader)?; match E::Fs::from_repr(s_repr) { Ok(s) => Ok(s), Err(_) => 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) } pub struct Signature { rbar: [u8; 32], sbar: [u8; 32], } pub struct PrivateKey(pub E::Fs); 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 = 8(-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()) } } #[cfg(test)] mod tests { use pairing::bls12_381::Bls12; use rand::thread_rng; use jubjub::{JubjubBls12, fs::Fs, edwards}; use super::*; #[test] fn cofactor_check() { let rng = &mut thread_rng(); let params = &JubjubBls12::new(); let inf = edwards::Point::zero(); let p_g = FixedGenerators::SpendingKeyGenerator; 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 != inf && r4 != inf && r8 == inf { break r; } }; let sk = PrivateKey::(rng.gen()); let vk = PublicKey::from_private(&sk, p_g, params); 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 thread_rng(); let p_g = FixedGenerators::SpendingKeyGenerator; let params = &JubjubBls12::new(); for _ in 0..1000 { let sk = PrivateKey::(rng.gen()); 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 thread_rng(); let p_g = FixedGenerators::SpendingKeyGenerator; let params = &JubjubBls12::new(); for _ in 0..1000 { let sk = PrivateKey::(rng.gen()); 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 = rng.gen(); 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)); } } }