commit
e4175d81e9
|
@ -1,7 +1,10 @@
|
||||||
use pairing::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError, LegendreSymbol};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
|
use pairing::{BitIterator, Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError, LegendreSymbol};
|
||||||
use pairing::LegendreSymbol::*;
|
use pairing::LegendreSymbol::*;
|
||||||
use pairing::{adc, sbb, mac_with_carry};
|
use pairing::{adc, sbb, mac_with_carry};
|
||||||
|
|
||||||
|
use super::ToUniform;
|
||||||
|
|
||||||
// s = 6554484396890773809930967563523245729705921265872317281365359162392183254199
|
// s = 6554484396890773809930967563523245729705921265872317281365359162392183254199
|
||||||
const MODULUS: FsRepr = FsRepr([0xd0970e5ed6f72cb7, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9]);
|
const MODULUS: FsRepr = FsRepr([0xd0970e5ed6f72cb7, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9]);
|
||||||
|
|
||||||
|
@ -548,6 +551,31 @@ impl Fs {
|
||||||
(self.0).0[3] = r7;
|
(self.0).0[3] = r7;
|
||||||
self.reduce();
|
self.reduce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mul_bits<S: AsRef<[u64]>>(&self, bits: BitIterator<S>) -> Self {
|
||||||
|
let mut res = Self::zero();
|
||||||
|
for bit in bits {
|
||||||
|
res.double();
|
||||||
|
|
||||||
|
if bit {
|
||||||
|
res.add_assign(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToUniform for Fs {
|
||||||
|
/// Convert a little endian byte string into a uniform
|
||||||
|
/// field element. The number is reduced mod s. The caller
|
||||||
|
/// is responsible for ensuring the input is 64 bytes of
|
||||||
|
/// Random Oracle output.
|
||||||
|
fn to_uniform(digest: &[u8]) -> Self {
|
||||||
|
assert_eq!(digest.len(), 64);
|
||||||
|
let mut repr: [u64; 8] = [0; 8];
|
||||||
|
LittleEndian::read_u64_into(digest, &mut repr);
|
||||||
|
Self::one().mul_bits(BitIterator::new(repr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SqrtField for Fs {
|
impl SqrtField for Fs {
|
||||||
|
|
|
@ -85,12 +85,16 @@ pub enum FixedGenerators {
|
||||||
Max = 6
|
Max = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ToUniform {
|
||||||
|
fn to_uniform(digest: &[u8]) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
/// This is an extension to the pairing Engine trait which
|
/// This is an extension to the pairing Engine trait which
|
||||||
/// offers a scalar field for the embedded curve (Jubjub)
|
/// offers a scalar field for the embedded curve (Jubjub)
|
||||||
/// and some pre-computed parameters.
|
/// and some pre-computed parameters.
|
||||||
pub trait JubjubEngine: Engine {
|
pub trait JubjubEngine: Engine {
|
||||||
/// The scalar field of the Jubjub curve
|
/// The scalar field of the Jubjub curve
|
||||||
type Fs: PrimeField + SqrtField;
|
type Fs: PrimeField + SqrtField + ToUniform;
|
||||||
/// The parameters of Jubjub and the Sapling protocol
|
/// The parameters of Jubjub and the Sapling protocol
|
||||||
type Params: JubjubParams<Self>;
|
type Params: JubjubParams<Self>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,4 +18,5 @@ pub mod circuit;
|
||||||
pub mod pedersen_hash;
|
pub mod pedersen_hash;
|
||||||
pub mod primitives;
|
pub mod primitives;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod redjubjub;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
//! 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 rand::Rng;
|
||||||
|
|
||||||
|
use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point};
|
||||||
|
use util::hash_to_scalar;
|
||||||
|
|
||||||
|
fn h_star<E: JubjubEngine>(a: &[u8], b: &[u8]) -> E::Fs {
|
||||||
|
hash_to_scalar::<E>(b"Zcash_RedJubjubH", a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Signature<E: JubjubEngine> {
|
||||||
|
r: Point<E, Unknown>,
|
||||||
|
s: E::Fs,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PrivateKey<E: JubjubEngine>(E::Fs);
|
||||||
|
|
||||||
|
pub struct PublicKey<E: JubjubEngine>(Point<E, Unknown>);
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> PrivateKey<E> {
|
||||||
|
pub fn randomize(&self, alpha: E::Fs) -> Self {
|
||||||
|
let mut tmp = self.0;
|
||||||
|
tmp.add_assign(&alpha);
|
||||||
|
PrivateKey(tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign<R: Rng>(&self, msg: &[u8], rng: &mut R, params: &E::Params) -> Signature<E> {
|
||||||
|
// 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::<E>(&t[..], msg);
|
||||||
|
|
||||||
|
// R = r . G
|
||||||
|
let r_g = params
|
||||||
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
|
.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::<E>(&rbar[..], msg);
|
||||||
|
s.mul_assign(&self.0);
|
||||||
|
s.add_assign(&r);
|
||||||
|
|
||||||
|
Signature { r: r_g.into(), s }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> PublicKey<E> {
|
||||||
|
pub fn from_private(privkey: &PrivateKey<E>, params: &E::Params) -> Self {
|
||||||
|
let res = params
|
||||||
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
|
.mul(privkey.0, params)
|
||||||
|
.into();
|
||||||
|
PublicKey(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn randomize(&self, alpha: E::Fs, params: &E::Params) -> Self {
|
||||||
|
let res: Point<E, Unknown> = params
|
||||||
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
|
.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<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);
|
||||||
|
|
||||||
|
// S . G = R + c . vk
|
||||||
|
self.0.mul(c, params).add(&sig.r, params)
|
||||||
|
== params
|
||||||
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
|
.mul(sig.s, params)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
|
use jubjub::JubjubBls12;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn random_signatures() {
|
||||||
|
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 msg1 = b"Foo bar";
|
||||||
|
let msg2 = b"Spam eggs";
|
||||||
|
|
||||||
|
let sig1 = sk.sign(msg1, rng, params);
|
||||||
|
let sig2 = sk.sign(msg2, rng, 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));
|
||||||
|
|
||||||
|
let alpha = rng.gen();
|
||||||
|
let rsk = sk.randomize(alpha);
|
||||||
|
let rvk = vk.randomize(alpha, params);
|
||||||
|
|
||||||
|
let sig1 = rsk.sign(msg1, rng, params);
|
||||||
|
let sig2 = rsk.sign(msg2, rng, 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
src/util.rs
12
src/util.rs
|
@ -1,3 +1,7 @@
|
||||||
|
use blake2_rfc::blake2b::Blake2b;
|
||||||
|
|
||||||
|
use jubjub::{JubjubEngine, ToUniform};
|
||||||
|
|
||||||
pub fn swap_bits_u64(x: u64) -> u64
|
pub fn swap_bits_u64(x: u64) -> u64
|
||||||
{
|
{
|
||||||
let mut tmp = 0;
|
let mut tmp = 0;
|
||||||
|
@ -7,6 +11,14 @@ pub fn swap_bits_u64(x: u64) -> u64
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs {
|
||||||
|
let mut hasher = Blake2b::with_params(64, &[], &[], persona);
|
||||||
|
hasher.update(a);
|
||||||
|
hasher.update(b);
|
||||||
|
let ret = hasher.finalize();
|
||||||
|
E::Fs::to_uniform(ret.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_swap_bits_u64() {
|
fn test_swap_bits_u64() {
|
||||||
assert_eq!(swap_bits_u64(17182120934178543809), 0b1000001100011011110000011000111000101111111001001100111001110111);
|
assert_eq!(swap_bits_u64(17182120934178543809), 0b1000001100011011110000011000111000101111111001001100111001110111);
|
||||||
|
|
Loading…
Reference in New Issue