Introduce SpendAuth: SigType and Binding: SigType traits
The prior `SpendAuth` and `Binding` enums have been renamed to `sapling::{SpendAuth, Binding}`. These might subsequently be removed from the crate entirely (moving into a wrapping `redjubjub` crate). The code assumes that scalar and point representations are [u8; 32], which will be the case for all curves we instantiate RedDSA with for Zcash.
This commit is contained in:
parent
7e80588550
commit
878dd1351b
|
@ -26,6 +26,7 @@ features = ["nightly"]
|
||||||
blake2b_simd = "0.5"
|
blake2b_simd = "0.5"
|
||||||
byteorder = "1.4"
|
byteorder = "1.4"
|
||||||
digest = "0.9"
|
digest = "0.9"
|
||||||
|
group = "0.11"
|
||||||
jubjub = "0.8"
|
jubjub = "0.8"
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
serde = { version = "1", optional = true, features = ["derive"] }
|
serde = { version = "1", optional = true, features = ["derive"] }
|
||||||
|
|
11
README.md
11
README.md
|
@ -1,15 +1,18 @@
|
||||||
A minimal [RedDSA][reddsa] implementation for use in Zcash.
|
A minimal [RedDSA][reddsa] implementation for use in Zcash.
|
||||||
|
|
||||||
Two parameterizations of RedJubjub are used in Zcash, one for
|
Two specializations of RedDSA are used in Zcash: RedJubjub and
|
||||||
|
RedPallas. For each of these, two parameterizations are used, one for
|
||||||
`BindingSig` and one for `SpendAuthSig`. This library distinguishes
|
`BindingSig` and one for `SpendAuthSig`. This library distinguishes
|
||||||
these in the type system, using the [sealed] `SigType` trait as a
|
these in the type system, using the [sealed] `SigType` trait as a
|
||||||
type-level enum.
|
type-level enum.
|
||||||
|
|
||||||
In addition to the `Signature`, `SigningKey`, `VerificationKey` types,
|
In addition to the `Signature`, `SigningKey`, `VerificationKey` types,
|
||||||
the library also provides `VerificationKeyBytes`, a [refinement] of a
|
the library also provides `VerificationKeyBytes`, a [refinement] of a
|
||||||
`[u8; 32]` indicating that bytes represent an encoding of a RedJubjub
|
`[u8; 32]` indicating that bytes represent an encoding of a RedDSA
|
||||||
verification key. This allows the `VerificationKey` type to cache
|
verification key. This allows the `VerificationKey` type to cache
|
||||||
verification checks related to the verification key encoding.
|
verification checks related to the verification key encoding.
|
||||||
|
For all specializations of RedDSA used in Zcash, encodings of signing
|
||||||
|
and verification keys are 32 bytes.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
@ -24,7 +27,7 @@ use reddsa::*;
|
||||||
let msg = b"Hello!";
|
let msg = b"Hello!";
|
||||||
|
|
||||||
// Generate a secret key and sign the message
|
// Generate a secret key and sign the message
|
||||||
let sk = SigningKey::<Binding>::new(thread_rng());
|
let sk = SigningKey::<sapling::Binding>::new(thread_rng());
|
||||||
let sig = sk.sign(thread_rng(), msg);
|
let sig = sk.sign(thread_rng(), msg);
|
||||||
|
|
||||||
// Types can be converted to raw byte arrays using From/Into
|
// Types can be converted to raw byte arrays using From/Into
|
||||||
|
@ -32,7 +35,7 @@ let sig_bytes: [u8; 64] = sig.into();
|
||||||
let pk_bytes: [u8; 32] = VerificationKey::from(&sk).into();
|
let pk_bytes: [u8; 32] = VerificationKey::from(&sk).into();
|
||||||
|
|
||||||
// Deserialize and verify the signature.
|
// Deserialize and verify the signature.
|
||||||
let sig: Signature<Binding> = sig_bytes.into();
|
let sig: Signature<sapling::Binding> = sig_bytes.into();
|
||||||
assert!(
|
assert!(
|
||||||
VerificationKey::try_from(pk_bytes)
|
VerificationKey::try_from(pk_bytes)
|
||||||
.and_then(|pk| pk.verify(msg, &sig))
|
.and_then(|pk| pk.verify(msg, &sig))
|
||||||
|
|
|
@ -6,12 +6,12 @@ use std::convert::TryFrom;
|
||||||
|
|
||||||
enum Item {
|
enum Item {
|
||||||
SpendAuth {
|
SpendAuth {
|
||||||
vk_bytes: VerificationKeyBytes<SpendAuth>,
|
vk_bytes: VerificationKeyBytes<sapling::SpendAuth>,
|
||||||
sig: Signature<SpendAuth>,
|
sig: Signature<sapling::SpendAuth>,
|
||||||
},
|
},
|
||||||
Binding {
|
Binding {
|
||||||
vk_bytes: VerificationKeyBytes<Binding>,
|
vk_bytes: VerificationKeyBytes<sapling::Binding>,
|
||||||
sig: Signature<Binding>,
|
sig: Signature<sapling::Binding>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,13 +21,13 @@ fn sigs_with_distinct_keys() -> impl Iterator<Item = Item> {
|
||||||
let msg = b"Bench";
|
let msg = b"Bench";
|
||||||
match rng.gen::<u8>() % 2 {
|
match rng.gen::<u8>() % 2 {
|
||||||
0 => {
|
0 => {
|
||||||
let sk = SigningKey::<SpendAuth>::new(thread_rng());
|
let sk = SigningKey::<sapling::SpendAuth>::new(thread_rng());
|
||||||
let vk_bytes = VerificationKey::from(&sk).into();
|
let vk_bytes = VerificationKey::from(&sk).into();
|
||||||
let sig = sk.sign(thread_rng(), &msg[..]);
|
let sig = sk.sign(thread_rng(), &msg[..]);
|
||||||
Item::SpendAuth { vk_bytes, sig }
|
Item::SpendAuth { vk_bytes, sig }
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let sk = SigningKey::<Binding>::new(thread_rng());
|
let sk = SigningKey::<sapling::Binding>::new(thread_rng());
|
||||||
let vk_bytes = VerificationKey::from(&sk).into();
|
let vk_bytes = VerificationKey::from(&sk).into();
|
||||||
let sig = sk.sign(thread_rng(), &msg[..]);
|
let sig = sk.sign(thread_rng(), &msg[..]);
|
||||||
Item::Binding { vk_bytes, sig }
|
Item::Binding { vk_bytes, sig }
|
||||||
|
@ -76,10 +76,10 @@ fn bench_batch_verify(c: &mut Criterion) {
|
||||||
let msg = b"Bench";
|
let msg = b"Bench";
|
||||||
match item {
|
match item {
|
||||||
Item::SpendAuth { vk_bytes, sig } => {
|
Item::SpendAuth { vk_bytes, sig } => {
|
||||||
batch.queue((*vk_bytes, *sig, msg));
|
batch.queue(batch::Item::from_spendauth(*vk_bytes, *sig, msg));
|
||||||
}
|
}
|
||||||
Item::Binding { vk_bytes, sig } => {
|
Item::Binding { vk_bytes, sig } => {
|
||||||
batch.queue((*vk_bytes, *sig, msg));
|
batch.queue(batch::Item::from_binding(*vk_bytes, *sig, msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
117
src/batch.rs
117
src/batch.rs
|
@ -8,7 +8,7 @@
|
||||||
// - Deirdre Connolly <deirdre@zfnd.org>
|
// - Deirdre Connolly <deirdre@zfnd.org>
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
//! Performs batch RedJubjub signature verification.
|
//! Performs batch RedDSA signature verification.
|
||||||
//!
|
//!
|
||||||
//! Batch verification asks whether *all* signatures in some set are valid,
|
//! Batch verification asks whether *all* signatures in some set are valid,
|
||||||
//! rather than asking whether *each* of them is valid. This allows sharing
|
//! rather than asking whether *each* of them is valid. This allows sharing
|
||||||
|
@ -20,10 +20,14 @@
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use jubjub::*;
|
use group::{
|
||||||
|
cofactor::CofactorGroup,
|
||||||
|
ff::{Field, PrimeField},
|
||||||
|
GroupEncoding,
|
||||||
|
};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use crate::{private::Sealed, scalar_mul::VartimeMultiscalarMul, *};
|
use crate::{private::SealedScalar, scalar_mul::VartimeMultiscalarMul, *};
|
||||||
|
|
||||||
// Shim to generate a random 128bit value in a [u64; 4], without
|
// Shim to generate a random 128bit value in a [u64; 4], without
|
||||||
// importing `rand`.
|
// importing `rand`.
|
||||||
|
@ -35,16 +39,16 @@ fn gen_128_bits<R: RngCore + CryptoRng>(mut rng: R) -> [u64; 4] {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Inner {
|
enum Inner<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
|
||||||
SpendAuth {
|
SpendAuth {
|
||||||
vk_bytes: VerificationKeyBytes<SpendAuth>,
|
vk_bytes: VerificationKeyBytes<S>,
|
||||||
sig: Signature<SpendAuth>,
|
sig: Signature<S>,
|
||||||
c: Scalar,
|
c: S::Scalar,
|
||||||
},
|
},
|
||||||
Binding {
|
Binding {
|
||||||
vk_bytes: VerificationKeyBytes<Binding>,
|
vk_bytes: VerificationKeyBytes<B>,
|
||||||
sig: Signature<Binding>,
|
sig: Signature<B>,
|
||||||
c: Scalar,
|
c: B::Scalar,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,26 +58,19 @@ enum Inner {
|
||||||
/// lifetime of the message. This is useful when using the batch verification API
|
/// lifetime of the message. This is useful when using the batch verification API
|
||||||
/// in an async context.
|
/// in an async context.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Item {
|
pub struct Item<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
|
||||||
inner: Inner,
|
inner: Inner<S, B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'msg, M: AsRef<[u8]>>
|
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Item<S, B> {
|
||||||
From<(
|
/// Create a batch item from a `SpendAuth` signature.
|
||||||
VerificationKeyBytes<SpendAuth>,
|
pub fn from_spendauth<'msg, M: AsRef<[u8]>>(
|
||||||
Signature<SpendAuth>,
|
vk_bytes: VerificationKeyBytes<S>,
|
||||||
&'msg M,
|
sig: Signature<S>,
|
||||||
)> for Item
|
msg: &'msg M,
|
||||||
{
|
|
||||||
fn from(
|
|
||||||
(vk_bytes, sig, msg): (
|
|
||||||
VerificationKeyBytes<SpendAuth>,
|
|
||||||
Signature<SpendAuth>,
|
|
||||||
&'msg M,
|
|
||||||
),
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Compute c now to avoid dependency on the msg lifetime.
|
// Compute c now to avoid dependency on the msg lifetime.
|
||||||
let c = HStar::default()
|
let c = HStar::<S>::default()
|
||||||
.update(&sig.r_bytes[..])
|
.update(&sig.r_bytes[..])
|
||||||
.update(&vk_bytes.bytes[..])
|
.update(&vk_bytes.bytes[..])
|
||||||
.update(msg)
|
.update(msg)
|
||||||
|
@ -82,16 +79,15 @@ impl<'msg, M: AsRef<[u8]>>
|
||||||
inner: Inner::SpendAuth { vk_bytes, sig, c },
|
inner: Inner::SpendAuth { vk_bytes, sig, c },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'msg, M: AsRef<[u8]>> From<(VerificationKeyBytes<Binding>, Signature<Binding>, &'msg M)>
|
/// Create a batch item from a `Binding` signature.
|
||||||
for Item
|
pub fn from_binding<'msg, M: AsRef<[u8]>>(
|
||||||
{
|
vk_bytes: VerificationKeyBytes<B>,
|
||||||
fn from(
|
sig: Signature<B>,
|
||||||
(vk_bytes, sig, msg): (VerificationKeyBytes<Binding>, Signature<Binding>, &'msg M),
|
msg: &'msg M,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Compute c now to avoid dependency on the msg lifetime.
|
// Compute c now to avoid dependency on the msg lifetime.
|
||||||
let c = HStar::default()
|
let c = HStar::<B>::default()
|
||||||
.update(&sig.r_bytes[..])
|
.update(&sig.r_bytes[..])
|
||||||
.update(&vk_bytes.bytes[..])
|
.update(&vk_bytes.bytes[..])
|
||||||
.update(msg)
|
.update(msg)
|
||||||
|
@ -100,9 +96,7 @@ impl<'msg, M: AsRef<[u8]>> From<(VerificationKeyBytes<Binding>, Signature<Bindin
|
||||||
inner: Inner::Binding { vk_bytes, sig, c },
|
inner: Inner::Binding { vk_bytes, sig, c },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Item {
|
|
||||||
/// Perform non-batched verification of this `Item`.
|
/// Perform non-batched verification of this `Item`.
|
||||||
///
|
///
|
||||||
/// This is useful (in combination with `Item::clone`) for implementing fallback
|
/// This is useful (in combination with `Item::clone`) for implementing fallback
|
||||||
|
@ -113,31 +107,36 @@ impl Item {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn verify_single(self) -> Result<(), Error> {
|
pub fn verify_single(self) -> Result<(), Error> {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Inner::Binding { vk_bytes, sig, c } => VerificationKey::<Binding>::try_from(vk_bytes)
|
Inner::Binding { vk_bytes, sig, c } => {
|
||||||
.and_then(|vk| vk.verify_prehashed(&sig, c)),
|
VerificationKey::<B>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
|
||||||
|
}
|
||||||
Inner::SpendAuth { vk_bytes, sig, c } => {
|
Inner::SpendAuth { vk_bytes, sig, c } => {
|
||||||
VerificationKey::<SpendAuth>::try_from(vk_bytes)
|
VerificationKey::<S>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
|
||||||
.and_then(|vk| vk.verify_prehashed(&sig, c))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
/// A batch verification context.
|
/// A batch verification context.
|
||||||
pub struct Verifier {
|
pub struct Verifier<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
|
||||||
/// Signature data queued for verification.
|
/// Signature data queued for verification.
|
||||||
signatures: Vec<Item>,
|
signatures: Vec<Item<S, B>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Verifier {
|
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Default for Verifier<S, B> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Verifier { signatures: vec![] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Verifier<S, B> {
|
||||||
/// Construct a new batch verifier.
|
/// Construct a new batch verifier.
|
||||||
pub fn new() -> Verifier {
|
pub fn new() -> Verifier<S, B> {
|
||||||
Verifier::default()
|
Verifier::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queue an Item for verification.
|
/// Queue an Item for verification.
|
||||||
pub fn queue<I: Into<Item>>(&mut self, item: I) {
|
pub fn queue<I: Into<Item<S, B>>>(&mut self, item: I) {
|
||||||
self.signatures.push(item.into());
|
self.signatures.push(item.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +162,7 @@ impl Verifier {
|
||||||
/// - h_G is the cofactor of the group;
|
/// - h_G is the cofactor of the group;
|
||||||
/// - P_G is the generator of the subgroup;
|
/// - P_G is the generator of the subgroup;
|
||||||
///
|
///
|
||||||
/// Since RedJubjub uses different subgroups for different types
|
/// Since RedDSA uses different subgroups for different types
|
||||||
/// of signatures, SpendAuth's and Binding's, we need to have yet
|
/// of signatures, SpendAuth's and Binding's, we need to have yet
|
||||||
/// another point and associated scalar accumulator for all the
|
/// another point and associated scalar accumulator for all the
|
||||||
/// signatures of each type in our batch, but we can still
|
/// signatures of each type in our batch, but we can still
|
||||||
|
@ -185,8 +184,8 @@ impl Verifier {
|
||||||
let mut VKs = Vec::with_capacity(n);
|
let mut VKs = Vec::with_capacity(n);
|
||||||
let mut R_coeffs = Vec::with_capacity(self.signatures.len());
|
let mut R_coeffs = Vec::with_capacity(self.signatures.len());
|
||||||
let mut Rs = Vec::with_capacity(self.signatures.len());
|
let mut Rs = Vec::with_capacity(self.signatures.len());
|
||||||
let mut P_spendauth_coeff = Scalar::zero();
|
let mut P_spendauth_coeff = S::Scalar::zero();
|
||||||
let mut P_binding_coeff = Scalar::zero();
|
let mut P_binding_coeff = B::Scalar::zero();
|
||||||
|
|
||||||
for item in self.signatures.iter() {
|
for item in self.signatures.iter() {
|
||||||
let (s_bytes, r_bytes, c) = match item.inner {
|
let (s_bytes, r_bytes, c) = match item.inner {
|
||||||
|
@ -196,7 +195,9 @@ impl Verifier {
|
||||||
|
|
||||||
let s = {
|
let s = {
|
||||||
// XXX-jubjub: should not use CtOption here
|
// XXX-jubjub: should not use CtOption here
|
||||||
let maybe_scalar = Scalar::from_bytes(&s_bytes);
|
let mut repr = <S::Scalar as PrimeField>::Repr::default();
|
||||||
|
repr.as_mut().copy_from_slice(&s_bytes);
|
||||||
|
let maybe_scalar = S::Scalar::from_repr(repr);
|
||||||
if maybe_scalar.is_some().into() {
|
if maybe_scalar.is_some().into() {
|
||||||
maybe_scalar.unwrap()
|
maybe_scalar.unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
@ -207,9 +208,11 @@ impl Verifier {
|
||||||
let R = {
|
let R = {
|
||||||
// XXX-jubjub: should not use CtOption here
|
// XXX-jubjub: should not use CtOption here
|
||||||
// XXX-jubjub: inconsistent ownership in from_bytes
|
// XXX-jubjub: inconsistent ownership in from_bytes
|
||||||
let maybe_point = AffinePoint::from_bytes(r_bytes);
|
let mut repr = <S::Point as GroupEncoding>::Repr::default();
|
||||||
|
repr.as_mut().copy_from_slice(&r_bytes);
|
||||||
|
let maybe_point = S::Point::from_bytes(&repr);
|
||||||
if maybe_point.is_some().into() {
|
if maybe_point.is_some().into() {
|
||||||
jubjub::ExtendedPoint::from(maybe_point.unwrap())
|
maybe_point.unwrap()
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::InvalidSignature);
|
return Err(Error::InvalidSignature);
|
||||||
}
|
}
|
||||||
|
@ -217,14 +220,14 @@ impl Verifier {
|
||||||
|
|
||||||
let VK = match item.inner {
|
let VK = match item.inner {
|
||||||
Inner::SpendAuth { vk_bytes, .. } => {
|
Inner::SpendAuth { vk_bytes, .. } => {
|
||||||
VerificationKey::<SpendAuth>::try_from(vk_bytes.bytes)?.point
|
VerificationKey::<S>::try_from(vk_bytes.bytes)?.point
|
||||||
}
|
}
|
||||||
Inner::Binding { vk_bytes, .. } => {
|
Inner::Binding { vk_bytes, .. } => {
|
||||||
VerificationKey::<Binding>::try_from(vk_bytes.bytes)?.point
|
VerificationKey::<B>::try_from(vk_bytes.bytes)?.point
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let z = Scalar::from_raw(gen_128_bits(&mut rng));
|
let z = S::Scalar::from_raw(gen_128_bits(&mut rng));
|
||||||
|
|
||||||
let P_coeff = z * s;
|
let P_coeff = z * s;
|
||||||
match item.inner {
|
match item.inner {
|
||||||
|
@ -239,7 +242,7 @@ impl Verifier {
|
||||||
R_coeffs.push(z);
|
R_coeffs.push(z);
|
||||||
Rs.push(R);
|
Rs.push(R);
|
||||||
|
|
||||||
VK_coeffs.push(Scalar::zero() + (z * c));
|
VK_coeffs.push(S::Scalar::zero() + (z * c));
|
||||||
VKs.push(VK);
|
VKs.push(VK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,10 +253,10 @@ impl Verifier {
|
||||||
.chain(VK_coeffs.iter())
|
.chain(VK_coeffs.iter())
|
||||||
.chain(R_coeffs.iter());
|
.chain(R_coeffs.iter());
|
||||||
|
|
||||||
let basepoints = [SpendAuth::basepoint(), Binding::basepoint()];
|
let basepoints = [S::basepoint(), B::basepoint()];
|
||||||
let points = basepoints.iter().chain(VKs.iter()).chain(Rs.iter());
|
let points = basepoints.iter().chain(VKs.iter()).chain(Rs.iter());
|
||||||
|
|
||||||
let check = ExtendedPoint::vartime_multiscalar_mul(scalars, points);
|
let check = S::Point::vartime_multiscalar_mul(scalars, points);
|
||||||
|
|
||||||
if check.is_small_order().into() {
|
if check.is_small_order().into() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// An error related to RedJubJub signatures.
|
/// An error related to RedDSA signatures.
|
||||||
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// The encoding of a signing key was malformed.
|
/// The encoding of a signing key was malformed.
|
||||||
|
|
294
src/frost.rs
294
src/frost.rs
|
@ -23,36 +23,43 @@
|
||||||
//! Internally, keygen_with_dealer generates keys using Verifiable Secret
|
//! Internally, keygen_with_dealer generates keys using Verifiable Secret
|
||||||
//! Sharing, where shares are generated using Shamir Secret Sharing.
|
//! Sharing, where shares are generated using Shamir Secret Sharing.
|
||||||
|
|
||||||
use std::{collections::HashMap, convert::TryFrom, marker::PhantomData};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
use jubjub::Scalar;
|
use group::{
|
||||||
|
cofactor::CofactorCurve,
|
||||||
|
ff::{Field, PrimeField},
|
||||||
|
Curve, Group, GroupEncoding,
|
||||||
|
};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
use zeroize::DefaultIsZeroes;
|
use zeroize::DefaultIsZeroes;
|
||||||
|
|
||||||
use crate::private::Sealed;
|
use crate::{private::SealedScalar, sapling, HStar, Signature, SpendAuth, VerificationKey};
|
||||||
use crate::{HStar, Signature, SpendAuth, VerificationKey};
|
|
||||||
|
|
||||||
/// A secret scalar value representing a single signer's secret key.
|
/// A secret scalar value representing a single signer's secret key.
|
||||||
#[derive(Clone, Copy, Default, PartialEq)]
|
#[derive(Clone, Copy, Default, PartialEq)]
|
||||||
pub struct Secret(pub(crate) Scalar);
|
pub struct Secret<S: SpendAuth>(pub(crate) S::Scalar);
|
||||||
|
|
||||||
// Zeroizes `Secret` to be the `Default` value on drop (when it goes out of
|
// Zeroizes `Secret` to be the `Default` value on drop (when it goes out of
|
||||||
// scope). Luckily the derived `Default` includes the `Default` impl of
|
// scope). Luckily the derived `Default` includes the `Default` impl of
|
||||||
// jubjub::Fr/Scalar, which is four 0u64's under the hood.
|
// jubjub::Fr/Scalar, which is four 0u64's under the hood.
|
||||||
impl DefaultIsZeroes for Secret {}
|
impl<S: SpendAuth> DefaultIsZeroes for Secret<S> {}
|
||||||
|
|
||||||
impl From<Scalar> for Secret {
|
impl From<jubjub::Scalar> for Secret<sapling::SpendAuth> {
|
||||||
fn from(source: Scalar) -> Secret {
|
fn from(source: jubjub::Scalar) -> Secret<sapling::SpendAuth> {
|
||||||
Secret(source)
|
Secret(source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A public group element that represents a single signer's public key.
|
/// A public group element that represents a single signer's public key.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct Public(jubjub::ExtendedPoint);
|
pub struct Public<S: SpendAuth>(S::Point);
|
||||||
|
|
||||||
impl From<jubjub::ExtendedPoint> for Public {
|
impl From<jubjub::ExtendedPoint> for Public<sapling::SpendAuth> {
|
||||||
fn from(source: jubjub::ExtendedPoint) -> Public {
|
fn from(source: jubjub::ExtendedPoint) -> Public<sapling::SpendAuth> {
|
||||||
Public(source)
|
Public(source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,12 +68,12 @@ impl From<jubjub::ExtendedPoint> for Public {
|
||||||
/// n is the total number of shares and t is the threshold required to
|
/// n is the total number of shares and t is the threshold required to
|
||||||
/// reconstruct the secret; in this case we use Shamir's secret sharing.
|
/// reconstruct the secret; in this case we use Shamir's secret sharing.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Share {
|
pub struct Share<S: SpendAuth> {
|
||||||
receiver_index: u64,
|
receiver_index: u64,
|
||||||
/// Secret Key.
|
/// Secret Key.
|
||||||
pub(crate) value: Secret,
|
pub(crate) value: Secret<S>,
|
||||||
/// The commitments to be distributed among signers.
|
/// The commitments to be distributed among signers.
|
||||||
pub(crate) commitment: ShareCommitment,
|
pub(crate) commitment: ShareCommitment<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Jubjub point that is a commitment to one coefficient of our secret
|
/// A Jubjub point that is a commitment to one coefficient of our secret
|
||||||
|
@ -75,7 +82,7 @@ pub struct Share {
|
||||||
/// This is a (public) commitment to one coefficient of a secret polynomial used
|
/// This is a (public) commitment to one coefficient of a secret polynomial used
|
||||||
/// for performing verifiable secret sharing for a Shamir secret share.
|
/// for performing verifiable secret sharing for a Shamir secret share.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub(crate) struct Commitment(pub(crate) jubjub::AffinePoint);
|
pub(crate) struct Commitment<S: SpendAuth>(pub(crate) <S::Point as CofactorCurve>::Affine);
|
||||||
|
|
||||||
/// Contains the commitments to the coefficients for our secret polynomial _f_,
|
/// Contains the commitments to the coefficients for our secret polynomial _f_,
|
||||||
/// used to generate participants' key shares.
|
/// used to generate participants' key shares.
|
||||||
|
@ -90,30 +97,30 @@ pub(crate) struct Commitment(pub(crate) jubjub::AffinePoint);
|
||||||
/// some agreed-upon public location for publication, where each participant can
|
/// some agreed-upon public location for publication, where each participant can
|
||||||
/// ensure that they received the correct (and same) value.
|
/// ensure that they received the correct (and same) value.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ShareCommitment(pub(crate) Vec<Commitment>);
|
pub struct ShareCommitment<S: SpendAuth>(pub(crate) Vec<Commitment<S>>);
|
||||||
|
|
||||||
/// The product of all signers' individual commitments, published as part of the
|
/// The product of all signers' individual commitments, published as part of the
|
||||||
/// final signature.
|
/// final signature.
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct GroupCommitment(pub(crate) jubjub::AffinePoint);
|
pub struct GroupCommitment<S: SpendAuth>(pub(crate) <S::Point as CofactorCurve>::Affine);
|
||||||
|
|
||||||
/// Secret and public key material generated by a dealer performing
|
/// Secret and public key material generated by a dealer performing
|
||||||
/// [`keygen_with_dealer`].
|
/// [`keygen_with_dealer`].
|
||||||
///
|
///
|
||||||
/// To derive a FROST keypair, the receiver of the [`SharePackage`] *must* call
|
/// To derive a FROST keypair, the receiver of the [`SharePackage`] *must* call
|
||||||
/// .into(), which under the hood also performs validation.
|
/// .into(), which under the hood also performs validation.
|
||||||
pub struct SharePackage {
|
pub struct SharePackage<S: SpendAuth> {
|
||||||
/// The public signing key that represents the entire group.
|
/// The public signing key that represents the entire group.
|
||||||
pub(crate) group_public: VerificationKey<SpendAuth>,
|
pub(crate) group_public: VerificationKey<S>,
|
||||||
/// Denotes the participant index each share is owned by.
|
/// Denotes the participant index each share is owned by.
|
||||||
pub index: u64,
|
pub index: u64,
|
||||||
/// This participant's public key.
|
/// This participant's public key.
|
||||||
pub(crate) public: Public,
|
pub(crate) public: Public<S>,
|
||||||
/// This participant's share.
|
/// This participant's share.
|
||||||
pub(crate) share: Share,
|
pub(crate) share: Share<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<SharePackage> for KeyPackage {
|
impl<S: SpendAuth> TryFrom<SharePackage<S>> for KeyPackage<S> {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
/// Tries to verify a share and construct a [`KeyPackage`] from it.
|
/// Tries to verify a share and construct a [`KeyPackage`] from it.
|
||||||
|
@ -124,7 +131,7 @@ impl TryFrom<SharePackage> for KeyPackage {
|
||||||
/// every participant has the same view of the commitment issued by the
|
/// every participant has the same view of the commitment issued by the
|
||||||
/// dealer, but implementations *MUST* make sure that all participants have
|
/// dealer, but implementations *MUST* make sure that all participants have
|
||||||
/// a consistent view of this commitment in practice.
|
/// a consistent view of this commitment in practice.
|
||||||
fn try_from(sharepackage: SharePackage) -> Result<Self, &'static str> {
|
fn try_from(sharepackage: SharePackage<S>) -> Result<Self, &'static str> {
|
||||||
verify_share(&sharepackage.share)?;
|
verify_share(&sharepackage.share)?;
|
||||||
|
|
||||||
Ok(KeyPackage {
|
Ok(KeyPackage {
|
||||||
|
@ -143,25 +150,25 @@ impl TryFrom<SharePackage> for KeyPackage {
|
||||||
/// participants, who then perform verification, before deriving
|
/// participants, who then perform verification, before deriving
|
||||||
/// [`KeyPackage`]s, which they store to later use during signing.
|
/// [`KeyPackage`]s, which they store to later use during signing.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct KeyPackage {
|
pub struct KeyPackage<S: SpendAuth> {
|
||||||
index: u64,
|
index: u64,
|
||||||
secret_share: Secret,
|
secret_share: Secret<S>,
|
||||||
public: Public,
|
public: Public<S>,
|
||||||
group_public: VerificationKey<SpendAuth>,
|
group_public: VerificationKey<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Public data that contains all the signer's public keys as well as the
|
/// Public data that contains all the signer's public keys as well as the
|
||||||
/// group public key.
|
/// group public key.
|
||||||
///
|
///
|
||||||
/// Used for verification purposes before publishing a signature.
|
/// Used for verification purposes before publishing a signature.
|
||||||
pub struct PublicKeyPackage {
|
pub struct PublicKeyPackage<S: SpendAuth> {
|
||||||
/// When performing signing, the coordinator must ensure that they have the
|
/// When performing signing, the coordinator must ensure that they have the
|
||||||
/// correct view of participant's public keys to perform verification before
|
/// correct view of participant's public keys to perform verification before
|
||||||
/// publishing a signature. signer_pubkeys represents all signers for a
|
/// publishing a signature. signer_pubkeys represents all signers for a
|
||||||
/// signing operation.
|
/// signing operation.
|
||||||
pub(crate) signer_pubkeys: HashMap<u64, Public>,
|
pub(crate) signer_pubkeys: HashMap<u64, Public<S>>,
|
||||||
/// group_public represents the joint public key for the entire group.
|
/// group_public represents the joint public key for the entire group.
|
||||||
pub group_public: VerificationKey<SpendAuth>,
|
pub group_public: VerificationKey<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows all participants' keys to be generated using a central, trusted
|
/// Allows all participants' keys to be generated using a central, trusted
|
||||||
|
@ -172,22 +179,22 @@ pub struct PublicKeyPackage {
|
||||||
/// key. The output from this function is a set of shares along with one single
|
/// key. The output from this function is a set of shares along with one single
|
||||||
/// commitment that participants use to verify the integrity of the share. The
|
/// commitment that participants use to verify the integrity of the share. The
|
||||||
/// number of signers is limited to 255.
|
/// number of signers is limited to 255.
|
||||||
pub fn keygen_with_dealer<R: RngCore + CryptoRng>(
|
pub fn keygen_with_dealer<R: RngCore + CryptoRng, S: SpendAuth>(
|
||||||
num_signers: u8,
|
num_signers: u8,
|
||||||
threshold: u8,
|
threshold: u8,
|
||||||
mut rng: R,
|
mut rng: R,
|
||||||
) -> Result<(Vec<SharePackage>, PublicKeyPackage), &'static str> {
|
) -> Result<(Vec<SharePackage<S>>, PublicKeyPackage<S>), &'static str> {
|
||||||
let mut bytes = [0; 64];
|
let mut bytes = [0; 64];
|
||||||
rng.fill_bytes(&mut bytes);
|
rng.fill_bytes(&mut bytes);
|
||||||
|
|
||||||
let secret = Secret(Scalar::from_bytes_wide(&bytes));
|
let secret = Secret(S::Scalar::from_bytes_wide(&bytes));
|
||||||
let group_public = VerificationKey::from(&secret.0);
|
let group_public = VerificationKey::from(&secret.0);
|
||||||
let shares = generate_shares(&secret, num_signers, threshold, rng)?;
|
let shares = generate_shares(&secret, num_signers, threshold, rng)?;
|
||||||
let mut sharepackages: Vec<SharePackage> = Vec::with_capacity(num_signers as usize);
|
let mut sharepackages: Vec<SharePackage<S>> = Vec::with_capacity(num_signers as usize);
|
||||||
let mut signer_pubkeys: HashMap<u64, Public> = HashMap::with_capacity(num_signers as usize);
|
let mut signer_pubkeys: HashMap<u64, Public<S>> = HashMap::with_capacity(num_signers as usize);
|
||||||
|
|
||||||
for share in shares {
|
for share in shares {
|
||||||
let signer_public = Public(SpendAuth::basepoint() * share.value.0);
|
let signer_public = Public(S::basepoint() * share.value.0);
|
||||||
sharepackages.push(SharePackage {
|
sharepackages.push(SharePackage {
|
||||||
index: share.receiver_index,
|
index: share.receiver_index,
|
||||||
share: share.clone(),
|
share: share.clone(),
|
||||||
|
@ -213,13 +220,13 @@ pub fn keygen_with_dealer<R: RngCore + CryptoRng>(
|
||||||
/// mechanism as all other signing participants. Note that participants *MUST*
|
/// mechanism as all other signing participants. Note that participants *MUST*
|
||||||
/// ensure that they have the same view as all other participants of the
|
/// ensure that they have the same view as all other participants of the
|
||||||
/// commitment!
|
/// commitment!
|
||||||
fn verify_share(share: &Share) -> Result<(), &'static str> {
|
fn verify_share<S: SpendAuth>(share: &Share<S>) -> Result<(), &'static str> {
|
||||||
let f_result = SpendAuth::basepoint() * share.value.0;
|
let f_result = S::basepoint() * share.value.0;
|
||||||
|
|
||||||
let x = Scalar::from(share.receiver_index as u64);
|
let x = S::Scalar::from(share.receiver_index as u64);
|
||||||
|
|
||||||
let (_, result) = share.commitment.0.iter().fold(
|
let (_, result) = share.commitment.0.iter().fold(
|
||||||
(Scalar::one(), jubjub::ExtendedPoint::identity()),
|
(S::Scalar::one(), S::Point::identity()),
|
||||||
|(x_to_the_i, sum_so_far), comm_i| (x_to_the_i * x, sum_so_far + comm_i.0 * x_to_the_i),
|
|(x_to_the_i, sum_so_far), comm_i| (x_to_the_i * x, sum_so_far + comm_i.0 * x_to_the_i),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -245,12 +252,12 @@ fn verify_share(share: &Share) -> Result<(), &'static str> {
|
||||||
/// polynomial f
|
/// polynomial f
|
||||||
/// - For each participant i, their secret share is f(i)
|
/// - For each participant i, their secret share is f(i)
|
||||||
/// - The commitment to the secret polynomial f is [g^a, g^b, g^c]
|
/// - The commitment to the secret polynomial f is [g^a, g^b, g^c]
|
||||||
fn generate_shares<R: RngCore + CryptoRng>(
|
fn generate_shares<R: RngCore + CryptoRng, S: SpendAuth>(
|
||||||
secret: &Secret,
|
secret: &Secret<S>,
|
||||||
numshares: u8,
|
numshares: u8,
|
||||||
threshold: u8,
|
threshold: u8,
|
||||||
mut rng: R,
|
mut rng: R,
|
||||||
) -> Result<Vec<Share>, &'static str> {
|
) -> Result<Vec<Share<S>>, &'static str> {
|
||||||
if threshold < 1 {
|
if threshold < 1 {
|
||||||
return Err("Threshold cannot be 0");
|
return Err("Threshold cannot be 0");
|
||||||
}
|
}
|
||||||
|
@ -265,36 +272,37 @@ fn generate_shares<R: RngCore + CryptoRng>(
|
||||||
|
|
||||||
let numcoeffs = threshold - 1;
|
let numcoeffs = threshold - 1;
|
||||||
|
|
||||||
let mut coefficients: Vec<Scalar> = Vec::with_capacity(threshold as usize);
|
let mut coefficients: Vec<S::Scalar> = Vec::with_capacity(threshold as usize);
|
||||||
|
|
||||||
let mut shares: Vec<Share> = Vec::with_capacity(numshares as usize);
|
let mut shares: Vec<Share<S>> = Vec::with_capacity(numshares as usize);
|
||||||
|
|
||||||
let mut commitment: ShareCommitment = ShareCommitment(Vec::with_capacity(threshold as usize));
|
let mut commitment: ShareCommitment<S> =
|
||||||
|
ShareCommitment(Vec::with_capacity(threshold as usize));
|
||||||
|
|
||||||
for _ in 0..numcoeffs {
|
for _ in 0..numcoeffs {
|
||||||
let mut bytes = [0; 64];
|
let mut bytes = [0; 64];
|
||||||
rng.fill_bytes(&mut bytes);
|
rng.fill_bytes(&mut bytes);
|
||||||
coefficients.push(Scalar::from_bytes_wide(&bytes));
|
coefficients.push(S::Scalar::from_bytes_wide(&bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifiable secret sharing, to make sure that participants can ensure their secret is consistent
|
// Verifiable secret sharing, to make sure that participants can ensure their secret is consistent
|
||||||
// with every other participant's.
|
// with every other participant's.
|
||||||
commitment.0.push(Commitment(jubjub::AffinePoint::from(
|
commitment
|
||||||
SpendAuth::basepoint() * secret.0,
|
.0
|
||||||
)));
|
.push(Commitment((S::basepoint() * secret.0).to_affine()));
|
||||||
|
|
||||||
for c in &coefficients {
|
for c in &coefficients {
|
||||||
commitment.0.push(Commitment(jubjub::AffinePoint::from(
|
commitment
|
||||||
SpendAuth::basepoint() * c,
|
.0
|
||||||
)));
|
.push(Commitment((S::basepoint() * c).to_affine()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the polynomial with `secret` as the constant term
|
// Evaluate the polynomial with `secret` as the constant term
|
||||||
// and `coeffs` as the other coefficients at the point x=share_index,
|
// and `coeffs` as the other coefficients at the point x=share_index,
|
||||||
// using Horner's method.
|
// using Horner's method.
|
||||||
for index in 1..numshares + 1 {
|
for index in 1..numshares + 1 {
|
||||||
let scalar_index = Scalar::from(index as u64);
|
let scalar_index = S::Scalar::from(index as u64);
|
||||||
let mut value = Scalar::zero();
|
let mut value = S::Scalar::zero();
|
||||||
|
|
||||||
// Polynomial evaluation, for this index
|
// Polynomial evaluation, for this index
|
||||||
for i in (0..numcoeffs).rev() {
|
for i in (0..numcoeffs).rev() {
|
||||||
|
@ -319,17 +327,17 @@ fn generate_shares<R: RngCore + CryptoRng>(
|
||||||
/// operation; re-using nonces will result in leakage of a signer's long-lived
|
/// operation; re-using nonces will result in leakage of a signer's long-lived
|
||||||
/// signing key.
|
/// signing key.
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct SigningNonces {
|
pub struct SigningNonces<S: SpendAuth> {
|
||||||
hiding: Scalar,
|
hiding: S::Scalar,
|
||||||
binding: Scalar,
|
binding: S::Scalar,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zeroizes `SigningNonces` to be the `Default` value on drop (when it goes out
|
// Zeroizes `SigningNonces` to be the `Default` value on drop (when it goes out
|
||||||
// of scope). Luckily the derived `Default` includes the `Default` impl of the
|
// of scope). Luckily the derived `Default` includes the `Default` impl of the
|
||||||
// `jubjub::Fr/Scalar`'s, which is four 0u64's under the hood.
|
// `jubjub::Fr/Scalar`'s, which is four 0u64's under the hood.
|
||||||
impl DefaultIsZeroes for SigningNonces {}
|
impl<S: SpendAuth> DefaultIsZeroes for SigningNonces<S> {}
|
||||||
|
|
||||||
impl SigningNonces {
|
impl<S: SpendAuth> SigningNonces<S> {
|
||||||
/// Generates a new signing nonce.
|
/// Generates a new signing nonce.
|
||||||
///
|
///
|
||||||
/// Each participant generates signing nonces before performing a signing
|
/// Each participant generates signing nonces before performing a signing
|
||||||
|
@ -353,8 +361,8 @@ impl SigningNonces {
|
||||||
|
|
||||||
// The values of 'hiding' and 'binding' must be non-zero so that commitments are not the
|
// The values of 'hiding' and 'binding' must be non-zero so that commitments are not the
|
||||||
// identity.
|
// identity.
|
||||||
let hiding = Scalar::from_bytes_wide(&random_nonzero_bytes(rng));
|
let hiding = S::Scalar::from_bytes_wide(&random_nonzero_bytes(rng));
|
||||||
let binding = Scalar::from_bytes_wide(&random_nonzero_bytes(rng));
|
let binding = S::Scalar::from_bytes_wide(&random_nonzero_bytes(rng));
|
||||||
|
|
||||||
Self { hiding, binding }
|
Self { hiding, binding }
|
||||||
}
|
}
|
||||||
|
@ -365,32 +373,32 @@ impl SigningNonces {
|
||||||
/// This step can be batched if desired by the implementation. Each
|
/// This step can be batched if desired by the implementation. Each
|
||||||
/// SigningCommitment can be used for exactly *one* signature.
|
/// SigningCommitment can be used for exactly *one* signature.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct SigningCommitments {
|
pub struct SigningCommitments<S: SpendAuth> {
|
||||||
/// The participant index
|
/// The participant index
|
||||||
pub(crate) index: u64,
|
pub(crate) index: u64,
|
||||||
/// The hiding point.
|
/// The hiding point.
|
||||||
pub(crate) hiding: jubjub::ExtendedPoint,
|
pub(crate) hiding: S::Point,
|
||||||
/// The binding point.
|
/// The binding point.
|
||||||
pub(crate) binding: jubjub::ExtendedPoint,
|
pub(crate) binding: S::Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(u64, &SigningNonces)> for SigningCommitments {
|
impl<S: SpendAuth> From<(u64, &SigningNonces<S>)> for SigningCommitments<S> {
|
||||||
/// For SpendAuth signatures only, not Binding signatures, in RedJubjub/Zcash.
|
/// For SpendAuth signatures only, not Binding signatures, in RedDSA/Zcash.
|
||||||
fn from((index, nonces): (u64, &SigningNonces)) -> Self {
|
fn from((index, nonces): (u64, &SigningNonces<S>)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
index,
|
index,
|
||||||
hiding: SpendAuth::basepoint() * nonces.hiding,
|
hiding: S::basepoint() * nonces.hiding,
|
||||||
binding: SpendAuth::basepoint() * nonces.binding,
|
binding: S::basepoint() * nonces.binding,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generated by the coordinator of the signing operation and distributed to
|
/// Generated by the coordinator of the signing operation and distributed to
|
||||||
/// each signing party.
|
/// each signing party.
|
||||||
pub struct SigningPackage {
|
pub struct SigningPackage<S: SpendAuth> {
|
||||||
/// The set of commitments participants published in the first round of the
|
/// The set of commitments participants published in the first round of the
|
||||||
/// protocol.
|
/// protocol.
|
||||||
pub signing_commitments: Vec<SigningCommitments>,
|
pub signing_commitments: Vec<SigningCommitments<S>>,
|
||||||
/// Message which each participant will sign.
|
/// Message which each participant will sign.
|
||||||
///
|
///
|
||||||
/// Each signer should perform protocol-specific verification on the message.
|
/// Each signer should perform protocol-specific verification on the message.
|
||||||
|
@ -399,37 +407,35 @@ pub struct SigningPackage {
|
||||||
|
|
||||||
/// A representation of a single signature used in FROST structures and messages.
|
/// A representation of a single signature used in FROST structures and messages.
|
||||||
#[derive(Clone, Copy, Default, PartialEq)]
|
#[derive(Clone, Copy, Default, PartialEq)]
|
||||||
pub struct SignatureResponse(pub(crate) Scalar);
|
pub struct SignatureResponse<S: SpendAuth>(pub(crate) S::Scalar);
|
||||||
|
|
||||||
/// A participant's signature share, which the coordinator will use to aggregate
|
/// A participant's signature share, which the coordinator will use to aggregate
|
||||||
/// with all other signer's shares into the joint signature.
|
/// with all other signer's shares into the joint signature.
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct SignatureShare {
|
pub struct SignatureShare<S: SpendAuth> {
|
||||||
/// Represents the participant index.
|
/// Represents the participant index.
|
||||||
pub(crate) index: u64,
|
pub(crate) index: u64,
|
||||||
/// This participant's signature over the message.
|
/// This participant's signature over the message.
|
||||||
pub(crate) signature: SignatureResponse,
|
pub(crate) signature: SignatureResponse<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zeroizes `SignatureShare` to be the `Default` value on drop (when it goes out
|
// Zeroizes `SignatureShare` to be the `Default` value on drop (when it goes out
|
||||||
// of scope). Luckily the derived `Default` includes the `Default` impl of
|
// of scope). Luckily the derived `Default` includes the `Default` impl of
|
||||||
// jubjub::Fr/Scalar, which is four 0u64's under the hood, and u32, which is
|
// jubjub::Fr/Scalar, which is four 0u64's under the hood, and u32, which is
|
||||||
// 0u32.
|
// 0u32.
|
||||||
impl DefaultIsZeroes for SignatureShare {}
|
impl<S: SpendAuth> DefaultIsZeroes for SignatureShare<S> {}
|
||||||
|
|
||||||
impl SignatureShare {
|
impl<S: SpendAuth> SignatureShare<S> {
|
||||||
/// Tests if a signature share issued by a participant is valid before
|
/// Tests if a signature share issued by a participant is valid before
|
||||||
/// aggregating it into a final joint signature to publish.
|
/// aggregating it into a final joint signature to publish.
|
||||||
pub fn check_is_valid(
|
pub fn check_is_valid(
|
||||||
&self,
|
&self,
|
||||||
pubkey: &Public,
|
pubkey: &Public<S>,
|
||||||
lambda_i: Scalar,
|
lambda_i: S::Scalar,
|
||||||
commitment: jubjub::ExtendedPoint,
|
commitment: S::Point,
|
||||||
challenge: Scalar,
|
challenge: S::Scalar,
|
||||||
) -> Result<(), &'static str> {
|
) -> Result<(), &'static str> {
|
||||||
if (SpendAuth::basepoint() * self.signature.0)
|
if (S::basepoint() * self.signature.0) != (commitment + pubkey.0 * challenge * lambda_i) {
|
||||||
!= (commitment + pubkey.0 * challenge * lambda_i)
|
|
||||||
{
|
|
||||||
return Err("Invalid signature share");
|
return Err("Invalid signature share");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -448,16 +454,18 @@ impl SignatureShare {
|
||||||
/// turns out to be too conservative.
|
/// turns out to be too conservative.
|
||||||
// TODO: Make sure the above is a correct statement, fix if needed in:
|
// TODO: Make sure the above is a correct statement, fix if needed in:
|
||||||
// https://github.com/ZcashFoundation/redjubjub/issues/111
|
// https://github.com/ZcashFoundation/redjubjub/issues/111
|
||||||
pub fn preprocess<R>(
|
pub fn preprocess<R, S>(
|
||||||
num_nonces: u8,
|
num_nonces: u8,
|
||||||
participant_index: u64,
|
participant_index: u64,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> (Vec<SigningNonces>, Vec<SigningCommitments>)
|
) -> (Vec<SigningNonces<S>>, Vec<SigningCommitments<S>>)
|
||||||
where
|
where
|
||||||
R: CryptoRng + RngCore,
|
R: CryptoRng + RngCore,
|
||||||
|
S: SpendAuth,
|
||||||
{
|
{
|
||||||
let mut signing_nonces: Vec<SigningNonces> = Vec::with_capacity(num_nonces as usize);
|
let mut signing_nonces: Vec<SigningNonces<S>> = Vec::with_capacity(num_nonces as usize);
|
||||||
let mut signing_commitments: Vec<SigningCommitments> = Vec::with_capacity(num_nonces as usize);
|
let mut signing_commitments: Vec<SigningCommitments<S>> =
|
||||||
|
Vec::with_capacity(num_nonces as usize);
|
||||||
|
|
||||||
for _ in 0..num_nonces {
|
for _ in 0..num_nonces {
|
||||||
let nonces = SigningNonces::new(rng);
|
let nonces = SigningNonces::new(rng);
|
||||||
|
@ -470,28 +478,28 @@ where
|
||||||
|
|
||||||
/// Generates the binding factor that ensures each signature share is strongly
|
/// Generates the binding factor that ensures each signature share is strongly
|
||||||
/// bound to a signing set, specific set of commitments, and a specific message.
|
/// bound to a signing set, specific set of commitments, and a specific message.
|
||||||
fn gen_rho_i(index: u64, signing_package: &SigningPackage) -> Scalar {
|
fn gen_rho_i<S: SpendAuth>(index: u64, signing_package: &SigningPackage<S>) -> S::Scalar {
|
||||||
// Hash signature message with HStar before deriving the binding factor.
|
// Hash signature message with HStar before deriving the binding factor.
|
||||||
//
|
//
|
||||||
// To avoid a collision with other inputs to the hash that generates the
|
// To avoid a collision with other inputs to the hash that generates the
|
||||||
// binding factor, we should hash our input message first. Our 'standard'
|
// binding factor, we should hash our input message first. Our 'standard'
|
||||||
// hash is HStar, which uses a domain separator already, and is the same one
|
// hash is HStar, which uses a domain separator already, and is the same one
|
||||||
// that generates the binding factor.
|
// that generates the binding factor.
|
||||||
let message_hash = HStar::default()
|
let message_hash = HStar::<S>::default()
|
||||||
.update(signing_package.message.as_slice())
|
.update(signing_package.message.as_slice())
|
||||||
.finalize();
|
.finalize();
|
||||||
|
|
||||||
let mut hasher = HStar::default();
|
let mut hasher = HStar::<S>::default();
|
||||||
hasher
|
hasher
|
||||||
.update("FROST_rho".as_bytes())
|
.update("FROST_rho".as_bytes())
|
||||||
.update(index.to_be_bytes())
|
.update(index.to_be_bytes())
|
||||||
.update(message_hash.to_bytes());
|
.update(message_hash.to_repr());
|
||||||
|
|
||||||
for item in signing_package.signing_commitments.iter() {
|
for item in signing_package.signing_commitments.iter() {
|
||||||
hasher.update(item.index.to_be_bytes());
|
hasher.update(item.index.to_be_bytes());
|
||||||
let hiding_bytes = jubjub::AffinePoint::from(item.hiding).to_bytes();
|
let hiding_bytes = item.hiding.to_bytes();
|
||||||
hasher.update(hiding_bytes);
|
hasher.update(hiding_bytes);
|
||||||
let binding_bytes = jubjub::AffinePoint::from(item.binding).to_bytes();
|
let binding_bytes = item.binding.to_bytes();
|
||||||
hasher.update(binding_bytes);
|
hasher.update(binding_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,11 +508,11 @@ fn gen_rho_i(index: u64, signing_package: &SigningPackage) -> Scalar {
|
||||||
|
|
||||||
/// Generates the group commitment which is published as part of the joint
|
/// Generates the group commitment which is published as part of the joint
|
||||||
/// Schnorr signature.
|
/// Schnorr signature.
|
||||||
fn gen_group_commitment(
|
fn gen_group_commitment<S: SpendAuth>(
|
||||||
signing_package: &SigningPackage,
|
signing_package: &SigningPackage<S>,
|
||||||
bindings: &HashMap<u64, Scalar>,
|
bindings: &HashMap<u64, S::Scalar>,
|
||||||
) -> Result<GroupCommitment, &'static str> {
|
) -> Result<GroupCommitment<S>, &'static str> {
|
||||||
let identity = jubjub::ExtendedPoint::identity();
|
let identity = S::Point::identity();
|
||||||
let mut accumulator = identity;
|
let mut accumulator = identity;
|
||||||
|
|
||||||
for commitment in signing_package.signing_commitments.iter() {
|
for commitment in signing_package.signing_commitments.iter() {
|
||||||
|
@ -520,18 +528,18 @@ fn gen_group_commitment(
|
||||||
accumulator += commitment.hiding + (commitment.binding * rho_i)
|
accumulator += commitment.hiding + (commitment.binding * rho_i)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(GroupCommitment(jubjub::AffinePoint::from(accumulator)))
|
Ok(GroupCommitment(accumulator.to_affine()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the challenge as is required for Schnorr signatures.
|
/// Generates the challenge as is required for Schnorr signatures.
|
||||||
fn gen_challenge(
|
fn gen_challenge<S: SpendAuth>(
|
||||||
signing_package: &SigningPackage,
|
signing_package: &SigningPackage<S>,
|
||||||
group_commitment: &GroupCommitment,
|
group_commitment: &GroupCommitment<S>,
|
||||||
group_public: &VerificationKey<SpendAuth>,
|
group_public: &VerificationKey<S>,
|
||||||
) -> Scalar {
|
) -> S::Scalar {
|
||||||
let group_commitment_bytes = jubjub::AffinePoint::from(group_commitment.0).to_bytes();
|
let group_commitment_bytes = group_commitment.0.to_bytes();
|
||||||
|
|
||||||
HStar::default()
|
HStar::<S>::default()
|
||||||
.update(group_commitment_bytes)
|
.update(group_commitment_bytes)
|
||||||
.update(group_public.bytes.bytes)
|
.update(group_public.bytes.bytes)
|
||||||
.update(signing_package.message.as_slice())
|
.update(signing_package.message.as_slice())
|
||||||
|
@ -539,21 +547,21 @@ fn gen_challenge(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the lagrange coefficient for the i'th participant.
|
/// Generates the lagrange coefficient for the i'th participant.
|
||||||
fn gen_lagrange_coeff(
|
fn gen_lagrange_coeff<S: SpendAuth>(
|
||||||
signer_index: u64,
|
signer_index: u64,
|
||||||
signing_package: &SigningPackage,
|
signing_package: &SigningPackage<S>,
|
||||||
) -> Result<Scalar, &'static str> {
|
) -> Result<S::Scalar, &'static str> {
|
||||||
let mut num = Scalar::one();
|
let mut num = S::Scalar::one();
|
||||||
let mut den = Scalar::one();
|
let mut den = S::Scalar::one();
|
||||||
for commitment in signing_package.signing_commitments.iter() {
|
for commitment in signing_package.signing_commitments.iter() {
|
||||||
if commitment.index == signer_index {
|
if commitment.index == signer_index {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
num *= Scalar::from(commitment.index as u64);
|
num *= S::Scalar::from(commitment.index as u64);
|
||||||
den *= Scalar::from(commitment.index as u64) - Scalar::from(signer_index as u64);
|
den *= S::Scalar::from(commitment.index as u64) - S::Scalar::from(signer_index as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
if den == Scalar::zero() {
|
if den == S::Scalar::zero() {
|
||||||
return Err("Duplicate shares provided");
|
return Err("Duplicate shares provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,12 +579,12 @@ fn gen_lagrange_coeff(
|
||||||
///
|
///
|
||||||
/// Assumes the participant has already determined which nonce corresponds with
|
/// Assumes the participant has already determined which nonce corresponds with
|
||||||
/// the commitment that was assigned by the coordinator in the SigningPackage.
|
/// the commitment that was assigned by the coordinator in the SigningPackage.
|
||||||
pub fn sign(
|
pub fn sign<S: SpendAuth>(
|
||||||
signing_package: &SigningPackage,
|
signing_package: &SigningPackage<S>,
|
||||||
participant_nonces: SigningNonces,
|
participant_nonces: SigningNonces<S>,
|
||||||
share_package: &SharePackage,
|
share_package: &SharePackage<S>,
|
||||||
) -> Result<SignatureShare, &'static str> {
|
) -> Result<SignatureShare<S>, &'static str> {
|
||||||
let mut bindings: HashMap<u64, Scalar> =
|
let mut bindings: HashMap<u64, S::Scalar> =
|
||||||
HashMap::with_capacity(signing_package.signing_commitments.len());
|
HashMap::with_capacity(signing_package.signing_commitments.len());
|
||||||
|
|
||||||
for comm in signing_package.signing_commitments.iter() {
|
for comm in signing_package.signing_commitments.iter() {
|
||||||
|
@ -599,7 +607,7 @@ pub fn sign(
|
||||||
.ok_or("No matching binding!")?;
|
.ok_or("No matching binding!")?;
|
||||||
|
|
||||||
// The Schnorr signature share
|
// The Schnorr signature share
|
||||||
let signature: Scalar = participant_nonces.hiding
|
let signature: S::Scalar = participant_nonces.hiding
|
||||||
+ (participant_nonces.binding * participant_rho_i)
|
+ (participant_nonces.binding * participant_rho_i)
|
||||||
+ (lambda_i * share_package.share.value.0 * challenge);
|
+ (lambda_i * share_package.share.value.0 * challenge);
|
||||||
|
|
||||||
|
@ -624,12 +632,12 @@ pub fn sign(
|
||||||
/// signature, if the coordinator themselves is a signer and misbehaves, they
|
/// signature, if the coordinator themselves is a signer and misbehaves, they
|
||||||
/// can avoid that step. However, at worst, this results in a denial of
|
/// can avoid that step. However, at worst, this results in a denial of
|
||||||
/// service attack due to publishing an invalid signature.
|
/// service attack due to publishing an invalid signature.
|
||||||
pub fn aggregate(
|
pub fn aggregate<S: SpendAuth>(
|
||||||
signing_package: &SigningPackage,
|
signing_package: &SigningPackage<S>,
|
||||||
signing_shares: &[SignatureShare],
|
signing_shares: &[SignatureShare<S>],
|
||||||
pubkeys: &PublicKeyPackage,
|
pubkeys: &PublicKeyPackage<S>,
|
||||||
) -> Result<Signature<SpendAuth>, &'static str> {
|
) -> Result<Signature<S>, &'static str> {
|
||||||
let mut bindings: HashMap<u64, Scalar> =
|
let mut bindings: HashMap<u64, S::Scalar> =
|
||||||
HashMap::with_capacity(signing_package.signing_commitments.len());
|
HashMap::with_capacity(signing_package.signing_commitments.len());
|
||||||
|
|
||||||
for comm in signing_package.signing_commitments.iter() {
|
for comm in signing_package.signing_commitments.iter() {
|
||||||
|
@ -658,14 +666,14 @@ pub fn aggregate(
|
||||||
|
|
||||||
// The aggregation of the signature shares by summing them up, resulting in
|
// The aggregation of the signature shares by summing them up, resulting in
|
||||||
// a plain Schnorr signature.
|
// a plain Schnorr signature.
|
||||||
let mut z = Scalar::zero();
|
let mut z = S::Scalar::zero();
|
||||||
for signature_share in signing_shares {
|
for signature_share in signing_shares {
|
||||||
z += signature_share.signature.0;
|
z += signature_share.signature.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Signature {
|
Ok(Signature {
|
||||||
r_bytes: jubjub::AffinePoint::from(group_commitment.0).to_bytes(),
|
r_bytes: group_commitment.0.to_bytes().as_ref().try_into().unwrap(),
|
||||||
s_bytes: z.to_bytes(),
|
s_bytes: z.to_repr().as_ref().try_into().unwrap(),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -673,35 +681,37 @@ pub fn aggregate(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::{private::Sealed, sapling};
|
||||||
|
use jubjub::Scalar;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
|
||||||
fn reconstruct_secret(shares: Vec<Share>) -> Result<Scalar, &'static str> {
|
fn reconstruct_secret<S: SpendAuth>(shares: Vec<Share<S>>) -> Result<S::Scalar, &'static str> {
|
||||||
let numshares = shares.len();
|
let numshares = shares.len();
|
||||||
|
|
||||||
if numshares < 1 {
|
if numshares < 1 {
|
||||||
return Err("No shares provided");
|
return Err("No shares provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut lagrange_coeffs: Vec<Scalar> = Vec::with_capacity(numshares as usize);
|
let mut lagrange_coeffs: Vec<S::Scalar> = Vec::with_capacity(numshares as usize);
|
||||||
|
|
||||||
for i in 0..numshares {
|
for i in 0..numshares {
|
||||||
let mut num = Scalar::one();
|
let mut num = S::Scalar::one();
|
||||||
let mut den = Scalar::one();
|
let mut den = S::Scalar::one();
|
||||||
for j in 0..numshares {
|
for j in 0..numshares {
|
||||||
if j == i {
|
if j == i {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
num *= Scalar::from(shares[j].receiver_index as u64);
|
num *= S::Scalar::from(shares[j].receiver_index as u64);
|
||||||
den *= Scalar::from(shares[j].receiver_index as u64)
|
den *= S::Scalar::from(shares[j].receiver_index as u64)
|
||||||
- Scalar::from(shares[i].receiver_index as u64);
|
- S::Scalar::from(shares[i].receiver_index as u64);
|
||||||
}
|
}
|
||||||
if den == Scalar::zero() {
|
if den == S::Scalar::zero() {
|
||||||
return Err("Duplicate shares provided");
|
return Err("Duplicate shares provided");
|
||||||
}
|
}
|
||||||
lagrange_coeffs.push(num * den.invert().unwrap());
|
lagrange_coeffs.push(num * den.invert().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut secret = Scalar::zero();
|
let mut secret = S::Scalar::zero();
|
||||||
|
|
||||||
for i in 0..numshares {
|
for i in 0..numshares {
|
||||||
secret += lagrange_coeffs[i] * shares[i].value.0;
|
secret += lagrange_coeffs[i] * shares[i].value.0;
|
||||||
|
@ -720,9 +730,9 @@ mod tests {
|
||||||
rng.fill_bytes(&mut bytes);
|
rng.fill_bytes(&mut bytes);
|
||||||
let secret = Secret(Scalar::from_bytes_wide(&bytes));
|
let secret = Secret(Scalar::from_bytes_wide(&bytes));
|
||||||
|
|
||||||
let _ = SpendAuth::basepoint() * secret.0;
|
let _ = sapling::SpendAuth::basepoint() * secret.0;
|
||||||
|
|
||||||
let shares = generate_shares(&secret, 5, 3, rng).unwrap();
|
let shares = generate_shares::<_, sapling::SpendAuth>(&secret, 5, 3, rng).unwrap();
|
||||||
|
|
||||||
for share in shares.iter() {
|
for share in shares.iter() {
|
||||||
assert_eq!(verify_share(&share), Ok(()));
|
assert_eq!(verify_share(&share), Ok(()));
|
||||||
|
|
27
src/hash.rs
27
src/hash.rs
|
@ -8,25 +8,32 @@
|
||||||
// - Deirdre Connolly <deirdre@zfnd.org>
|
// - Deirdre Connolly <deirdre@zfnd.org>
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
use blake2b_simd::{Params, State};
|
use std::marker::PhantomData;
|
||||||
use jubjub::Scalar;
|
|
||||||
|
|
||||||
/// Provides H^star, the hash-to-scalar function used by RedJubjub.
|
use blake2b_simd::{Params, State};
|
||||||
pub struct HStar {
|
|
||||||
|
use crate::{private::SealedScalar, SigType};
|
||||||
|
|
||||||
|
/// Provides H^star, the hash-to-scalar function used by RedDSA.
|
||||||
|
pub struct HStar<T: SigType> {
|
||||||
state: State,
|
state: State,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for HStar {
|
impl<T: SigType> Default for HStar<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let state = Params::new()
|
let state = Params::new()
|
||||||
.hash_length(64)
|
.hash_length(64)
|
||||||
.personal(b"Zcash_RedJubjubH")
|
.personal(T::H_STAR_PERSONALIZATION)
|
||||||
.to_state();
|
.to_state();
|
||||||
Self { state }
|
Self {
|
||||||
|
state,
|
||||||
|
_marker: PhantomData::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HStar {
|
impl<T: SigType> HStar<T> {
|
||||||
/// Add `data` to the hash, and return `Self` for chaining.
|
/// Add `data` to the hash, and return `Self` for chaining.
|
||||||
pub fn update(&mut self, data: impl AsRef<[u8]>) -> &mut Self {
|
pub fn update(&mut self, data: impl AsRef<[u8]>) -> &mut Self {
|
||||||
self.state.update(data.as_ref());
|
self.state.update(data.as_ref());
|
||||||
|
@ -34,7 +41,7 @@ impl HStar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume `self` to compute the hash output.
|
/// Consume `self` to compute the hash output.
|
||||||
pub fn finalize(&self) -> Scalar {
|
pub fn finalize(&self) -> T::Scalar {
|
||||||
Scalar::from_bytes_wide(self.state.finalize().as_array())
|
T::Scalar::from_bytes_wide(self.state.finalize().as_array())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
61
src/lib.rs
61
src/lib.rs
|
@ -17,13 +17,14 @@ mod error;
|
||||||
pub mod frost;
|
pub mod frost;
|
||||||
mod hash;
|
mod hash;
|
||||||
mod messages;
|
mod messages;
|
||||||
|
pub mod sapling;
|
||||||
mod scalar_mul;
|
mod scalar_mul;
|
||||||
pub(crate) mod signature;
|
pub(crate) mod signature;
|
||||||
mod signing_key;
|
mod signing_key;
|
||||||
mod verification_key;
|
mod verification_key;
|
||||||
|
|
||||||
/// An element of the JubJub scalar field used for randomization of public and secret keys.
|
/// An element of the protocol's scalar field used for randomization of public and secret keys.
|
||||||
pub type Randomizer = jubjub::Scalar;
|
pub type Randomizer<S> = <S as private::Sealed<S>>::Scalar;
|
||||||
|
|
||||||
use hash::HStar;
|
use hash::HStar;
|
||||||
|
|
||||||
|
@ -32,11 +33,11 @@ pub use signature::Signature;
|
||||||
pub use signing_key::SigningKey;
|
pub use signing_key::SigningKey;
|
||||||
pub use verification_key::{VerificationKey, VerificationKeyBytes};
|
pub use verification_key::{VerificationKey, VerificationKeyBytes};
|
||||||
|
|
||||||
/// Abstracts over different RedJubJub parameter choices, [`Binding`]
|
/// Abstracts over different RedDSA parameter choices, [`Binding`]
|
||||||
/// and [`SpendAuth`].
|
/// and [`SpendAuth`].
|
||||||
///
|
///
|
||||||
/// As described [at the end of §5.4.6][concretereddsa] of the Zcash
|
/// As described [at the end of §5.4.6][concretereddsa] of the Zcash
|
||||||
/// protocol specification, the generator used in RedJubjub is left as
|
/// protocol specification, the generator used in RedDSA is left as
|
||||||
/// an unspecified parameter, chosen differently for each of
|
/// an unspecified parameter, chosen differently for each of
|
||||||
/// `BindingSig` and `SpendAuthSig`.
|
/// `BindingSig` and `SpendAuthSig`.
|
||||||
///
|
///
|
||||||
|
@ -44,31 +45,57 @@ pub use verification_key::{VerificationKey, VerificationKeyBytes};
|
||||||
/// parameter.
|
/// parameter.
|
||||||
///
|
///
|
||||||
/// [concretereddsa]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa
|
/// [concretereddsa]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa
|
||||||
pub trait SigType: private::Sealed {}
|
pub trait SigType: private::Sealed<Self> {}
|
||||||
|
|
||||||
/// A type variable corresponding to Zcash's `BindingSig`.
|
/// A trait corresponding to `BindingSig` in Zcash protocols.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
pub trait Binding: SigType {}
|
||||||
pub enum Binding {}
|
|
||||||
impl SigType for Binding {}
|
|
||||||
|
|
||||||
/// A type variable corresponding to Zcash's `SpendAuthSig`.
|
/// A trait corresponding to `SpendAuthSig` in Zcash protocols.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
pub trait SpendAuth: SigType {}
|
||||||
pub enum SpendAuth {}
|
|
||||||
impl SigType for SpendAuth {}
|
|
||||||
|
|
||||||
pub(crate) mod private {
|
pub(crate) mod private {
|
||||||
use super::*;
|
use super::*;
|
||||||
pub trait Sealed: Copy + Clone + Eq + PartialEq + std::fmt::Debug {
|
|
||||||
fn basepoint() -> jubjub::ExtendedPoint;
|
pub trait SealedScalar {
|
||||||
|
fn from_bytes_wide(bytes: &[u8; 64]) -> Self;
|
||||||
|
fn from_raw(val: [u64; 4]) -> Self;
|
||||||
}
|
}
|
||||||
impl Sealed for Binding {
|
|
||||||
|
impl SealedScalar for jubjub::Scalar {
|
||||||
|
fn from_bytes_wide(bytes: &[u8; 64]) -> Self {
|
||||||
|
jubjub::Scalar::from_bytes_wide(bytes)
|
||||||
|
}
|
||||||
|
fn from_raw(val: [u64; 4]) -> Self {
|
||||||
|
jubjub::Scalar::from_raw(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Sealed<T: SigType>:
|
||||||
|
Copy + Clone + Default + Eq + PartialEq + std::fmt::Debug
|
||||||
|
{
|
||||||
|
const H_STAR_PERSONALIZATION: &'static [u8; 16];
|
||||||
|
type Scalar: group::ff::PrimeField + SealedScalar;
|
||||||
|
type Point: group::cofactor::CofactorCurve<Scalar = Self::Scalar>
|
||||||
|
+ scalar_mul::VartimeMultiscalarMul<Scalar = Self::Scalar, Point = Self::Point>;
|
||||||
|
|
||||||
|
fn basepoint() -> T::Point;
|
||||||
|
}
|
||||||
|
impl Sealed<sapling::Binding> for sapling::Binding {
|
||||||
|
const H_STAR_PERSONALIZATION: &'static [u8; 16] = b"Zcash_RedJubjubH";
|
||||||
|
type Point = jubjub::ExtendedPoint;
|
||||||
|
type Scalar = jubjub::Scalar;
|
||||||
|
|
||||||
fn basepoint() -> jubjub::ExtendedPoint {
|
fn basepoint() -> jubjub::ExtendedPoint {
|
||||||
jubjub::AffinePoint::from_bytes(constants::BINDINGSIG_BASEPOINT_BYTES)
|
jubjub::AffinePoint::from_bytes(constants::BINDINGSIG_BASEPOINT_BYTES)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Sealed for SpendAuth {
|
impl Sealed<sapling::SpendAuth> for sapling::SpendAuth {
|
||||||
|
const H_STAR_PERSONALIZATION: &'static [u8; 16] = b"Zcash_RedJubjubH";
|
||||||
|
type Point = jubjub::ExtendedPoint;
|
||||||
|
type Scalar = jubjub::Scalar;
|
||||||
|
|
||||||
fn basepoint() -> jubjub::ExtendedPoint {
|
fn basepoint() -> jubjub::ExtendedPoint {
|
||||||
jubjub::AffinePoint::from_bytes(constants::SPENDAUTHSIG_BASEPOINT_BYTES)
|
jubjub::AffinePoint::from_bytes(constants::SPENDAUTHSIG_BASEPOINT_BYTES)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
//! [RFC-001]: https://github.com/ZcashFoundation/redjubjub/blob/main/rfcs/0001-messages.md
|
//! [RFC-001]: https://github.com/ZcashFoundation/redjubjub/blob/main/rfcs/0001-messages.md
|
||||||
|
|
||||||
use crate::{frost, signature, verification_key, SpendAuth};
|
use crate::{frost, signature, verification_key, SpendAuth};
|
||||||
|
use group::GroupEncoding;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::{collections::BTreeMap, convert::TryInto};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use proptest_derive::Arbitrary;
|
use proptest_derive::Arbitrary;
|
||||||
|
@ -34,9 +35,12 @@ pub struct Secret([u8; 32]);
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct Commitment([u8; 32]);
|
pub struct Commitment([u8; 32]);
|
||||||
|
|
||||||
impl From<frost::Commitment> for Commitment {
|
impl<S: SpendAuth> From<frost::Commitment<S>> for Commitment {
|
||||||
fn from(value: frost::Commitment) -> Commitment {
|
fn from(value: frost::Commitment<S>) -> Commitment {
|
||||||
Commitment(jubjub::AffinePoint::from(value.0).to_bytes())
|
// TODO(str4d): We need to either enforce somewhere that these messages are only
|
||||||
|
// used with curves that have 32-byte encodings, or make the curve a parameter of
|
||||||
|
// the encoding. This will be easier once const_evaluatable_checked stabilises.
|
||||||
|
Commitment(value.0.to_bytes().as_ref().try_into().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,14 +60,14 @@ pub struct GroupCommitment([u8; 32]);
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct SignatureResponse([u8; 32]);
|
pub struct SignatureResponse([u8; 32]);
|
||||||
|
|
||||||
impl From<signature::Signature<SpendAuth>> for SignatureResponse {
|
impl<S: SpendAuth> From<signature::Signature<S>> for SignatureResponse {
|
||||||
fn from(value: signature::Signature<SpendAuth>) -> SignatureResponse {
|
fn from(value: signature::Signature<S>) -> SignatureResponse {
|
||||||
SignatureResponse(value.s_bytes)
|
SignatureResponse(value.s_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<signature::Signature<SpendAuth>> for GroupCommitment {
|
impl<S: SpendAuth> From<signature::Signature<S>> for GroupCommitment {
|
||||||
fn from(value: signature::Signature<SpendAuth>) -> GroupCommitment {
|
fn from(value: signature::Signature<S>) -> GroupCommitment {
|
||||||
GroupCommitment(value.r_bytes)
|
GroupCommitment(value.r_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,8 +80,8 @@ impl From<signature::Signature<SpendAuth>> for GroupCommitment {
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
pub struct VerificationKey([u8; 32]);
|
pub struct VerificationKey([u8; 32]);
|
||||||
|
|
||||||
impl From<verification_key::VerificationKey<SpendAuth>> for VerificationKey {
|
impl<S: SpendAuth> From<verification_key::VerificationKey<S>> for VerificationKey {
|
||||||
fn from(value: verification_key::VerificationKey<SpendAuth>) -> VerificationKey {
|
fn from(value: verification_key::VerificationKey<S>) -> VerificationKey {
|
||||||
VerificationKey(<[u8; 32]>::from(value))
|
VerificationKey(<[u8; 32]>::from(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,19 +224,22 @@ pub struct SigningPackage {
|
||||||
message: Vec<u8>,
|
message: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SigningPackage> for frost::SigningPackage {
|
impl<S: SpendAuth> From<SigningPackage> for frost::SigningPackage<S> {
|
||||||
fn from(value: SigningPackage) -> frost::SigningPackage {
|
fn from(value: SigningPackage) -> frost::SigningPackage<S> {
|
||||||
let mut signing_commitments = Vec::new();
|
let mut signing_commitments = Vec::new();
|
||||||
for (participant_id, commitment) in &value.signing_commitments {
|
for (participant_id, commitment) in &value.signing_commitments {
|
||||||
|
// TODO(str4d): This will be so much nicer once const_evaluatable_checked
|
||||||
|
// stabilises, and `GroupEncoding::from_bytes` can take the array directly.
|
||||||
|
let mut hiding_repr = <S::Point as GroupEncoding>::Repr::default();
|
||||||
|
let mut binding_repr = <S::Point as GroupEncoding>::Repr::default();
|
||||||
|
hiding_repr.as_mut().copy_from_slice(&commitment.hiding.0);
|
||||||
|
binding_repr.as_mut().copy_from_slice(&commitment.binding.0);
|
||||||
|
|
||||||
let s = frost::SigningCommitments {
|
let s = frost::SigningCommitments {
|
||||||
index: u64::from(*participant_id),
|
index: u64::from(*participant_id),
|
||||||
// TODO: The `from_bytes()` response is a `CtOption` so we have to `unwrap()`
|
// TODO: The `from_bytes()` response is a `CtOption` so we have to `unwrap()`
|
||||||
hiding: jubjub::ExtendedPoint::from(
|
hiding: S::Point::from_bytes(&hiding_repr).unwrap(),
|
||||||
jubjub::AffinePoint::from_bytes(commitment.hiding.0).unwrap(),
|
binding: S::Point::from_bytes(&binding_repr).unwrap(),
|
||||||
),
|
|
||||||
binding: jubjub::ExtendedPoint::from(
|
|
||||||
jubjub::AffinePoint::from_bytes(commitment.binding.0).unwrap(),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
signing_commitments.push(s);
|
signing_commitments.push(s);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
validate::{MsgErr, Validate},
|
validate::{MsgErr, Validate},
|
||||||
*,
|
*,
|
||||||
},
|
},
|
||||||
verification_key,
|
sapling, verification_key,
|
||||||
};
|
};
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
@ -53,8 +53,12 @@ fn validate_sender_receiver() {
|
||||||
#[test]
|
#[test]
|
||||||
fn validate_sharepackage() {
|
fn validate_sharepackage() {
|
||||||
let setup = basic_setup();
|
let setup = basic_setup();
|
||||||
let (mut shares, _pubkeys) =
|
let (mut shares, _pubkeys) = frost::keygen_with_dealer::<_, sapling::SpendAuth>(
|
||||||
frost::keygen_with_dealer(setup.num_signers, setup.threshold, setup.rng.clone()).unwrap();
|
setup.num_signers,
|
||||||
|
setup.threshold,
|
||||||
|
setup.rng.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let header = create_valid_header(setup.signer1, setup.signer2);
|
let header = create_valid_header(setup.signer1, setup.signer2);
|
||||||
|
|
||||||
|
@ -130,8 +134,12 @@ fn validate_sharepackage() {
|
||||||
fn serialize_sharepackage() {
|
fn serialize_sharepackage() {
|
||||||
let setup = basic_setup();
|
let setup = basic_setup();
|
||||||
|
|
||||||
let (mut shares, _pubkeys) =
|
let (mut shares, _pubkeys) = frost::keygen_with_dealer::<_, sapling::SpendAuth>(
|
||||||
frost::keygen_with_dealer(setup.num_signers, setup.threshold, setup.rng.clone()).unwrap();
|
setup.num_signers,
|
||||||
|
setup.threshold,
|
||||||
|
setup.rng.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let header = create_valid_header(setup.dealer, setup.signer1);
|
let header = create_valid_header(setup.dealer, setup.signer1);
|
||||||
|
|
||||||
|
@ -196,7 +204,8 @@ fn serialize_sharepackage() {
|
||||||
fn validate_signingcommitments() {
|
fn validate_signingcommitments() {
|
||||||
let mut setup = basic_setup();
|
let mut setup = basic_setup();
|
||||||
|
|
||||||
let (_nonce, commitment) = frost::preprocess(1, u64::from(setup.signer1), &mut setup.rng);
|
let (_nonce, commitment) =
|
||||||
|
frost::preprocess::<_, sapling::SpendAuth>(1, u64::from(setup.signer1), &mut setup.rng);
|
||||||
|
|
||||||
let header = create_valid_header(setup.aggregator, setup.signer2);
|
let header = create_valid_header(setup.aggregator, setup.signer2);
|
||||||
|
|
||||||
|
@ -236,7 +245,8 @@ fn validate_signingcommitments() {
|
||||||
fn serialize_signingcommitments() {
|
fn serialize_signingcommitments() {
|
||||||
let mut setup = basic_setup();
|
let mut setup = basic_setup();
|
||||||
|
|
||||||
let (_nonce, commitment) = frost::preprocess(1, u64::from(setup.signer1), &mut setup.rng);
|
let (_nonce, commitment) =
|
||||||
|
frost::preprocess::<_, sapling::SpendAuth>(1, u64::from(setup.signer1), &mut setup.rng);
|
||||||
|
|
||||||
let header = create_valid_header(setup.aggregator, setup.signer1);
|
let header = create_valid_header(setup.aggregator, setup.signer1);
|
||||||
|
|
||||||
|
@ -724,16 +734,16 @@ fn basic_setup() -> Setup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_setup() -> (Setup, signature::Signature<SpendAuth>) {
|
fn full_setup() -> (Setup, signature::Signature<sapling::SpendAuth>) {
|
||||||
let mut setup = basic_setup();
|
let mut setup = basic_setup();
|
||||||
|
|
||||||
// aggregator creates the shares and pubkeys for this round
|
// aggregator creates the shares and pubkeys for this round
|
||||||
let (shares, pubkeys) =
|
let (shares, pubkeys) =
|
||||||
frost::keygen_with_dealer(setup.num_signers, setup.threshold, setup.rng.clone()).unwrap();
|
frost::keygen_with_dealer(setup.num_signers, setup.threshold, setup.rng.clone()).unwrap();
|
||||||
|
|
||||||
let mut nonces: std::collections::HashMap<u64, Vec<frost::SigningNonces>> =
|
let mut nonces: std::collections::HashMap<u64, Vec<frost::SigningNonces<sapling::SpendAuth>>> =
|
||||||
std::collections::HashMap::with_capacity(setup.threshold as usize);
|
std::collections::HashMap::with_capacity(setup.threshold as usize);
|
||||||
let mut commitments: Vec<frost::SigningCommitments> =
|
let mut commitments: Vec<frost::SigningCommitments<sapling::SpendAuth>> =
|
||||||
Vec::with_capacity(setup.threshold as usize);
|
Vec::with_capacity(setup.threshold as usize);
|
||||||
|
|
||||||
// aggregator generates nonces and signing commitments for each participant.
|
// aggregator generates nonces and signing commitments for each participant.
|
||||||
|
@ -744,7 +754,7 @@ fn full_setup() -> (Setup, signature::Signature<SpendAuth>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// aggregator generates a signing package
|
// aggregator generates a signing package
|
||||||
let mut signature_shares: Vec<frost::SignatureShare> =
|
let mut signature_shares: Vec<frost::SignatureShare<sapling::SpendAuth>> =
|
||||||
Vec::with_capacity(setup.threshold as usize);
|
Vec::with_capacity(setup.threshold as usize);
|
||||||
let message = "message to sign".as_bytes().to_vec();
|
let message = "message to sign".as_bytes().to_vec();
|
||||||
let signing_package = frost::SigningPackage {
|
let signing_package = frost::SigningPackage {
|
||||||
|
@ -770,7 +780,7 @@ fn full_setup() -> (Setup, signature::Signature<SpendAuth>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_share_commitment(
|
fn generate_share_commitment(
|
||||||
shares: &Vec<frost::SharePackage>,
|
shares: &Vec<frost::SharePackage<sapling::SpendAuth>>,
|
||||||
participants: Vec<ParticipantId>,
|
participants: Vec<ParticipantId>,
|
||||||
) -> BTreeMap<ParticipantId, Commitment> {
|
) -> BTreeMap<ParticipantId, Commitment> {
|
||||||
assert_eq!(shares.len(), participants.len());
|
assert_eq!(shares.len(), participants.len());
|
||||||
|
@ -787,7 +797,7 @@ fn generate_share_commitment(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_signing_commitments(
|
fn create_signing_commitments(
|
||||||
commitments: Vec<frost::SigningCommitments>,
|
commitments: Vec<frost::SigningCommitments<sapling::SpendAuth>>,
|
||||||
participants: Vec<ParticipantId>,
|
participants: Vec<ParticipantId>,
|
||||||
) -> BTreeMap<ParticipantId, SigningCommitments> {
|
) -> BTreeMap<ParticipantId, SigningCommitments> {
|
||||||
assert_eq!(commitments.len(), participants.len());
|
assert_eq!(commitments.len(), participants.len());
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
//! Signature types for the Sapling protocol.
|
||||||
|
|
||||||
|
use super::SigType;
|
||||||
|
|
||||||
|
/// A type variable corresponding to Zcash's Sapling `SpendAuthSig`.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum SpendAuth {}
|
||||||
|
// This should not exist, but is necessary to use zeroize::DefaultIsZeroes.
|
||||||
|
impl Default for SpendAuth {
|
||||||
|
fn default() -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SigType for SpendAuth {}
|
||||||
|
impl super::SpendAuth for SpendAuth {}
|
||||||
|
|
||||||
|
/// A type variable corresponding to Zcash's Sapling `BindingSig`.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum Binding {}
|
||||||
|
// This should not exist, but is necessary to use zeroize::DefaultIsZeroes.
|
||||||
|
impl Default for Binding {
|
||||||
|
fn default() -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SigType for Binding {}
|
||||||
|
impl super::Binding for Binding {}
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
use std::{borrow::Borrow, fmt::Debug};
|
use std::{borrow::Borrow, fmt::Debug};
|
||||||
|
|
||||||
use jubjub::*;
|
use jubjub::{ExtendedNielsPoint, ExtendedPoint};
|
||||||
|
|
||||||
pub trait NonAdjacentForm {
|
pub trait NonAdjacentForm {
|
||||||
fn non_adjacent_form(&self, w: usize) -> [i8; 256];
|
fn non_adjacent_form(&self, w: usize) -> [i8; 256];
|
||||||
|
@ -20,7 +20,9 @@ pub trait NonAdjacentForm {
|
||||||
|
|
||||||
/// A trait for variable-time multiscalar multiplication without precomputation.
|
/// A trait for variable-time multiscalar multiplication without precomputation.
|
||||||
pub trait VartimeMultiscalarMul {
|
pub trait VartimeMultiscalarMul {
|
||||||
/// The type of point being multiplied, e.g., `AffinePoint`.
|
/// The type of scalar being multiplied, e.g., `jubjub::Scalar`.
|
||||||
|
type Scalar;
|
||||||
|
/// The type of point being multiplied, e.g., `jubjub::AffinePoint`.
|
||||||
type Point;
|
type Point;
|
||||||
|
|
||||||
/// Given an iterator of public scalars and an iterator of
|
/// Given an iterator of public scalars and an iterator of
|
||||||
|
@ -32,7 +34,7 @@ pub trait VartimeMultiscalarMul {
|
||||||
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<Self::Point>
|
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<Self::Point>
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Self::Scalar>,
|
||||||
J: IntoIterator<Item = Option<Self::Point>>;
|
J: IntoIterator<Item = Option<Self::Point>>;
|
||||||
|
|
||||||
/// Given an iterator of public scalars and an iterator of
|
/// Given an iterator of public scalars and an iterator of
|
||||||
|
@ -46,7 +48,7 @@ pub trait VartimeMultiscalarMul {
|
||||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Self::Scalar>,
|
||||||
J: IntoIterator,
|
J: IntoIterator,
|
||||||
J::Item: Borrow<Self::Point>,
|
J::Item: Borrow<Self::Point>,
|
||||||
Self::Point: Clone,
|
Self::Point: Clone,
|
||||||
|
@ -59,7 +61,7 @@ pub trait VartimeMultiscalarMul {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NonAdjacentForm for Scalar {
|
impl NonAdjacentForm for jubjub::Scalar {
|
||||||
/// Compute a width-\\(w\\) "Non-Adjacent Form" of this scalar.
|
/// Compute a width-\\(w\\) "Non-Adjacent Form" of this scalar.
|
||||||
///
|
///
|
||||||
/// Thanks to curve25519-dalek
|
/// Thanks to curve25519-dalek
|
||||||
|
@ -155,13 +157,14 @@ impl<'a> From<&'a ExtendedPoint> for LookupTable5<ExtendedNielsPoint> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VartimeMultiscalarMul for ExtendedPoint {
|
impl VartimeMultiscalarMul for ExtendedPoint {
|
||||||
|
type Scalar = jubjub::Scalar;
|
||||||
type Point = ExtendedPoint;
|
type Point = ExtendedPoint;
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<ExtendedPoint>
|
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<ExtendedPoint>
|
||||||
where
|
where
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: Borrow<Scalar>,
|
I::Item: Borrow<Self::Scalar>,
|
||||||
J: IntoIterator<Item = Option<ExtendedPoint>>,
|
J: IntoIterator<Item = Option<ExtendedPoint>>,
|
||||||
{
|
{
|
||||||
let nafs: Vec<_> = scalars
|
let nafs: Vec<_> = scalars
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
// Authors:
|
// Authors:
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
//! Redjubjub Signatures
|
//! RedDSA Signatures
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::SigType;
|
use crate::SigType;
|
||||||
|
|
||||||
/// A RedJubJub signature.
|
/// A RedDSA signature.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct Signature<T: SigType> {
|
pub struct Signature<T: SigType> {
|
||||||
|
|
|
@ -13,19 +13,21 @@ use std::{
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Error, Randomizer, SigType, Signature, SpendAuth, VerificationKey};
|
use crate::{
|
||||||
|
private::SealedScalar, Error, Randomizer, SigType, Signature, SpendAuth, VerificationKey,
|
||||||
|
};
|
||||||
|
|
||||||
use jubjub::Scalar;
|
use group::{ff::PrimeField, GroupEncoding};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
/// A RedJubJub signing key.
|
/// A RedDSA signing key.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(try_from = "SerdeHelper"))]
|
#[cfg_attr(feature = "serde", serde(try_from = "SerdeHelper"))]
|
||||||
#[cfg_attr(feature = "serde", serde(into = "SerdeHelper"))]
|
#[cfg_attr(feature = "serde", serde(into = "SerdeHelper"))]
|
||||||
#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))]
|
#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))]
|
||||||
pub struct SigningKey<T: SigType> {
|
pub struct SigningKey<T: SigType> {
|
||||||
sk: Scalar,
|
sk: T::Scalar,
|
||||||
pk: VerificationKey<T>,
|
pk: VerificationKey<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ impl<'a, T: SigType> From<&'a SigningKey<T>> for VerificationKey<T> {
|
||||||
|
|
||||||
impl<T: SigType> From<SigningKey<T>> for [u8; 32] {
|
impl<T: SigType> From<SigningKey<T>> for [u8; 32] {
|
||||||
fn from(sk: SigningKey<T>) -> [u8; 32] {
|
fn from(sk: SigningKey<T>) -> [u8; 32] {
|
||||||
sk.sk.to_bytes()
|
sk.sk.to_repr().as_ref().try_into().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +48,9 @@ impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
|
||||||
|
|
||||||
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
||||||
// XXX-jubjub: this should not use CtOption
|
// XXX-jubjub: this should not use CtOption
|
||||||
let maybe_sk = Scalar::from_bytes(&bytes);
|
let mut repr = <T::Scalar as PrimeField>::Repr::default();
|
||||||
|
repr.as_mut().copy_from_slice(&bytes);
|
||||||
|
let maybe_sk = T::Scalar::from_repr(repr);
|
||||||
if maybe_sk.is_some().into() {
|
if maybe_sk.is_some().into() {
|
||||||
let sk = maybe_sk.unwrap();
|
let sk = maybe_sk.unwrap();
|
||||||
let pk = VerificationKey::from(&sk);
|
let pk = VerificationKey::from(&sk);
|
||||||
|
@ -74,10 +78,10 @@ impl<T: SigType> From<SigningKey<T>> for SerdeHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SigningKey<SpendAuth> {
|
impl<T: SpendAuth> SigningKey<T> {
|
||||||
/// Randomize this public key with the given `randomizer`.
|
/// Randomize this public key with the given `randomizer`.
|
||||||
pub fn randomize(&self, randomizer: &Randomizer) -> SigningKey<SpendAuth> {
|
pub fn randomize(&self, randomizer: &Randomizer<T>) -> SigningKey<T> {
|
||||||
let sk = &self.sk + randomizer;
|
let sk = self.sk + randomizer;
|
||||||
let pk = VerificationKey::from(&sk);
|
let pk = VerificationKey::from(&sk);
|
||||||
SigningKey { sk, pk }
|
SigningKey { sk, pk }
|
||||||
}
|
}
|
||||||
|
@ -89,7 +93,7 @@ impl<T: SigType> SigningKey<T> {
|
||||||
let sk = {
|
let sk = {
|
||||||
let mut bytes = [0; 64];
|
let mut bytes = [0; 64];
|
||||||
rng.fill_bytes(&mut bytes);
|
rng.fill_bytes(&mut bytes);
|
||||||
Scalar::from_bytes_wide(&bytes)
|
T::Scalar::from_bytes_wide(&bytes)
|
||||||
};
|
};
|
||||||
let pk = VerificationKey::from(&sk);
|
let pk = VerificationKey::from(&sk);
|
||||||
SigningKey { sk, pk }
|
SigningKey { sk, pk }
|
||||||
|
@ -101,28 +105,31 @@ impl<T: SigType> SigningKey<T> {
|
||||||
use crate::HStar;
|
use crate::HStar;
|
||||||
|
|
||||||
// Choose a byte sequence uniformly at random of length
|
// Choose a byte sequence uniformly at random of length
|
||||||
// (\ell_H + 128)/8 bytes. For RedJubjub this is (512 + 128)/8 = 80.
|
// (\ell_H + 128)/8 bytes. For RedJubjub and RedPallas this is
|
||||||
|
// (512 + 128)/8 = 80.
|
||||||
let random_bytes = {
|
let random_bytes = {
|
||||||
let mut bytes = [0; 80];
|
let mut bytes = [0; 80];
|
||||||
rng.fill_bytes(&mut bytes);
|
rng.fill_bytes(&mut bytes);
|
||||||
bytes
|
bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
let nonce = HStar::default()
|
let nonce = HStar::<T>::default()
|
||||||
.update(&random_bytes[..])
|
.update(&random_bytes[..])
|
||||||
.update(&self.pk.bytes.bytes[..]) // XXX ugly
|
.update(&self.pk.bytes.bytes[..]) // XXX ugly
|
||||||
.update(msg)
|
.update(msg)
|
||||||
.finalize();
|
.finalize();
|
||||||
|
|
||||||
let r_bytes = jubjub::AffinePoint::from(&T::basepoint() * &nonce).to_bytes();
|
let r: T::Point = T::basepoint() * nonce;
|
||||||
|
let r_bytes: [u8; 32] = r.to_bytes().as_ref().try_into().unwrap();
|
||||||
|
|
||||||
let c = HStar::default()
|
let c = HStar::<T>::default()
|
||||||
.update(&r_bytes[..])
|
.update(&r_bytes[..])
|
||||||
.update(&self.pk.bytes.bytes[..]) // XXX ugly
|
.update(&self.pk.bytes.bytes[..]) // XXX ugly
|
||||||
.update(msg)
|
.update(msg)
|
||||||
.finalize();
|
.finalize();
|
||||||
|
|
||||||
let s_bytes = (&nonce + &(&c * &self.sk)).to_bytes();
|
let s = nonce + (c * self.sk);
|
||||||
|
let s_bytes = s.to_repr().as_ref().try_into().unwrap();
|
||||||
|
|
||||||
Signature {
|
Signature {
|
||||||
r_bytes,
|
r_bytes,
|
||||||
|
|
|
@ -9,17 +9,17 @@
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
convert::{TryFrom, TryInto},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use jubjub::Scalar;
|
use group::{cofactor::CofactorGroup, ff::PrimeField, GroupEncoding};
|
||||||
|
|
||||||
use crate::{Error, Randomizer, SigType, Signature, SpendAuth};
|
use crate::{Error, Randomizer, SigType, Signature, SpendAuth};
|
||||||
|
|
||||||
/// A refinement type for `[u8; 32]` indicating that the bytes represent
|
/// A refinement type for `[u8; 32]` indicating that the bytes represent
|
||||||
/// an encoding of a RedJubJub verification key.
|
/// an encoding of a RedDSA verification key.
|
||||||
///
|
///
|
||||||
/// This is useful for representing a compressed verification key; the
|
/// This is useful for representing a compressed verification key; the
|
||||||
/// [`VerificationKey`] type in this library holds other decompressed state
|
/// [`VerificationKey`] type in this library holds other decompressed state
|
||||||
|
@ -53,7 +53,7 @@ impl<T: SigType> Hash for VerificationKeyBytes<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A valid RedJubJub verification key.
|
/// A valid RedDSA verification key.
|
||||||
///
|
///
|
||||||
/// This type holds decompressed state used in signature verification; if the
|
/// This type holds decompressed state used in signature verification; if the
|
||||||
/// verification key may not be used immediately, it is probably better to use
|
/// verification key may not be used immediately, it is probably better to use
|
||||||
|
@ -72,8 +72,7 @@ impl<T: SigType> Hash for VerificationKeyBytes<T> {
|
||||||
#[cfg_attr(feature = "serde", serde(into = "VerificationKeyBytes<T>"))]
|
#[cfg_attr(feature = "serde", serde(into = "VerificationKeyBytes<T>"))]
|
||||||
#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))]
|
#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))]
|
||||||
pub struct VerificationKey<T: SigType> {
|
pub struct VerificationKey<T: SigType> {
|
||||||
// XXX-jubjub: this should just be Point
|
pub(crate) point: T::Point,
|
||||||
pub(crate) point: jubjub::ExtendedPoint,
|
|
||||||
pub(crate) bytes: VerificationKeyBytes<T>,
|
pub(crate) bytes: VerificationKeyBytes<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,9 +95,11 @@ impl<T: SigType> TryFrom<VerificationKeyBytes<T>> for VerificationKey<T> {
|
||||||
// XXX-jubjub: this should not use CtOption
|
// XXX-jubjub: this should not use CtOption
|
||||||
// XXX-jubjub: this takes ownership of bytes, while Fr doesn't.
|
// XXX-jubjub: this takes ownership of bytes, while Fr doesn't.
|
||||||
// This checks that the encoding is canonical...
|
// This checks that the encoding is canonical...
|
||||||
let maybe_point = jubjub::AffinePoint::from_bytes(bytes.bytes);
|
let mut repr = <T::Point as GroupEncoding>::Repr::default();
|
||||||
|
repr.as_mut().copy_from_slice(&bytes.bytes);
|
||||||
|
let maybe_point = T::Point::from_bytes(&repr);
|
||||||
if maybe_point.is_some().into() {
|
if maybe_point.is_some().into() {
|
||||||
let point: jubjub::ExtendedPoint = maybe_point.unwrap().into();
|
let point = maybe_point.unwrap();
|
||||||
// Note that small-order verification keys (including the identity) are not
|
// Note that small-order verification keys (including the identity) are not
|
||||||
// rejected here. Previously they were rejected, but this was a bug as the
|
// rejected here. Previously they were rejected, but this was a bug as the
|
||||||
// RedDSA specification allows them. Zcash Sapling rejects small-order points
|
// RedDSA specification allows them. Zcash Sapling rejects small-order points
|
||||||
|
@ -116,20 +117,18 @@ impl<T: SigType> TryFrom<[u8; 32]> for VerificationKey<T> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
|
||||||
use std::convert::TryInto;
|
|
||||||
VerificationKeyBytes::from(bytes).try_into()
|
VerificationKeyBytes::from(bytes).try_into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationKey<SpendAuth> {
|
impl<T: SpendAuth> VerificationKey<T> {
|
||||||
/// Randomize this verification key with the given `randomizer`.
|
/// Randomize this verification key with the given `randomizer`.
|
||||||
///
|
///
|
||||||
/// Randomization is only supported for `SpendAuth` keys.
|
/// Randomization is only supported for `SpendAuth` keys.
|
||||||
pub fn randomize(&self, randomizer: &Randomizer) -> VerificationKey<SpendAuth> {
|
pub fn randomize(&self, randomizer: &Randomizer<T>) -> VerificationKey<T> {
|
||||||
use crate::private::Sealed;
|
let point = self.point + (T::basepoint() * randomizer);
|
||||||
let point = &self.point + &(&SpendAuth::basepoint() * randomizer);
|
|
||||||
let bytes = VerificationKeyBytes {
|
let bytes = VerificationKeyBytes {
|
||||||
bytes: jubjub::AffinePoint::from(&point).to_bytes(),
|
bytes: point.to_bytes().as_ref().try_into().unwrap(),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
VerificationKey { bytes, point }
|
VerificationKey { bytes, point }
|
||||||
|
@ -137,10 +136,10 @@ impl VerificationKey<SpendAuth> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SigType> VerificationKey<T> {
|
impl<T: SigType> VerificationKey<T> {
|
||||||
pub(crate) fn from(s: &Scalar) -> VerificationKey<T> {
|
pub(crate) fn from(s: &T::Scalar) -> VerificationKey<T> {
|
||||||
let point = &T::basepoint() * s;
|
let point = T::basepoint() * s;
|
||||||
let bytes = VerificationKeyBytes {
|
let bytes = VerificationKeyBytes {
|
||||||
bytes: jubjub::AffinePoint::from(&point).to_bytes(),
|
bytes: point.to_bytes().as_ref().try_into().unwrap(),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
VerificationKey { bytes, point }
|
VerificationKey { bytes, point }
|
||||||
|
@ -150,7 +149,7 @@ impl<T: SigType> VerificationKey<T> {
|
||||||
// This is similar to impl signature::Verifier but without boxed errors
|
// This is similar to impl signature::Verifier but without boxed errors
|
||||||
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), Error> {
|
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), Error> {
|
||||||
use crate::HStar;
|
use crate::HStar;
|
||||||
let c = HStar::default()
|
let c = HStar::<T>::default()
|
||||||
.update(&signature.r_bytes[..])
|
.update(&signature.r_bytes[..])
|
||||||
.update(&self.bytes.bytes[..]) // XXX ugly
|
.update(&self.bytes.bytes[..]) // XXX ugly
|
||||||
.update(msg)
|
.update(msg)
|
||||||
|
@ -163,14 +162,16 @@ impl<T: SigType> VerificationKey<T> {
|
||||||
pub(crate) fn verify_prehashed(
|
pub(crate) fn verify_prehashed(
|
||||||
&self,
|
&self,
|
||||||
signature: &Signature<T>,
|
signature: &Signature<T>,
|
||||||
c: Scalar,
|
c: T::Scalar,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let r = {
|
let r = {
|
||||||
// XXX-jubjub: should not use CtOption here
|
// XXX-jubjub: should not use CtOption here
|
||||||
// XXX-jubjub: inconsistent ownership in from_bytes
|
// XXX-jubjub: inconsistent ownership in from_bytes
|
||||||
let maybe_point = jubjub::AffinePoint::from_bytes(signature.r_bytes);
|
let mut repr = <T::Point as GroupEncoding>::Repr::default();
|
||||||
|
repr.as_mut().copy_from_slice(&signature.r_bytes);
|
||||||
|
let maybe_point = T::Point::from_bytes(&repr);
|
||||||
if maybe_point.is_some().into() {
|
if maybe_point.is_some().into() {
|
||||||
jubjub::ExtendedPoint::from(maybe_point.unwrap())
|
maybe_point.unwrap()
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::InvalidSignature);
|
return Err(Error::InvalidSignature);
|
||||||
}
|
}
|
||||||
|
@ -178,7 +179,9 @@ impl<T: SigType> VerificationKey<T> {
|
||||||
|
|
||||||
let s = {
|
let s = {
|
||||||
// XXX-jubjub: should not use CtOption here
|
// XXX-jubjub: should not use CtOption here
|
||||||
let maybe_scalar = Scalar::from_bytes(&signature.s_bytes);
|
let mut repr = <T::Scalar as PrimeField>::Repr::default();
|
||||||
|
repr.as_mut().copy_from_slice(&signature.s_bytes);
|
||||||
|
let maybe_scalar = T::Scalar::from_repr(repr);
|
||||||
if maybe_scalar.is_some().into() {
|
if maybe_scalar.is_some().into() {
|
||||||
maybe_scalar.unwrap()
|
maybe_scalar.unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,8 +192,8 @@ impl<T: SigType> VerificationKey<T> {
|
||||||
// XXX rewrite as normal double scalar mul
|
// XXX rewrite as normal double scalar mul
|
||||||
// Verify check is h * ( - s * B + R + c * A) == 0
|
// Verify check is h * ( - s * B + R + c * A) == 0
|
||||||
// h * ( s * B - c * A - R) == 0
|
// h * ( s * B - c * A - R) == 0
|
||||||
let sB = &T::basepoint() * &s;
|
let sB = T::basepoint() * s;
|
||||||
let cA = &self.point * &c;
|
let cA = self.point * c;
|
||||||
let check = sB - cA - r;
|
let check = sB - cA - r;
|
||||||
|
|
||||||
if check.is_small_order().into() {
|
if check.is_small_order().into() {
|
||||||
|
|
|
@ -5,13 +5,13 @@ use reddsa::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn spendauth_batch_verify() {
|
fn spendauth_batch_verify() {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let mut batch = batch::Verifier::new();
|
let mut batch = batch::Verifier::<_, sapling::Binding>::new();
|
||||||
for _ in 0..32 {
|
for _ in 0..32 {
|
||||||
let sk = SigningKey::<SpendAuth>::new(&mut rng);
|
let sk = SigningKey::<sapling::SpendAuth>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = sk.sign(&mut rng, &msg[..]);
|
let sig = sk.sign(&mut rng, &msg[..]);
|
||||||
batch.queue((vk.into(), sig, msg));
|
batch.queue(batch::Item::from_spendauth(vk.into(), sig, msg));
|
||||||
}
|
}
|
||||||
assert!(batch.verify(rng).is_ok());
|
assert!(batch.verify(rng).is_ok());
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ fn spendauth_batch_verify() {
|
||||||
#[test]
|
#[test]
|
||||||
fn binding_batch_verify() {
|
fn binding_batch_verify() {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let mut batch = batch::Verifier::new();
|
let mut batch = batch::Verifier::<sapling::SpendAuth, _>::new();
|
||||||
for _ in 0..32 {
|
for _ in 0..32 {
|
||||||
let sk = SigningKey::<Binding>::new(&mut rng);
|
let sk = SigningKey::<sapling::Binding>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = sk.sign(&mut rng, &msg[..]);
|
let sig = sk.sign(&mut rng, &msg[..]);
|
||||||
batch.queue((vk.into(), sig, msg));
|
batch.queue(batch::Item::from_binding(vk.into(), sig, msg));
|
||||||
}
|
}
|
||||||
assert!(batch.verify(rng).is_ok());
|
assert!(batch.verify(rng).is_ok());
|
||||||
}
|
}
|
||||||
|
@ -35,20 +35,20 @@ fn alternating_batch_verify() {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let mut batch = batch::Verifier::new();
|
let mut batch = batch::Verifier::new();
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
let item: batch::Item = match i % 2 {
|
let item = match i % 2 {
|
||||||
0 => {
|
0 => {
|
||||||
let sk = SigningKey::<SpendAuth>::new(&mut rng);
|
let sk = SigningKey::<sapling::SpendAuth>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = sk.sign(&mut rng, &msg[..]);
|
let sig = sk.sign(&mut rng, &msg[..]);
|
||||||
(vk.into(), sig, msg).into()
|
batch::Item::from_spendauth(vk.into(), sig, msg)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let sk = SigningKey::<Binding>::new(&mut rng);
|
let sk = SigningKey::<sapling::Binding>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = sk.sign(&mut rng, &msg[..]);
|
let sig = sk.sign(&mut rng, &msg[..]);
|
||||||
(vk.into(), sig, msg).into()
|
batch::Item::from_binding(vk.into(), sig, msg)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
@ -64,9 +64,9 @@ fn bad_batch_verify() {
|
||||||
let mut batch = batch::Verifier::new();
|
let mut batch = batch::Verifier::new();
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
let item: batch::Item = match i % 2 {
|
let item = match i % 2 {
|
||||||
0 => {
|
0 => {
|
||||||
let sk = SigningKey::<SpendAuth>::new(&mut rng);
|
let sk = SigningKey::<sapling::SpendAuth>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = if i != bad_index {
|
let sig = if i != bad_index {
|
||||||
|
@ -74,14 +74,14 @@ fn bad_batch_verify() {
|
||||||
} else {
|
} else {
|
||||||
sk.sign(&mut rng, b"bad")
|
sk.sign(&mut rng, b"bad")
|
||||||
};
|
};
|
||||||
(vk.into(), sig, msg).into()
|
batch::Item::from_spendauth(vk.into(), sig, msg)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let sk = SigningKey::<Binding>::new(&mut rng);
|
let sk = SigningKey::<sapling::Binding>::new(&mut rng);
|
||||||
let vk = VerificationKey::from(&sk);
|
let vk = VerificationKey::from(&sk);
|
||||||
let msg = b"BatchVerifyTest";
|
let msg = b"BatchVerifyTest";
|
||||||
let sig = sk.sign(&mut rng, &msg[..]);
|
let sig = sk.sign(&mut rng, &msg[..]);
|
||||||
(vk.into(), sig, msg).into()
|
batch::Item::from_binding(vk.into(), sig, msg)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,8 +9,8 @@ proptest! {
|
||||||
fn secretkey_serialization(
|
fn secretkey_serialization(
|
||||||
bytes in prop::array::uniform32(any::<u8>()),
|
bytes in prop::array::uniform32(any::<u8>()),
|
||||||
) {
|
) {
|
||||||
let sk_result_from = SigningKey::<SpendAuth>::try_from(bytes);
|
let sk_result_from = SigningKey::<sapling::SpendAuth>::try_from(bytes);
|
||||||
let sk_result_bincode: Result<SigningKey::<SpendAuth>, _>
|
let sk_result_bincode: Result<SigningKey::<sapling::SpendAuth>, _>
|
||||||
= bincode::deserialize(&bytes[..]);
|
= bincode::deserialize(&bytes[..]);
|
||||||
|
|
||||||
// Check 1: both decoding methods should agree
|
// Check 1: both decoding methods should agree
|
||||||
|
@ -39,8 +39,8 @@ proptest! {
|
||||||
fn publickeybytes_serialization(
|
fn publickeybytes_serialization(
|
||||||
bytes in prop::array::uniform32(any::<u8>()),
|
bytes in prop::array::uniform32(any::<u8>()),
|
||||||
) {
|
) {
|
||||||
let pk_bytes_from = VerificationKeyBytes::<SpendAuth>::from(bytes);
|
let pk_bytes_from = VerificationKeyBytes::<sapling::SpendAuth>::from(bytes);
|
||||||
let pk_bytes_bincode: VerificationKeyBytes::<SpendAuth>
|
let pk_bytes_bincode: VerificationKeyBytes::<sapling::SpendAuth>
|
||||||
= bincode::deserialize(&bytes[..]).unwrap();
|
= bincode::deserialize(&bytes[..]).unwrap();
|
||||||
|
|
||||||
// Check 1: both decoding methods should have the same result.
|
// Check 1: both decoding methods should have the same result.
|
||||||
|
@ -59,8 +59,8 @@ proptest! {
|
||||||
fn publickey_serialization(
|
fn publickey_serialization(
|
||||||
bytes in prop::array::uniform32(any::<u8>()),
|
bytes in prop::array::uniform32(any::<u8>()),
|
||||||
) {
|
) {
|
||||||
let pk_result_try_from = VerificationKey::<SpendAuth>::try_from(bytes);
|
let pk_result_try_from = VerificationKey::<sapling::SpendAuth>::try_from(bytes);
|
||||||
let pk_result_bincode: Result<VerificationKey::<SpendAuth>, _>
|
let pk_result_bincode: Result<VerificationKey::<sapling::SpendAuth>, _>
|
||||||
= bincode::deserialize(&bytes[..]);
|
= bincode::deserialize(&bytes[..]);
|
||||||
|
|
||||||
// Check 1: both decoding methods should have the same result
|
// Check 1: both decoding methods should have the same result
|
||||||
|
@ -93,8 +93,8 @@ proptest! {
|
||||||
bytes
|
bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
let sig_bytes_from = Signature::<SpendAuth>::from(bytes);
|
let sig_bytes_from = Signature::<sapling::SpendAuth>::from(bytes);
|
||||||
let sig_bytes_bincode: Signature::<SpendAuth>
|
let sig_bytes_bincode: Signature::<sapling::SpendAuth>
|
||||||
= bincode::deserialize(&bytes[..]).unwrap();
|
= bincode::deserialize(&bytes[..]).unwrap();
|
||||||
|
|
||||||
// Check 1: both decoding methods should have the same result.
|
// Check 1: both decoding methods should have the same result.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use reddsa::frost;
|
use reddsa::{frost, sapling};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_sign_with_dealer() {
|
fn check_sign_with_dealer() {
|
||||||
|
@ -10,9 +10,10 @@ fn check_sign_with_dealer() {
|
||||||
let threshold = 3;
|
let threshold = 3;
|
||||||
let (shares, pubkeys) = frost::keygen_with_dealer(numsigners, threshold, &mut rng).unwrap();
|
let (shares, pubkeys) = frost::keygen_with_dealer(numsigners, threshold, &mut rng).unwrap();
|
||||||
|
|
||||||
let mut nonces: HashMap<u64, Vec<frost::SigningNonces>> =
|
let mut nonces: HashMap<u64, Vec<frost::SigningNonces<sapling::SpendAuth>>> =
|
||||||
HashMap::with_capacity(threshold as usize);
|
HashMap::with_capacity(threshold as usize);
|
||||||
let mut commitments: Vec<frost::SigningCommitments> = Vec::with_capacity(threshold as usize);
|
let mut commitments: Vec<frost::SigningCommitments<sapling::SpendAuth>> =
|
||||||
|
Vec::with_capacity(threshold as usize);
|
||||||
|
|
||||||
// Round 1, generating nonces and signing commitments for each participant.
|
// Round 1, generating nonces and signing commitments for each participant.
|
||||||
for participant_index in 1..(threshold + 1) {
|
for participant_index in 1..(threshold + 1) {
|
||||||
|
@ -26,7 +27,8 @@ fn check_sign_with_dealer() {
|
||||||
// This is what the signature aggregator / coordinator needs to do:
|
// This is what the signature aggregator / coordinator needs to do:
|
||||||
// - decide what message to sign
|
// - decide what message to sign
|
||||||
// - take one (unused) commitment per signing participant
|
// - take one (unused) commitment per signing participant
|
||||||
let mut signature_shares: Vec<frost::SignatureShare> = Vec::with_capacity(threshold as usize);
|
let mut signature_shares: Vec<frost::SignatureShare<sapling::SpendAuth>> =
|
||||||
|
Vec::with_capacity(threshold as usize);
|
||||||
let message = "message to sign".as_bytes();
|
let message = "message to sign".as_bytes();
|
||||||
let signing_package = frost::SigningPackage {
|
let signing_package = frost::SigningPackage {
|
||||||
message: message.to_vec(),
|
message: message.to_vec(),
|
||||||
|
|
|
@ -26,8 +26,8 @@ fn verify_librustzcash_binding() {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref LIBRUSTZCASH_SPENDAUTH_SIGS: [(
|
static ref LIBRUSTZCASH_SPENDAUTH_SIGS: [(
|
||||||
Vec<u8>,
|
Vec<u8>,
|
||||||
Signature<SpendAuth>,
|
Signature<sapling::SpendAuth>,
|
||||||
VerificationKeyBytes<SpendAuth>
|
VerificationKeyBytes<sapling::SpendAuth>
|
||||||
); 32] = [
|
); 32] = [
|
||||||
(
|
(
|
||||||
[
|
[
|
||||||
|
@ -638,7 +638,11 @@ lazy_static! {
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
static ref LIBRUSTZCASH_BINDING_SIGS: [(Vec<u8>, Signature<Binding>, VerificationKeyBytes<Binding>); 32] = [
|
static ref LIBRUSTZCASH_BINDING_SIGS: [(
|
||||||
|
Vec<u8>,
|
||||||
|
Signature<sapling::Binding>,
|
||||||
|
VerificationKeyBytes<sapling::Binding>
|
||||||
|
); 32] = [
|
||||||
(
|
(
|
||||||
[
|
[
|
||||||
16, 28, 190, 75, 156, 66, 96, 79, 4, 199, 3, 195, 150, 247, 136, 198, 203, 45, 109,
|
16, 28, 190, 75, 156, 66, 96, 79, 4, 199, 3, 195, 150, 247, 136, 198, 203, 45, 109,
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl<T: SigType> SignatureCase<T> {
|
||||||
VerificationKeyBytes::<T>::from(bytes)
|
VerificationKeyBytes::<T>::from(bytes)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check that the verification key is a valid RedJubjub verification key.
|
// Check that the verification key is a valid RedDSA verification key.
|
||||||
let pub_key = VerificationKey::try_from(pk_bytes)
|
let pub_key = VerificationKey::try_from(pk_bytes)
|
||||||
.expect("The test verification key to be well-formed.");
|
.expect("The test verification key to be well-formed.");
|
||||||
|
|
||||||
|
@ -114,8 +114,8 @@ proptest! {
|
||||||
|
|
||||||
// Create a test case for each signature type.
|
// Create a test case for each signature type.
|
||||||
let msg = b"test message for proptests";
|
let msg = b"test message for proptests";
|
||||||
let mut binding = SignatureCase::<Binding>::new(&mut rng, msg.to_vec());
|
let mut binding = SignatureCase::<sapling::Binding>::new(&mut rng, msg.to_vec());
|
||||||
let mut spendauth = SignatureCase::<SpendAuth>::new(&mut rng, msg.to_vec());
|
let mut spendauth = SignatureCase::<sapling::SpendAuth>::new(&mut rng, msg.to_vec());
|
||||||
|
|
||||||
// Apply tweaks to each case.
|
// Apply tweaks to each case.
|
||||||
for t in &tweaks {
|
for t in &tweaks {
|
||||||
|
@ -136,10 +136,10 @@ proptest! {
|
||||||
// XXX-jubjub: better API for this
|
// XXX-jubjub: better API for this
|
||||||
let mut bytes = [0; 64];
|
let mut bytes = [0; 64];
|
||||||
rng.fill_bytes(&mut bytes[..]);
|
rng.fill_bytes(&mut bytes[..]);
|
||||||
Randomizer::from_bytes_wide(&bytes)
|
jubjub::Scalar::from_bytes_wide(&bytes)
|
||||||
};
|
};
|
||||||
|
|
||||||
let sk = SigningKey::<SpendAuth>::new(&mut rng);
|
let sk = SigningKey::<sapling::SpendAuth>::new(&mut rng);
|
||||||
let pk = VerificationKey::from(&sk);
|
let pk = VerificationKey::from(&sk);
|
||||||
|
|
||||||
let sk_r = sk.randomize(&r);
|
let sk_r = sk.randomize(&r);
|
||||||
|
|
|
@ -9,8 +9,8 @@ fn identity_publickey_passes() {
|
||||||
let identity = AffinePoint::identity();
|
let identity = AffinePoint::identity();
|
||||||
assert_eq!(<bool>::from(identity.is_small_order()), true);
|
assert_eq!(<bool>::from(identity.is_small_order()), true);
|
||||||
let bytes = identity.to_bytes();
|
let bytes = identity.to_bytes();
|
||||||
let pk_bytes = VerificationKeyBytes::<SpendAuth>::from(bytes);
|
let pk_bytes = VerificationKeyBytes::<sapling::SpendAuth>::from(bytes);
|
||||||
assert!(VerificationKey::<SpendAuth>::try_from(pk_bytes).is_ok());
|
assert!(VerificationKey::<sapling::SpendAuth>::try_from(pk_bytes).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -19,6 +19,6 @@ fn smallorder_publickey_passes() {
|
||||||
let order4 = AffinePoint::from_raw_unchecked(Fq::one(), Fq::zero());
|
let order4 = AffinePoint::from_raw_unchecked(Fq::one(), Fq::zero());
|
||||||
assert_eq!(<bool>::from(order4.is_small_order()), true);
|
assert_eq!(<bool>::from(order4.is_small_order()), true);
|
||||||
let bytes = order4.to_bytes();
|
let bytes = order4.to_bytes();
|
||||||
let pk_bytes = VerificationKeyBytes::<SpendAuth>::from(bytes);
|
let pk_bytes = VerificationKeyBytes::<sapling::SpendAuth>::from(bytes);
|
||||||
assert!(VerificationKey::<SpendAuth>::try_from(pk_bytes).is_ok());
|
assert!(VerificationKey::<sapling::SpendAuth>::try_from(pk_bytes).is_ok());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue