mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-02-15 03:05:48 +00:00
Implement RedJubjub serialization
Also alters the Signature struct to store Rbar and Sbar instead of R and S, to more closely match the specification.
This commit is contained in:
parent
e4175d81e9
commit
4eab1fc68a
143
src/redjubjub.rs
143
src/redjubjub.rs
@ -1,25 +1,73 @@
|
|||||||
//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve.
|
//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve.
|
||||||
//! See section 5.4.6 of the Sapling protocol specification.
|
//! See section 5.4.6 of the Sapling protocol specification.
|
||||||
|
|
||||||
use pairing::Field;
|
use pairing::{Field, PrimeField, PrimeFieldRepr};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point};
|
use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point};
|
||||||
use util::hash_to_scalar;
|
use util::{hash_to_scalar, swap_bits_u64};
|
||||||
|
|
||||||
|
fn read_scalar<E: JubjubEngine, R: Read>(reader: R) -> io::Result<E::Fs> {
|
||||||
|
let mut s_repr = <E::Fs as PrimeField>::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<E: JubjubEngine, W: Write>(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<E: JubjubEngine>(a: &[u8], b: &[u8]) -> E::Fs {
|
fn h_star<E: JubjubEngine>(a: &[u8], b: &[u8]) -> E::Fs {
|
||||||
hash_to_scalar::<E>(b"Zcash_RedJubjubH", a, b)
|
hash_to_scalar::<E>(b"Zcash_RedJubjubH", a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Signature<E: JubjubEngine> {
|
pub struct Signature {
|
||||||
r: Point<E, Unknown>,
|
rbar: [u8; 32],
|
||||||
s: E::Fs,
|
sbar: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PrivateKey<E: JubjubEngine>(E::Fs);
|
pub struct PrivateKey<E: JubjubEngine>(E::Fs);
|
||||||
|
|
||||||
pub struct PublicKey<E: JubjubEngine>(Point<E, Unknown>);
|
pub struct PublicKey<E: JubjubEngine>(Point<E, Unknown>);
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
|
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<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
writer.write_all(&self.rbar)?;
|
||||||
|
writer.write_all(&self.sbar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: JubjubEngine> PrivateKey<E> {
|
impl<E: JubjubEngine> PrivateKey<E> {
|
||||||
pub fn randomize(&self, alpha: E::Fs) -> Self {
|
pub fn randomize(&self, alpha: E::Fs) -> Self {
|
||||||
let mut tmp = self.0;
|
let mut tmp = self.0;
|
||||||
@ -27,7 +75,16 @@ impl<E: JubjubEngine> PrivateKey<E> {
|
|||||||
PrivateKey(tmp)
|
PrivateKey(tmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign<R: Rng>(&self, msg: &[u8], rng: &mut R, params: &E::Params) -> Signature<E> {
|
pub fn read<R: Read>(reader: R) -> io::Result<Self> {
|
||||||
|
let pk = read_scalar::<E, R>(reader)?;
|
||||||
|
Ok(PrivateKey(pk))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
|
||||||
|
write_scalar::<E, W>(&self.0, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign<R: Rng>(&self, msg: &[u8], rng: &mut R, params: &E::Params) -> Signature {
|
||||||
// T = (l_H + 128) bits of randomness
|
// T = (l_H + 128) bits of randomness
|
||||||
// For H*, l_H = 512 bits
|
// For H*, l_H = 512 bits
|
||||||
let mut t = [0u8; 80];
|
let mut t = [0u8; 80];
|
||||||
@ -48,8 +105,11 @@ impl<E: JubjubEngine> PrivateKey<E> {
|
|||||||
let mut s = h_star::<E>(&rbar[..], msg);
|
let mut s = h_star::<E>(&rbar[..], msg);
|
||||||
s.mul_assign(&self.0);
|
s.mul_assign(&self.0);
|
||||||
s.add_assign(&r);
|
s.add_assign(&r);
|
||||||
|
let mut sbar = [0u8; 32];
|
||||||
|
write_scalar::<E, &mut [u8]>(&s, &mut sbar[..])
|
||||||
|
.expect("Jubjub scalars should serialize to 32 bytes");
|
||||||
|
|
||||||
Signature { r: r_g.into(), s }
|
Signature { rbar, sbar }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,23 +131,34 @@ impl<E: JubjubEngine> PublicKey<E> {
|
|||||||
PublicKey(res)
|
PublicKey(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-conditions:
|
pub fn read<R: Read>(reader: R, params: &E::Params) -> io::Result<Self> {
|
||||||
// - rbar was the canonical representation of a point on the curve.
|
let p = Point::read(reader, params)?;
|
||||||
// - sig.s < order(G)
|
Ok(PublicKey(p))
|
||||||
// TODO(str4d): Enforce these during deserialization of Signature
|
}
|
||||||
pub fn verify(&self, msg: &[u8], sig: &Signature<E>, 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::<E>(&rbar[..], msg);
|
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
|
||||||
|
self.0.write(writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify(&self, msg: &[u8], sig: &Signature, params: &E::Params) -> bool {
|
||||||
|
// c = H*(Rbar || M)
|
||||||
|
let c = h_star::<E>(&sig.rbar[..], msg);
|
||||||
|
|
||||||
|
let r = match Point::read(&sig.rbar[..], params) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
let s = match read_scalar::<E, &[u8]>(&sig.sbar[..]) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
// S < order(G)
|
||||||
|
s.into_repr() < E::Fs::char() &&
|
||||||
// S . G = R + c . vk
|
// S . G = R + c . vk
|
||||||
self.0.mul(c, params).add(&sig.r, params)
|
self.0.mul(c, params).add(&r, params)
|
||||||
== params
|
== params
|
||||||
.generator(FixedGenerators::SpendingKeyGenerator)
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
.mul(sig.s, params)
|
.mul(s, params)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,6 +172,38 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn round_trip_serialization() {
|
||||||
|
let rng = &mut thread_rng();
|
||||||
|
let params = &JubjubBls12::new();
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let sk = PrivateKey::<Bls12>(rng.gen());
|
||||||
|
let vk = PublicKey::from_private(&sk, params);
|
||||||
|
let msg = b"Foo bar";
|
||||||
|
let sig = sk.sign(msg, rng, 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::<Bls12>::read(&sk_bytes[..]).unwrap();
|
||||||
|
let vk_2 = PublicKey::from_private(&sk_2, 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::<Bls12>::read(&vk_bytes[..], params).unwrap();
|
||||||
|
let sig_2 = Signature::read(&sig_bytes[..]).unwrap();
|
||||||
|
assert!(vk.verify(msg, &sig_2, params));
|
||||||
|
assert!(vk_2.verify(msg, &sig, params));
|
||||||
|
assert!(vk_2.verify(msg, &sig_2, params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn random_signatures() {
|
fn random_signatures() {
|
||||||
let rng = &mut thread_rng();
|
let rng = &mut thread_rng();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user