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:
Jack Grigg 2021-03-01 15:29:07 +00:00
parent 7e80588550
commit 878dd1351b
21 changed files with 471 additions and 357 deletions

View File

@ -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"] }

View File

@ -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))

View File

@ -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));
} }
} }
} }

View File

@ -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(())

View File

@ -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.

View File

@ -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(()));

View File

@ -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())
} }
} }

View File

@ -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()

View File

@ -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);
} }

View File

@ -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());

27
src/sapling.rs Normal file
View File

@ -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 {}

View File

@ -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

View File

@ -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> {

View File

@ -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,

View File

@ -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() {

View File

@ -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!(),
}; };

View File

@ -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.

View File

@ -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(),

View File

@ -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,

View File

@ -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);

View File

@ -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());
} }