From 602097a85494cc41e491729e57f960ff1011db12 Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Mon, 3 May 2021 10:51:32 -0400 Subject: [PATCH 1/5] Flesh out redpallas, direct port of redjubjub Minus batching and the multiscalar multiplication required for that, as the math needs updating or replacing completely. --- Cargo.lock | 3 + zebra-chain/Cargo.toml | 4 +- zebra-chain/src/primitives/redpallas.rs | 24 ++- .../src/primitives/redpallas/constants.rs | 10 +- zebra-chain/src/primitives/redpallas/hash.rs | 40 +++++ .../src/primitives/redpallas/signature.rs | 45 +++++ .../src/primitives/redpallas/signing_key.rs | 96 ++++++++++- zebra-chain/src/primitives/redpallas/tests.rs | 2 + .../primitives/redpallas/tests/basepoints.rs | 20 +++ .../src/primitives/redpallas/tests/prop.rs | 155 ++++++++++++++++++ .../primitives/redpallas/verification_key.rs | 100 ++++++++--- 11 files changed, 465 insertions(+), 34 deletions(-) create mode 100644 zebra-chain/src/primitives/redpallas/hash.rs create mode 100644 zebra-chain/src/primitives/redpallas/signature.rs create mode 100644 zebra-chain/src/primitives/redpallas/tests.rs create mode 100644 zebra-chain/src/primitives/redpallas/tests/basepoints.rs create mode 100644 zebra-chain/src/primitives/redpallas/tests/prop.rs diff --git a/Cargo.lock b/Cargo.lock index 086ee0132cb..7be7386bdcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4427,6 +4427,8 @@ dependencies = [ "primitive-types", "proptest", "proptest-derive", + "rand 0.8.1", + "rand_chacha 0.3.0", "rand_core 0.6.2", "redjubjub", "ripemd160", @@ -4457,6 +4459,7 @@ dependencies = [ "displaydoc", "futures 0.3.14", "futures-util", + "halo2", "jubjub", "lazy_static", "metrics", diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index 90e7268a6ec..ab9dfdaf4de 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -59,12 +59,14 @@ zebra-test = { path = "../zebra-test/", optional = true } bincode = "1" color-eyre = "0.5.11" criterion = { version = "0.3", features = ["html_reports"] } +itertools = "0.10.0" spandoc = "0.2" tracing = "0.1.25" proptest = "0.10" proptest-derive = "0.3" -itertools = "0.10.0" +rand = "0.8" +rand_chacha = "0.3" zebra-test = { path = "../zebra-test/" } diff --git a/zebra-chain/src/primitives/redpallas.rs b/zebra-chain/src/primitives/redpallas.rs index 17e986ecaa5..b625b807d5f 100644 --- a/zebra-chain/src/primitives/redpallas.rs +++ b/zebra-chain/src/primitives/redpallas.rs @@ -1,19 +1,35 @@ -// XXX: Extracted from redjubjub for now. +// -*- mode: rust; -*- +// +// This file is part of redjubjub. +// Copyright (c) 2019-2021 Zcash Foundation +// See LICENSE for licensing information. +// +// Authors: +// - Deirdre Connolly +// - Henry de Valence use group::GroupEncoding; use halo2::pasta::pallas; -// pub mod batch; mod constants; mod error; -// TODO: full redpallas implementation https://github.com/ZcashFoundation/zebra/issues/2044 +mod hash; +mod signature; mod signing_key; +#[cfg(test)] +mod tests; mod verification_key; pub use error::Error; +pub use hash::HStar; +pub use signature::Signature; pub use signing_key::SigningKey; pub use verification_key::{VerificationKey, VerificationKeyBytes}; +/// An element of the Pallas scalar field used for randomization of verification +/// and signing keys. +pub type Randomizer = pallas::Scalar; + /// Abstracts over different RedPallas parameter choices, [`Binding`] /// and [`SpendAuth`]. /// @@ -25,7 +41,7 @@ pub use verification_key::{VerificationKey, VerificationKeyBytes}; /// To handle this, we encode the parameter choice as a genuine type /// parameter. /// -/// [concretereddsa]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa +/// [concretereddsa]: https://zips.z.cash/protocol/nu5.pdf#concretereddsa pub trait SigType: private::Sealed {} /// A type variable corresponding to Zcash's `BindingSig`. diff --git a/zebra-chain/src/primitives/redpallas/constants.rs b/zebra-chain/src/primitives/redpallas/constants.rs index 54bb9d58d5b..a10863b711b 100644 --- a/zebra-chain/src/primitives/redpallas/constants.rs +++ b/zebra-chain/src/primitives/redpallas/constants.rs @@ -1,6 +1,6 @@ // -*- mode: rust; -*- // -// This file is part of redjubjub. +// This file is part of redpallas. // Copyright (c) 2019-2021 Zcash Foundation // See LICENSE for licensing information. // @@ -11,12 +11,12 @@ /// /// [pallasandvesta]: https://zips.z.cash/protocol/nu5.pdf#pallasandvesta pub const SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [ - 215, 148, 162, 4, 167, 65, 231, 17, 216, 7, 4, 206, 68, 161, 32, 20, 67, 192, 174, 143, 131, - 35, 240, 117, 113, 113, 7, 198, 56, 190, 133, 53, + 99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95, 62, 124, + 24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183, ]; /// The byte-encoding of the basepoint for `BindingSig` on the Pallas curve. pub const BINDINGSIG_BASEPOINT_BYTES: [u8; 32] = [ - 48, 181, 242, 170, 173, 50, 86, 48, 188, 221, 219, 206, 77, 103, 101, 109, 5, 253, 28, 194, - 208, 55, 187, 83, 117, 182, 233, 109, 158, 1, 161, 215, + 145, 90, 60, 136, 104, 198, 195, 14, 47, 128, 144, 238, 69, 215, 110, 64, 72, 32, 141, 234, 91, + 35, 102, 79, 187, 9, 164, 15, 85, 68, 244, 7, ]; diff --git a/zebra-chain/src/primitives/redpallas/hash.rs b/zebra-chain/src/primitives/redpallas/hash.rs new file mode 100644 index 00000000000..757acac5fd9 --- /dev/null +++ b/zebra-chain/src/primitives/redpallas/hash.rs @@ -0,0 +1,40 @@ +// -*- mode: rust; -*- +// +// This file is part of redpallas. +// Copyright (c) 2019-2021 Zcash Foundation +// See LICENSE for licensing information. +// +// Authors: +// - Deirdre Connolly +// - Henry de Valence + +use blake2b_simd::{Params, State}; +use halo2::{arithmetic::FieldExt, pasta::pallas::Scalar}; + +/// Provides H^star, the hash-to-scalar function used by RedPallas. +pub struct HStar { + state: State, +} + +impl Default for HStar { + fn default() -> Self { + let state = Params::new() + .hash_length(64) + .personal(b"Zcash_RedPallasH") + .to_state(); + Self { state } + } +} + +impl HStar { + /// Add `data` to the hash, and return `Self` for chaining. + pub fn update(&mut self, data: impl AsRef<[u8]>) -> &mut Self { + self.state.update(data.as_ref()); + self + } + + /// Consume `self` to compute the hash output. + pub fn finalize(&self) -> Scalar { + Scalar::from_bytes_wide(self.state.finalize().as_array()) + } +} diff --git a/zebra-chain/src/primitives/redpallas/signature.rs b/zebra-chain/src/primitives/redpallas/signature.rs new file mode 100644 index 00000000000..f9d4472f9b8 --- /dev/null +++ b/zebra-chain/src/primitives/redpallas/signature.rs @@ -0,0 +1,45 @@ +// -*- mode: rust; -*- +// +// This file is part of redpallas. +// Copyright (c) 2019-2021 Zcash Foundation +// See LICENSE for licensing information. +// +// Authors: +// - Henry de Valence +// - Deirdre Connolly + +use std::marker::PhantomData; + +use super::SigType; + +/// A RedJubJub signature. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Signature { + pub(crate) r_bytes: [u8; 32], + pub(crate) s_bytes: [u8; 32], + pub(crate) _marker: PhantomData, +} + +impl From<[u8; 64]> for Signature { + fn from(bytes: [u8; 64]) -> Signature { + let mut r_bytes = [0; 32]; + r_bytes.copy_from_slice(&bytes[0..32]); + let mut s_bytes = [0; 32]; + s_bytes.copy_from_slice(&bytes[32..64]); + Signature { + r_bytes, + s_bytes, + _marker: PhantomData, + } + } +} + +impl From> for [u8; 64] { + fn from(sig: Signature) -> [u8; 64] { + let mut bytes = [0; 64]; + bytes[0..32].copy_from_slice(&sig.r_bytes[..]); + bytes[32..64].copy_from_slice(&sig.s_bytes[..]); + bytes + } +} diff --git a/zebra-chain/src/primitives/redpallas/signing_key.rs b/zebra-chain/src/primitives/redpallas/signing_key.rs index d83a47cc379..702c502582a 100644 --- a/zebra-chain/src/primitives/redpallas/signing_key.rs +++ b/zebra-chain/src/primitives/redpallas/signing_key.rs @@ -1,9 +1,11 @@ -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; +use std::marker::PhantomData; -use halo2::arithmetic::FieldExt; -use halo2::pasta::pallas; +use group::GroupEncoding; +use halo2::{arithmetic::FieldExt, pasta::pallas}; +use rand_core::{CryptoRng, RngCore}; -use super::{Error, SigType, VerificationKey}; +use super::{Error, SigType, Signature, SpendAuth, VerificationKey}; /// A RedPallas signing key. #[derive(Copy, Clone, Debug)] @@ -22,6 +24,12 @@ impl<'a, T: SigType> From<&'a SigningKey> for VerificationKey { } } +impl From> for [u8; 32] { + fn from(sk: SigningKey) -> [u8; 32] { + sk.sk.to_bytes() + } +} + impl TryFrom<[u8; 32]> for SigningKey { type Error = Error; @@ -30,10 +38,88 @@ impl TryFrom<[u8; 32]> for SigningKey { if maybe_sk.is_some().into() { let sk = maybe_sk.unwrap(); - let pk = VerificationKey::from(&sk); + let pk = VerificationKey::from_scalar(&sk); Ok(SigningKey { sk, pk }) } else { Err(Error::MalformedSigningKey) } } } + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +struct SerdeHelper([u8; 32]); + +impl TryFrom for SigningKey { + type Error = Error; + + fn try_from(helper: SerdeHelper) -> Result { + helper.0.try_into() + } +} + +impl From> for SerdeHelper { + fn from(sk: SigningKey) -> Self { + Self(sk.into()) + } +} + +impl SigningKey { + /// Randomize this public key with the given `randomizer`. + pub fn randomize(&self, randomizer: &pallas::Scalar) -> SigningKey { + let sk = &self.sk + randomizer; + let pk = VerificationKey::from_scalar(&sk); + SigningKey { sk, pk } + } +} + +impl SigningKey { + /// Generate a new signing key. + pub fn new(mut rng: R) -> SigningKey { + let sk = { + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes); + pallas::Scalar::from_bytes_wide(&bytes) + }; + let pk = VerificationKey::from_scalar(&sk); + SigningKey { sk, pk } + } + + /// Create a signature of type `T` on `msg` using this `SigningKey`. + /// + /// https://zips.z.cash/protocol/nu5.pdf#concretereddsa + // Similar to signature::Signer but without boxed errors. + pub fn sign(&self, mut rng: R, msg: &[u8]) -> Signature { + use super::HStar; + + // RedDSA.GenRandom:() → R RedDSA.Random + // Choose a byte sequence uniformly at random of length + // (\ell_H + 128)/8 bytes. For RedPallas this is (512 + 128)/8 = 80. + let random_bytes = { + let mut bytes = [0; 80]; + rng.fill_bytes(&mut bytes); + bytes + }; + + let nonce = HStar::default() + .update(&random_bytes[..]) + .update(&self.pk.bytes.bytes[..]) // XXX ugly + .update(msg) + .finalize(); + + let r_bytes = pallas::Affine::from(&T::basepoint() * &nonce).to_bytes(); + + let c = HStar::default() + .update(&r_bytes[..]) + .update(&self.pk.bytes.bytes[..]) // XXX ugly + .update(msg) + .finalize(); + + let s_bytes = (&nonce + &(&c * &self.sk)).to_bytes(); + + Signature { + r_bytes, + s_bytes, + _marker: PhantomData, + } + } +} diff --git a/zebra-chain/src/primitives/redpallas/tests.rs b/zebra-chain/src/primitives/redpallas/tests.rs new file mode 100644 index 00000000000..e234c51584d --- /dev/null +++ b/zebra-chain/src/primitives/redpallas/tests.rs @@ -0,0 +1,2 @@ +mod basepoints; +mod prop; diff --git a/zebra-chain/src/primitives/redpallas/tests/basepoints.rs b/zebra-chain/src/primitives/redpallas/tests/basepoints.rs new file mode 100644 index 00000000000..a93652c9990 --- /dev/null +++ b/zebra-chain/src/primitives/redpallas/tests/basepoints.rs @@ -0,0 +1,20 @@ +use group::GroupEncoding; +use halo2::pasta::{arithmetic::CurveExt, pallas}; + +use super::super::constants; + +#[test] +fn orchard_spendauth_basepoint() { + assert_eq!( + pallas::Point::hash_to_curve("z.cash:Orchard")(b"G").to_bytes(), + constants::SPENDAUTHSIG_BASEPOINT_BYTES + ); +} + +#[test] +fn orchard_binding_basepoint() { + assert_eq!( + pallas::Point::hash_to_curve("z.cash:Orchard-cv")(b"r").to_bytes(), + constants::BINDINGSIG_BASEPOINT_BYTES + ); +} diff --git a/zebra-chain/src/primitives/redpallas/tests/prop.rs b/zebra-chain/src/primitives/redpallas/tests/prop.rs new file mode 100644 index 00000000000..984bed3023a --- /dev/null +++ b/zebra-chain/src/primitives/redpallas/tests/prop.rs @@ -0,0 +1,155 @@ +use std::convert::TryFrom; + +use halo2::arithmetic::FieldExt; +use proptest::prelude::*; +use rand_chacha::ChaChaRng; +use rand_core::{CryptoRng, RngCore, SeedableRng}; + +use super::super::SigningKey; +use super::super::*; + +/// A signature test-case, containing signature data and expected validity. +#[derive(Clone, Debug)] +struct SignatureCase { + msg: Vec, + sig: Signature, + pk_bytes: VerificationKeyBytes, + is_valid: bool, +} + +/// A modification to a test-case. +#[derive(Copy, Clone, Debug)] +enum Tweak { + /// No-op, used to check that unchanged cases verify. + None, + /// Change the message the signature is defined for, invalidating the signature. + ChangeMessage, + /// Change the public key the signature is defined for, invalidating the signature. + ChangePubkey, + /* XXX implement this -- needs to regenerate a custom signature because the + nonce commitment is fed into the hash, so it has to have torsion at signing + time. + /// Change the case to have a torsion component in the signature's `r` value. + AddTorsion, + */ + /* XXX implement this -- needs custom handling of field arithmetic. + /// Change the signature's `s` scalar to be unreduced (mod L), invalidating the signature. + UnreducedScalar, + */ +} + +impl SignatureCase { + fn new(mut rng: R, msg: Vec) -> Self { + let sk = SigningKey::new(&mut rng); + let sig = sk.sign(&mut rng, &msg); + let pk_bytes = VerificationKey::from(&sk).into(); + Self { + msg, + sig, + pk_bytes, + is_valid: true, + } + } + + // Check that signature verification succeeds or fails, as expected. + fn check(&self) -> bool { + // The signature data is stored in (refined) byte types, but do a round trip + // conversion to raw bytes to exercise those code paths. + let sig = { + let bytes: [u8; 64] = self.sig.into(); + Signature::::from(bytes) + }; + let pk_bytes = { + let bytes: [u8; 32] = self.pk_bytes.into(); + VerificationKeyBytes::::from(bytes) + }; + + // Check that signature validation has the expected result. + self.is_valid + == VerificationKey::try_from(pk_bytes) + .and_then(|pk| pk.verify(&self.msg, &sig)) + .is_ok() + } + + fn apply_tweak(&mut self, tweak: &Tweak) { + match tweak { + Tweak::None => {} + Tweak::ChangeMessage => { + // Changing the message makes the signature invalid. + self.msg.push(90); + self.is_valid = false; + } + Tweak::ChangePubkey => { + // Changing the public key makes the signature invalid. + let mut bytes: [u8; 32] = self.pk_bytes.clone().into(); + let j = (bytes[2] & 31) as usize; + bytes[2] ^= 0x23; + bytes[2] |= 0x99; + bytes[j] ^= bytes[2]; + self.pk_bytes = bytes.into(); + self.is_valid = false; + } + } + } +} + +fn tweak_strategy() -> impl Strategy { + prop_oneof![ + 10 => Just(Tweak::None), + 1 => Just(Tweak::ChangeMessage), + 1 => Just(Tweak::ChangePubkey), + ] +} + +proptest! { + #[test] + fn tweak_signature( + tweaks in prop::collection::vec(tweak_strategy(), (0,5)), + rng_seed in any::(), + ) { + // Use a deterministic RNG so that test failures can be reproduced. + // Seeding with 64 bits of entropy is INSECURE and this code should + // not be copied outside of this test! + let mut rng = ChaChaRng::seed_from_u64(rng_seed); + + // Create a test case for each signature type. + let msg = b"test message for proptests"; + let mut binding = SignatureCase::::new(&mut rng, msg.to_vec()); + let mut spendauth = SignatureCase::::new(&mut rng, msg.to_vec()); + + // Apply tweaks to each case. + for t in &tweaks { + binding.apply_tweak(t); + spendauth.apply_tweak(t); + } + + assert!(binding.check()); + assert!(spendauth.check()); + } + + #[test] + fn randomization_commutes_with_pubkey_homomorphism(rng_seed in any::()) { + // Use a deterministic RNG so that test failures can be reproduced. + // Seeding with 64 bits of entropy is INSECURE and this code should + // not be copied outside of this test! + let mut rng = ChaChaRng::seed_from_u64(rng_seed); + + let r = { + // XXX-jubjub: better API for this + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes[..]); + Randomizer::from_bytes_wide(&bytes) + }; + + let sk = SigningKey::::new(&mut rng); + let pk = VerificationKey::from(&sk); + + let sk_r = sk.randomize(&r); + let pk_r = pk.randomize(&r); + + let pk_r_via_sk_rand: [u8; 32] = VerificationKeyBytes::from(VerificationKey::from(&sk_r)).into(); + let pk_r_via_pk_rand: [u8; 32] = VerificationKeyBytes::from(pk_r).into(); + + assert_eq!(pk_r_via_pk_rand, pk_r_via_sk_rand); + } +} diff --git a/zebra-chain/src/primitives/redpallas/verification_key.rs b/zebra-chain/src/primitives/redpallas/verification_key.rs index 12b1d515c1f..3fb3345bfd1 100644 --- a/zebra-chain/src/primitives/redpallas/verification_key.rs +++ b/zebra-chain/src/primitives/redpallas/verification_key.rs @@ -1,13 +1,9 @@ -use std::{ - convert::TryFrom, - // hash::{Hash, Hasher}, - marker::PhantomData, -}; +use std::{convert::TryFrom, marker::PhantomData}; use group::{cofactor::CofactorGroup, GroupEncoding}; -use halo2::pasta::pallas; +use halo2::{arithmetic::FieldExt, pasta::pallas}; -use super::{Error, SigType}; +use super::*; /// A refinement type for `[u8; 32]` indicating that the bytes represent /// an encoding of a RedPallas verification key. @@ -73,18 +69,6 @@ impl From> for [u8; 32] { } } -impl From<&pallas::Scalar> for VerificationKey { - fn from(s: &pallas::Scalar) -> VerificationKey { - let point = T::basepoint() * s; - let bytes = VerificationKeyBytes { - bytes: pallas::Affine::from(&point).to_bytes(), - _marker: PhantomData, - }; - - Self { point, bytes } - } -} - impl TryFrom> for VerificationKey { type Error = Error; @@ -115,3 +99,81 @@ impl TryFrom<[u8; 32]> for VerificationKey { VerificationKeyBytes::from(bytes).try_into() } } + +impl VerificationKey { + /// Randomize this verification key with the given `randomizer`. + /// + /// Randomization is only supported for `SpendAuth` keys. + pub fn randomize(&self, randomizer: &Randomizer) -> VerificationKey { + use super::private::Sealed; + let point = &self.point + &(&SpendAuth::basepoint() * randomizer); + let bytes = VerificationKeyBytes { + bytes: pallas::Point::from(point).to_bytes(), + _marker: PhantomData, + }; + VerificationKey { bytes, point } + } +} + +impl VerificationKey { + pub(crate) fn from_scalar(s: &pallas::Scalar) -> VerificationKey { + let point = &T::basepoint() * s; + let bytes = VerificationKeyBytes { + bytes: pallas::Point::from(point).to_bytes(), + _marker: PhantomData, + }; + VerificationKey { bytes, point } + } + + /// Verify a purported `signature` over `msg` made by this verification key. + // This is similar to impl signature::Verifier but without boxed errors + pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { + let c = HStar::default() + .update(&signature.r_bytes[..]) + .update(&self.bytes.bytes[..]) // XXX ugly + .update(msg) + .finalize(); + self.verify_prehashed(signature, c) + } + + /// Verify a purported `signature` with a prehashed challenge. + #[allow(non_snake_case)] + pub(crate) fn verify_prehashed( + &self, + signature: &Signature, + c: pallas::Scalar, + ) -> Result<(), Error> { + let r = { + // XXX-pasta_curves: should not use CtOption here + let maybe_point = pallas::Affine::from_bytes(&signature.r_bytes); + if maybe_point.is_some().into() { + pallas::Point::from(maybe_point.unwrap()) + } else { + return Err(Error::InvalidSignature); + } + }; + + let s = { + // XXX-pasta_curves: should not use CtOption here + let maybe_scalar = pallas::Scalar::from_bytes(&signature.s_bytes); + if maybe_scalar.is_some().into() { + maybe_scalar.unwrap() + } else { + return Err(Error::InvalidSignature); + } + }; + + // XXX rewrite as normal double scalar mul + // Verify check is h * ( - s * B + R + c * A) == 0 + // h * ( s * B - c * A - R) == 0 + let sB = &T::basepoint() * &s; + let cA = &self.point * &c; + let check = sB - cA - r; + + if check.is_small_order().into() { + Ok(()) + } else { + Err(Error::InvalidSignature) + } + } +} From 7deb9820f0e9a871c6a31037e29ef1d2398595ab Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Mon, 3 May 2021 15:28:38 -0400 Subject: [PATCH 2/5] Clippy fix --- .../src/primitives/redpallas/signing_key.rs | 6 +++--- .../src/primitives/redpallas/tests/prop.rs | 2 +- .../src/primitives/redpallas/verification_key.rs | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/zebra-chain/src/primitives/redpallas/signing_key.rs b/zebra-chain/src/primitives/redpallas/signing_key.rs index 702c502582a..c068ff34f50 100644 --- a/zebra-chain/src/primitives/redpallas/signing_key.rs +++ b/zebra-chain/src/primitives/redpallas/signing_key.rs @@ -66,7 +66,7 @@ impl From> for SerdeHelper { impl SigningKey { /// Randomize this public key with the given `randomizer`. pub fn randomize(&self, randomizer: &pallas::Scalar) -> SigningKey { - let sk = &self.sk + randomizer; + let sk = self.sk + randomizer; let pk = VerificationKey::from_scalar(&sk); SigningKey { sk, pk } } @@ -106,7 +106,7 @@ impl SigningKey { .update(msg) .finalize(); - let r_bytes = pallas::Affine::from(&T::basepoint() * &nonce).to_bytes(); + let r_bytes = pallas::Affine::from(T::basepoint() * nonce).to_bytes(); let c = HStar::default() .update(&r_bytes[..]) @@ -114,7 +114,7 @@ impl SigningKey { .update(msg) .finalize(); - let s_bytes = (&nonce + &(&c * &self.sk)).to_bytes(); + let s_bytes = (nonce + (c * self.sk)).to_bytes(); Signature { r_bytes, diff --git a/zebra-chain/src/primitives/redpallas/tests/prop.rs b/zebra-chain/src/primitives/redpallas/tests/prop.rs index 984bed3023a..ad14ef7726a 100644 --- a/zebra-chain/src/primitives/redpallas/tests/prop.rs +++ b/zebra-chain/src/primitives/redpallas/tests/prop.rs @@ -81,7 +81,7 @@ impl SignatureCase { } Tweak::ChangePubkey => { // Changing the public key makes the signature invalid. - let mut bytes: [u8; 32] = self.pk_bytes.clone().into(); + let mut bytes: [u8; 32] = self.pk_bytes.into(); let j = (bytes[2] & 31) as usize; bytes[2] ^= 0x23; bytes[2] |= 0x99; diff --git a/zebra-chain/src/primitives/redpallas/verification_key.rs b/zebra-chain/src/primitives/redpallas/verification_key.rs index 3fb3345bfd1..d1ce26341cc 100644 --- a/zebra-chain/src/primitives/redpallas/verification_key.rs +++ b/zebra-chain/src/primitives/redpallas/verification_key.rs @@ -106,23 +106,23 @@ impl VerificationKey { /// Randomization is only supported for `SpendAuth` keys. pub fn randomize(&self, randomizer: &Randomizer) -> VerificationKey { use super::private::Sealed; - let point = &self.point + &(&SpendAuth::basepoint() * randomizer); + let point = self.point + (SpendAuth::basepoint() * randomizer); let bytes = VerificationKeyBytes { - bytes: pallas::Point::from(point).to_bytes(), + bytes: point.to_bytes(), _marker: PhantomData, }; - VerificationKey { bytes, point } + VerificationKey { point, bytes } } } impl VerificationKey { pub(crate) fn from_scalar(s: &pallas::Scalar) -> VerificationKey { - let point = &T::basepoint() * s; + let point = T::basepoint() * s; let bytes = VerificationKeyBytes { - bytes: pallas::Point::from(point).to_bytes(), + bytes: point.to_bytes(), _marker: PhantomData, }; - VerificationKey { bytes, point } + VerificationKey { point, bytes } } /// Verify a purported `signature` over `msg` made by this verification key. @@ -166,8 +166,8 @@ impl VerificationKey { // XXX rewrite as normal double scalar mul // Verify check is h * ( - s * B + R + c * A) == 0 // h * ( s * B - c * A - R) == 0 - let sB = &T::basepoint() * &s; - let cA = &self.point * &c; + let sB = T::basepoint() * s; + let cA = self.point * c; let check = sB - cA - r; if check.is_small_order().into() { From f718c8116b4bba209c617f4a5b6ffcefc24a480d Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 5 May 2021 08:11:04 -0400 Subject: [PATCH 3/5] s/RedJubjub/RedPallas/ Co-authored-by: Conrado Gouvea --- zebra-chain/src/primitives/redpallas/signature.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra-chain/src/primitives/redpallas/signature.rs b/zebra-chain/src/primitives/redpallas/signature.rs index f9d4472f9b8..303e921dd75 100644 --- a/zebra-chain/src/primitives/redpallas/signature.rs +++ b/zebra-chain/src/primitives/redpallas/signature.rs @@ -12,7 +12,7 @@ use std::marker::PhantomData; use super::SigType; -/// A RedJubJub signature. +/// A RedPallas signature. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Signature { From 7b3ad86a5650c066d363b2e7e9230a1a29cbc69e Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 5 May 2021 12:30:24 -0400 Subject: [PATCH 4/5] Seed redpallas proptest rng securely --- zebra-chain/src/primitives/redpallas/tests/prop.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/zebra-chain/src/primitives/redpallas/tests/prop.rs b/zebra-chain/src/primitives/redpallas/tests/prop.rs index ad14ef7726a..3cb7b9788e7 100644 --- a/zebra-chain/src/primitives/redpallas/tests/prop.rs +++ b/zebra-chain/src/primitives/redpallas/tests/prop.rs @@ -105,12 +105,10 @@ proptest! { #[test] fn tweak_signature( tweaks in prop::collection::vec(tweak_strategy(), (0,5)), - rng_seed in any::(), + rng_seed in prop::array::uniform32(any::()), ) { // Use a deterministic RNG so that test failures can be reproduced. - // Seeding with 64 bits of entropy is INSECURE and this code should - // not be copied outside of this test! - let mut rng = ChaChaRng::seed_from_u64(rng_seed); + let mut rng = ChaChaRng::from_seed(rng_seed); // Create a test case for each signature type. let msg = b"test message for proptests"; @@ -128,11 +126,9 @@ proptest! { } #[test] - fn randomization_commutes_with_pubkey_homomorphism(rng_seed in any::()) { + fn randomization_commutes_with_pubkey_homomorphism(rng_seed in prop::array::uniform32(any::())) { // Use a deterministic RNG so that test failures can be reproduced. - // Seeding with 64 bits of entropy is INSECURE and this code should - // not be copied outside of this test! - let mut rng = ChaChaRng::seed_from_u64(rng_seed); + let mut rng = ChaChaRng::from_seed(rng_seed); let r = { // XXX-jubjub: better API for this From 9daf14d6e49417c4f6aa762b91d2554b9f68718b Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Thu, 6 May 2021 09:54:05 -0400 Subject: [PATCH 5/5] Some comments --- zebra-chain/src/primitives/redpallas/constants.rs | 2 ++ zebra-chain/src/primitives/redpallas/tests/basepoints.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/zebra-chain/src/primitives/redpallas/constants.rs b/zebra-chain/src/primitives/redpallas/constants.rs index a10863b711b..e3eefc0727f 100644 --- a/zebra-chain/src/primitives/redpallas/constants.rs +++ b/zebra-chain/src/primitives/redpallas/constants.rs @@ -10,12 +10,14 @@ /// The byte-encoding of the basepoint for `SpendAuthSig` on the [Pallas curve][pallasandvesta]. /// /// [pallasandvesta]: https://zips.z.cash/protocol/nu5.pdf#pallasandvesta +// Reproducible by pallas::Point::hash_to_curve("z.cash:Orchard")(b"G").to_bytes() pub const SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [ 99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95, 62, 124, 24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183, ]; /// The byte-encoding of the basepoint for `BindingSig` on the Pallas curve. +// Reproducible by pallas::Point::hash_to_curve("z.cash:Orchard-cv")(b"r").to_bytes() pub const BINDINGSIG_BASEPOINT_BYTES: [u8; 32] = [ 145, 90, 60, 136, 104, 198, 195, 14, 47, 128, 144, 238, 69, 215, 110, 64, 72, 32, 141, 234, 91, 35, 102, 79, 187, 9, 164, 15, 85, 68, 244, 7, diff --git a/zebra-chain/src/primitives/redpallas/tests/basepoints.rs b/zebra-chain/src/primitives/redpallas/tests/basepoints.rs index a93652c9990..298990e906d 100644 --- a/zebra-chain/src/primitives/redpallas/tests/basepoints.rs +++ b/zebra-chain/src/primitives/redpallas/tests/basepoints.rs @@ -6,6 +6,7 @@ use super::super::constants; #[test] fn orchard_spendauth_basepoint() { assert_eq!( + // An instance of _GroupHash^P_ pallas::Point::hash_to_curve("z.cash:Orchard")(b"G").to_bytes(), constants::SPENDAUTHSIG_BASEPOINT_BYTES ); @@ -14,6 +15,7 @@ fn orchard_spendauth_basepoint() { #[test] fn orchard_binding_basepoint() { assert_eq!( + // An instance of _GroupHash^P_ pallas::Point::hash_to_curve("z.cash:Orchard-cv")(b"r").to_bytes(), constants::BINDINGSIG_BASEPOINT_BYTES );