diff --git a/src/redjubjub.rs b/src/redjubjub.rs index f8c13d4..0fe30e5 100644 --- a/src/redjubjub.rs +++ b/src/redjubjub.rs @@ -1,25 +1,73 @@ //! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve. //! See section 5.4.6 of the Sapling protocol specification. -use pairing::Field; +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; +use util::{hash_to_scalar, swap_bits_u64}; + +fn read_scalar(reader: R) -> io::Result { + let mut s_repr = ::Repr::default(); + + // This reads in big-endian, so we perform a swap of the + // limbs in the representation and swap the bit order. + s_repr.read_be(reader)?; + s_repr.as_mut().reverse(); + for b in s_repr.as_mut() { + *b = swap_bits_u64(*b); + } + + 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<()> { + let mut s_repr = s.into_repr(); + + // This writes in big-endian, so we perform a swap of the + // limbs in the representation and swap the bit order. + s_repr.as_mut().reverse(); + for b in s_repr.as_mut() { + *b = swap_bits_u64(*b); + } + s_repr.write_be(writer) +} fn h_star(a: &[u8], b: &[u8]) -> E::Fs { hash_to_scalar::(b"Zcash_RedJubjubH", a, b) } -pub struct Signature { - r: Point, - s: E::Fs, +pub struct Signature { + rbar: [u8; 32], + sbar: [u8; 32], } pub struct PrivateKey(E::Fs); pub struct PublicKey(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; @@ -27,7 +75,22 @@ impl PrivateKey { PrivateKey(tmp) } - pub fn sign(&self, msg: &[u8], rng: &mut R, params: &E::Params) -> Signature { + 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]; @@ -36,10 +99,8 @@ impl PrivateKey { // r = H*(T || M) let r = h_star::(&t[..], msg); - // R = r . G - let r_g = params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(r, params); + // 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"); @@ -48,47 +109,59 @@ impl PrivateKey { 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 { r: r_g.into(), s } + Signature { rbar, sbar } } } impl PublicKey { - pub fn from_private(privkey: &PrivateKey, params: &E::Params) -> Self { - let res = params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(privkey.0, params) - .into(); + 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, params: &E::Params) -> Self { - let res: Point = params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(alpha, params) - .into(); + 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) } - // Pre-conditions: - // - rbar was the canonical representation of a point on the curve. - // - sig.s < order(G) - // TODO(str4d): Enforce these during deserialization of Signature - pub fn verify(&self, msg: &[u8], sig: &Signature, params: &E::Params) -> bool { - // c = H*(Rbar || M) - let mut rbar = [0u8; 32]; - sig.r - .write(&mut rbar[..]) - .expect("Jubjub points should serialize to 32 bytes"); - let c = h_star::(&rbar[..], msg); + 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) + } - // S . G = R + c . vk - self.0.mul(c, params).add(&sig.r, params) - == params - .generator(FixedGenerators::SpendingKeyGenerator) - .mul(sig.s, params) - .into() + 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, + }; + // S . P_G = R + c . vk + self.0.mul(c, params).add(&r, params) == params.generator(p_g).mul(s, params).into() } } @@ -101,37 +174,71 @@ mod tests { use super::*; + #[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, params); + 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, params); - let sig2 = sk.sign(msg2, rng, params); + let sig1 = sk.sign(msg1, rng, p_g, params); + let sig2 = sk.sign(msg2, rng, p_g, params); - assert!(vk.verify(msg1, &sig1, params)); - assert!(vk.verify(msg2, &sig2, params)); - assert!(!vk.verify(msg1, &sig2, params)); - assert!(!vk.verify(msg2, &sig1, 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, params); + let rvk = vk.randomize(alpha, p_g, params); - let sig1 = rsk.sign(msg1, rng, params); - let sig2 = rsk.sign(msg2, rng, params); + let sig1 = rsk.sign(msg1, rng, p_g, params); + let sig2 = rsk.sign(msg2, rng, p_g, params); - assert!(rvk.verify(msg1, &sig1, params)); - assert!(rvk.verify(msg2, &sig2, params)); - assert!(!rvk.verify(msg1, &sig2, params)); - assert!(!rvk.verify(msg2, &sig1, 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)); } } }