diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index 1e7b91a33..0d6be86c4 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -100,6 +100,10 @@ and this library adheres to Rust's notion of - `zcash_primitives::sapling`: - `BatchValidator::validate` now takes the `SpendVerifyingKey` and `OutputVerifyingKey` newtypes. + - `SaplingVerificationContext::new` now always creates a context with ZIP 216 + rules enforced, and no longer has a boolean for configuring this. + - `SaplingVerificationContext::{check_spend, final_check}` now use the + `redjubjub` crate types for `rk`, `spend_auth_sig`, and `binding_sig`. - `SaplingVerificationContext::{check_spend, check_output}` now take the `PreparedSpendVerifyingKey` and `PreparedOutputVerifyingKey` newtypes. @@ -127,6 +131,15 @@ and this library adheres to Rust's notion of - `Error::MissingSignatures` - `bundle::Bundle` now has a second generic parameter `V`. - `bundle::Bundle::value_balance` now returns `&V` instead of `&Amount`. + - `bundle::Authorized::binding_sig` now has type `redjubjub::Signature`. + - `bundle::Authorized::AuthSig` now has type `redjubjub::Signature`. + - `bundle::SpendDescription::temporary_zcashd_from_parts` now takes `rk` as + `redjubjub::VerificationKey` instead of + `zcash_primitives::sapling::redjubjub::PublicKey`. + - `bundle::SpendDescription::rk` now returns `&redjubjub::VerificationKey`. + - `bundle::SpendDescriptionV5::into_spend_description` now takes + `spend_auth_sig` as `redjubjub::Signature` instead of + `zcash_primitives::sapling::redjubjub::Signature`. - `bundle::testing::arb_bundle` now takes a `value_balance: V` argument. - `bundle::MapAuth` trait methods now take `&mut self` instead of `&self`. - `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue` @@ -150,6 +163,11 @@ and this library adheres to Rust's notion of - `try_sapling_output_recovery` - `util::generate_random_rseed` now takes a `Zip212Enforcement` argument instead of a `P: consensus::Parameters` argument and a height. + - `value::TrapdoorSum::into_bsk` now returns `redjubjub::SigningKey` + instead of `zcash_primitives::sapling::redjubjub::PrivateKey`. + - `value::CommitmentSum::into_bvk` now returns + `redjubjub::VerificationKey` instead of + `zcash_primitives::sapling::redjubjub::PublicKey`. - `zcash_primitives::transaction`: - `builder::Builder::{build, build_zfuture}` now take `&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`. @@ -192,6 +210,8 @@ and this library adheres to Rust's notion of - `OutputDescriptionV5::read` - `note_encryption::SaplingDomain::for_height` (use `SaplingDomain::new` instead). + - `redjubjub` module (use the `redjubjub` crate instead). + - `spend_sig` (use `redjubjub::SigningKey::{randomize, sign}` instead). - `zcash_primitives::transaction::components::sapling`: - The following types were removed from this module (moved into `zcash_primitives::sapling::bundle`): diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 74e8ff5be..67fd3fc8f 100644 --- a/zcash_primitives/src/sapling.rs +++ b/zcash_primitives/src/sapling.rs @@ -11,7 +11,6 @@ pub mod note; pub mod note_encryption; pub mod pedersen_hash; pub mod prover; -pub mod redjubjub; mod spec; mod tree; pub mod util; @@ -19,13 +18,6 @@ pub mod value; mod verifier; pub mod zip32; -use group::GroupEncoding; -use rand_core::{CryptoRng, RngCore}; - -use constants::SPENDING_KEY_GENERATOR; - -use self::redjubjub::{PrivateKey, PublicKey, Signature}; - pub use address::PaymentAddress; pub use bundle::Bundle; pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey}; @@ -35,60 +27,6 @@ pub use tree::{ }; pub use verifier::{BatchValidator, SaplingVerificationContext}; -/// Create the spendAuthSig for a Sapling SpendDescription. -pub fn spend_sig( - ask: PrivateKey, - ar: jubjub::Fr, - sighash: &[u8; 32], - rng: &mut R, -) -> Signature { - spend_sig_internal(&ask, ar, sighash, rng) -} - -pub(crate) fn spend_sig_internal( - ask: &PrivateKey, - ar: jubjub::Fr, - sighash: &[u8; 32], - rng: &mut R, -) -> Signature { - // We compute `rsk`... - let rsk = ask.randomize(ar); - - // We compute `rk` from there (needed for key prefixing) - let rk = PublicKey::from_private(&rsk, SPENDING_KEY_GENERATOR); - - // Compute the signature's message for rk/spend_auth_sig - let mut data_to_be_signed = [0u8; 64]; - data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes()); - data_to_be_signed[32..64].copy_from_slice(&sighash[..]); - - // Do the signing - rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR) -} - -/// Verifies a spendAuthSig. -/// -/// This only exists because the RedJubjub implementation inside `zcash_primitives` does -/// not implement key prefixing (which was added in response to a Sapling audit). This -/// will be fixed by migrating to the redjubjub crate. -pub(crate) fn verify_spend_sig( - ak: &PublicKey, - alpha: jubjub::Fr, - sighash: &[u8; 32], - sig: &Signature, -) -> bool { - // We compute `rk` (needed for key prefixing) - let rk = ak.randomize(alpha, SPENDING_KEY_GENERATOR); - - // Compute the signature's message for rk/spend_auth_sig - let mut data_to_be_signed = [0u8; 64]; - data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes()); - data_to_be_signed[32..64].copy_from_slice(&sighash[..]); - - // Do the verifying - rk.verify(&data_to_be_signed, sig, SPENDING_KEY_GENERATOR) -} - #[cfg(any(test, feature = "test-dependencies"))] pub mod testing { pub use super::{ diff --git a/zcash_primitives/src/sapling/builder.rs b/zcash_primitives/src/sapling/builder.rs index 656894057..02bebbe45 100644 --- a/zcash_primitives/src/sapling/builder.rs +++ b/zcash_primitives/src/sapling/builder.rs @@ -3,9 +3,10 @@ use core::fmt; use std::{marker::PhantomData, sync::mpsc::Sender}; -use ff::Field; +use group::{ff::Field, GroupEncoding}; use rand::{seq::SliceRandom, RngCore}; use rand_core::CryptoRng; +use redjubjub::{Binding, SpendAuth}; use crate::{ sapling::{ @@ -14,17 +15,13 @@ use crate::{ Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription, SpendDescription, }, - constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, keys::OutgoingViewingKey, note_encryption::{sapling_note_encryption, Zip212Enforcement}, prover::{OutputProver, SpendProver}, - redjubjub::{PrivateKey, PublicKey, Signature}, - spend_sig_internal, util::generate_random_rseed_internal, value::{ CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum, }, - verify_spend_sig, zip32::ExtendedSpendingKey, Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk, }, @@ -117,10 +114,11 @@ impl SpendDescriptionInfo { // Construct the value commitment. let cv = ValueCommitment::derive(self.note.value(), self.rcv.clone()); - let ak = PublicKey(self.proof_generation_key.ak.into()); + let ak = redjubjub::VerificationKey::try_from(self.proof_generation_key.ak.to_bytes()) + .expect("valid points are valid verification keys"); // This is the result of the re-randomization, we compute it for the caller - let rk = ak.randomize(self.alpha, SPENDING_KEY_GENERATOR); + let rk = ak.randomize(&self.alpha); let nullifier = self.note.nf( &self.proof_generation_key.to_viewing_key().nk, @@ -507,10 +505,7 @@ impl SaplingBuilder { (spends - outputs) .into_bvk(i64::try_from(self.value_balance).map_err(|_| Error::InvalidAmount)?) }; - assert_eq!( - PublicKey::from_private(&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR).0, - bvk.0, - ); + assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk); let bundle = if shielded_spends.is_empty() && shielded_outputs.is_empty() { None @@ -676,16 +671,9 @@ impl Bundle, V> { } /// Marker for an unauthorized bundle with no signatures. +#[derive(Clone)] pub struct Unsigned { - bsk: PrivateKey, -} - -impl Clone for Unsigned { - fn clone(&self) -> Self { - Self { - bsk: PrivateKey(self.bsk.0), - } - } + bsk: redjubjub::SigningKey, } impl fmt::Debug for Unsigned { @@ -703,7 +691,7 @@ impl InProgressSignatures for Unsigned { pub struct SigningParts { /// The spend validating key for this spend description. Used to match spend /// authorizing keys to spend descriptions they can create signatures for. - ak: PublicKey, + ak: redjubjub::VerificationKey, /// The randomization needed to derive the actual signing key for this note. alpha: jubjub::Scalar, } @@ -711,7 +699,7 @@ pub struct SigningParts { /// Marker for a partially-authorized bundle, in the process of being signed. #[derive(Clone, Debug)] pub struct PartiallyAuthorized { - binding_signature: Signature, + binding_signature: redjubjub::Signature, sighash: [u8; 32], } @@ -720,16 +708,18 @@ impl InProgressSignatures for PartiallyAuthorized { } /// A heisen[`Signature`] for a particular [`SpendDescription`]. +/// +/// [`Signature`]: redjubjub::Signature #[derive(Clone, Debug)] pub enum MaybeSigned { /// The information needed to sign this [`SpendDescription`]. SigningMetadata(SigningParts), /// The signature for this [`SpendDescription`]. - Signature(Signature), + Signature(redjubjub::Signature), } impl MaybeSigned { - fn finalize(self) -> Result { + fn finalize(self) -> Result, Error> { match self { Self::Signature(sig) => Ok(sig), _ => Err(Error::MissingSignatures), @@ -752,11 +742,7 @@ impl Bundle, V> { MaybeSigned::SigningMetadata, |auth: InProgress| InProgress { sigs: PartiallyAuthorized { - binding_signature: auth.sigs.bsk.sign( - &sighash, - &mut rng, - VALUE_COMMITMENT_RANDOMNESS_GENERATOR, - ), + binding_signature: auth.sigs.bsk.sign(&mut rng, &sighash), sighash, }, _proof_state: PhantomData::default(), @@ -774,7 +760,7 @@ impl Bundle, V> { self, mut rng: R, sighash: [u8; 32], - signing_keys: &[PrivateKey], + signing_keys: &[redjubjub::SigningKey], ) -> Result, Error> { signing_keys .iter() @@ -786,18 +772,23 @@ impl Bundle, V> { } impl Bundle, V> { - /// Signs this bundle with the given [`PrivateKey`]. + /// Signs this bundle with the given [`redjubjub::SigningKey`]. /// /// This will apply signatures for all notes controlled by this spending key. - pub fn sign(self, mut rng: R, ask: &PrivateKey) -> Self { - let expected_ak = PublicKey::from_private(ask, SPENDING_KEY_GENERATOR); + pub fn sign( + self, + mut rng: R, + ask: &redjubjub::SigningKey, + ) -> Self { + let expected_ak = redjubjub::VerificationKey::from(ask); let sighash = self.authorization().sigs.sighash; self.map_authorization(( |proof| proof, |proof| proof, |maybe| match maybe { - MaybeSigned::SigningMetadata(parts) if parts.ak.0 == expected_ak.0 => { - MaybeSigned::Signature(spend_sig_internal(ask, parts.alpha, &sighash, &mut rng)) + MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => { + let rsk = ask.randomize(&parts.alpha); + MaybeSigned::Signature(rsk.sign(&mut rng, &sighash)) } s => s, }, @@ -805,16 +796,19 @@ impl Bundle, V> { )) } - /// Appends externally computed [`Signature`]s. + /// Appends externally computed [`redjubjub::Signature`]s. /// /// Each signature will be applied to the one input for which it is valid. An error /// will be returned if the signature is not valid for any inputs, or if it is valid /// for more than one input. - pub fn append_signatures(self, signatures: &[Signature]) -> Result { + pub fn append_signatures( + self, + signatures: &[redjubjub::Signature], + ) -> Result { signatures.iter().try_fold(self, Self::append_signature) } - fn append_signature(self, signature: &Signature) -> Result { + fn append_signature(self, signature: &redjubjub::Signature) -> Result { let sighash = self.authorization().sigs.sighash; let mut signature_valid_for = 0usize; let bundle = self.map_authorization(( @@ -822,7 +816,8 @@ impl Bundle, V> { |proof| proof, |maybe| match maybe { MaybeSigned::SigningMetadata(parts) => { - if verify_spend_sig(&parts.ak, parts.alpha, &sighash, signature) { + let rk = parts.ak.randomize(&parts.alpha); + if rk.verify(&sighash, signature).is_ok() { signature_valid_for += 1; MaybeSigned::Signature(*signature) } else { @@ -872,7 +867,6 @@ pub mod testing { bundle::{Authorized, Bundle}, note_encryption::Zip212Enforcement, prover::mock::{MockOutputProver, MockSpendProver}, - redjubjub::PrivateKey, testing::{arb_node, arb_note}, value::testing::arb_positive_note_value, zip32::testing::arb_extended_spending_key, @@ -929,7 +923,8 @@ pub mod testing { .apply_signatures( &mut rng, fake_sighash_bytes, - &[PrivateKey(extsk.expsk.ask)], + &[redjubjub::SigningKey::try_from(extsk.expsk.ask.to_bytes()) + .expect("valid scalars are valid signing keys")], ) .unwrap() }, diff --git a/zcash_primitives/src/sapling/bundle.rs b/zcash_primitives/src/sapling/bundle.rs index 615808704..a95bd9646 100644 --- a/zcash_primitives/src/sapling/bundle.rs +++ b/zcash_primitives/src/sapling/bundle.rs @@ -1,6 +1,7 @@ use core::fmt::Debug; use memuse::DynamicUsage; +use redjubjub::{Binding, SpendAuth}; use zcash_note_encryption::{ EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE, @@ -10,7 +11,6 @@ use crate::sapling::{ circuit::GROTH_PROOF_SIZE, note::ExtractedNoteCommitment, note_encryption::{CompactOutputDescription, SaplingDomain}, - redjubjub::{self, PublicKey, Signature}, value::ValueCommitment, Nullifier, }; @@ -29,13 +29,13 @@ pub trait Authorization: Debug { #[derive(Debug, Copy, Clone)] pub struct Authorized { // TODO: Make this private. - pub binding_sig: redjubjub::Signature, + pub binding_sig: redjubjub::Signature, } impl Authorization for Authorized { type SpendProof = GrothProofBytes; type OutputProof = GrothProofBytes; - type AuthSig = redjubjub::Signature; + type AuthSig = redjubjub::Signature; } /// A map from one bundle authorization to another. @@ -315,7 +315,7 @@ pub struct SpendDescription { cv: ValueCommitment, anchor: bls12_381::Scalar, nullifier: Nullifier, - rk: PublicKey, + rk: redjubjub::VerificationKey, zkproof: A::SpendProof, spend_auth_sig: A::AuthSig, } @@ -336,7 +336,7 @@ impl SpendDescription { cv: ValueCommitment, anchor: bls12_381::Scalar, nullifier: Nullifier, - rk: PublicKey, + rk: redjubjub::VerificationKey, zkproof: A::SpendProof, spend_auth_sig: A::AuthSig, ) -> Self { @@ -347,7 +347,7 @@ impl SpendDescription { cv: ValueCommitment, anchor: bls12_381::Scalar, nullifier: Nullifier, - rk: PublicKey, + rk: redjubjub::VerificationKey, zkproof: A::SpendProof, spend_auth_sig: A::AuthSig, ) -> Self { @@ -377,7 +377,7 @@ impl SpendDescription { } /// Returns the randomized verification key for the note being spent. - pub fn rk(&self) -> &PublicKey { + pub fn rk(&self) -> &redjubjub::VerificationKey { &self.rk } @@ -406,11 +406,15 @@ impl DynamicUsage for SpendDescription { pub struct SpendDescriptionV5 { cv: ValueCommitment, nullifier: Nullifier, - rk: PublicKey, + rk: redjubjub::VerificationKey, } impl SpendDescriptionV5 { - pub(crate) fn from_parts(cv: ValueCommitment, nullifier: Nullifier, rk: PublicKey) -> Self { + pub(crate) fn from_parts( + cv: ValueCommitment, + nullifier: Nullifier, + rk: redjubjub::VerificationKey, + ) -> Self { Self { cv, nullifier, rk } } @@ -418,7 +422,7 @@ impl SpendDescriptionV5 { self, anchor: bls12_381::Scalar, zkproof: GrothProofBytes, - spend_auth_sig: Signature, + spend_auth_sig: redjubjub::Signature, ) -> SpendDescription { SpendDescription { cv: self.cv, @@ -627,9 +631,7 @@ pub mod testing { use crate::{ sapling::{ - constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, note::ExtractedNoteCommitment, - redjubjub::{PrivateKey, PublicKey}, value::{ testing::{arb_note_value_bounded, arb_trapdoor}, ValueCommitment, MAX_NOTE_VALUE, @@ -669,8 +671,8 @@ pub mod testing { fake_sighash_bytes in prop::array::uniform32(prop::num::u8::ANY), ) -> SpendDescription { let mut rng = StdRng::from_seed(rng_seed); - let sk1 = PrivateKey(jubjub::Fr::random(&mut rng)); - let rk = PublicKey::from_private(&sk1, SPENDING_KEY_GENERATOR); + let sk1 = redjubjub::SigningKey::new(&mut rng); + let rk = redjubjub::VerificationKey::from(&sk1); let cv = ValueCommitment::derive(value, rcv); SpendDescription { cv, @@ -678,7 +680,7 @@ pub mod testing { nullifier, rk, zkproof, - spend_auth_sig: sk1.sign(&fake_sighash_bytes, &mut rng, SPENDING_KEY_GENERATOR), + spend_auth_sig: sk1.sign(&mut rng, &fake_sighash_bytes), } } } @@ -731,18 +733,14 @@ pub mod testing { None } else { let mut rng = StdRng::from_seed(rng_seed); - let bsk = PrivateKey(jubjub::Fr::random(&mut rng)); + let bsk = redjubjub::SigningKey::new(&mut rng); Some(Bundle { shielded_spends, shielded_outputs, value_balance, authorization: Authorized { - binding_sig: bsk.sign( - &fake_bvk_bytes, - &mut rng, - VALUE_COMMITMENT_RANDOMNESS_GENERATOR, - ), + binding_sig: bsk.sign(&mut rng, &fake_bvk_bytes), }, }) } diff --git a/zcash_primitives/src/sapling/redjubjub.rs b/zcash_primitives/src/sapling/redjubjub.rs deleted file mode 100644 index 9df1d0729..000000000 --- a/zcash_primitives/src/sapling/redjubjub.rs +++ /dev/null @@ -1,386 +0,0 @@ -//! Implementation of [RedJubjub], a specialization of RedDSA to the Jubjub -//! curve. -//! -//! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa - -use ff::{Field, PrimeField}; -use group::GroupEncoding; -use jubjub::{AffinePoint, ExtendedPoint, SubgroupPoint}; -use rand_core::RngCore; - -use std::fmt; -use std::io::{self, Read, Write}; -use std::ops::{AddAssign, MulAssign, Neg}; - -use super::util::hash_to_scalar; - -fn read_scalar(mut reader: R) -> io::Result { - let mut s_repr = [0u8; 32]; - reader.read_exact(s_repr.as_mut())?; - - Option::from(jubjub::Fr::from_repr(s_repr)) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field")) -} - -fn write_scalar(s: &jubjub::Fr, mut writer: W) -> io::Result<()> { - writer.write_all(s.to_repr().as_ref()) -} - -fn h_star(a: &[u8], b: &[u8]) -> jubjub::Fr { - hash_to_scalar(b"Zcash_RedJubjubH", a, b) -} - -#[derive(Copy, Clone)] -pub struct Signature { - rbar: [u8; 32], - sbar: [u8; 32], -} - -impl fmt::Debug for Signature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Signature") - .field("rbar", &hex::encode(self.rbar)) - .field("sbar", &hex::encode(self.sbar)) - .finish() - } -} - -pub struct PrivateKey(pub jubjub::Fr); - -#[derive(Debug, Clone)] -pub struct PublicKey(pub ExtendedPoint); - -impl Signature { - pub fn read(mut reader: R) -> io::Result { - let mut rbar = [0u8; 32]; - let mut sbar = [0u8; 32]; - reader.read_exact(&mut rbar)?; - reader.read_exact(&mut sbar)?; - Ok(Signature { rbar, sbar }) - } - - pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(&self.rbar)?; - writer.write_all(&self.sbar) - } -} - -impl PrivateKey { - #[must_use] - pub fn randomize(&self, alpha: jubjub::Fr) -> Self { - let mut tmp = self.0; - tmp.add_assign(&alpha); - PrivateKey(tmp) - } - - pub fn read(reader: R) -> io::Result { - let pk = read_scalar::(reader)?; - Ok(PrivateKey(pk)) - } - - pub fn write(&self, writer: W) -> io::Result<()> { - write_scalar::(&self.0, writer) - } - - pub fn sign(&self, msg: &[u8], rng: &mut R, p_g: SubgroupPoint) -> Signature { - // T = (l_H + 128) bits of randomness - // For H*, l_H = 512 bits - let mut t = [0u8; 80]; - rng.fill_bytes(&mut t[..]); - - // r = H*(T || M) - let r = h_star(&t[..], msg); - - // R = r . P_G - let r_g = p_g * r; - let rbar = r_g.to_bytes(); - - // S = r + H*(Rbar || M) . sk - let mut s = h_star(&rbar[..], msg); - s.mul_assign(&self.0); - s.add_assign(&r); - let mut sbar = [0u8; 32]; - write_scalar::<&mut [u8]>(&s, &mut sbar[..]) - .expect("Jubjub scalars should serialize to 32 bytes"); - - Signature { rbar, sbar } - } -} - -impl PublicKey { - pub fn from_private(privkey: &PrivateKey, p_g: SubgroupPoint) -> Self { - PublicKey((p_g * privkey.0).into()) - } - - #[must_use] - pub fn randomize(&self, alpha: jubjub::Fr, p_g: SubgroupPoint) -> Self { - PublicKey(ExtendedPoint::from(p_g * alpha) + self.0) - } - - pub fn read(mut reader: R) -> io::Result { - let mut bytes = [0u8; 32]; - reader.read_exact(&mut bytes)?; - let p = ExtendedPoint::from_bytes(&bytes).map(PublicKey); - if p.is_some().into() { - Ok(p.unwrap()) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "invalid RedJubjub public key", - )) - } - } - - pub fn write(&self, mut writer: W) -> io::Result<()> { - writer.write_all(&self.0.to_bytes()) - } - - pub fn verify(&self, msg: &[u8], sig: &Signature, p_g: SubgroupPoint) -> bool { - self.verify_with_zip216(msg, sig, p_g, true) - } - - pub fn verify_with_zip216( - &self, - msg: &[u8], - sig: &Signature, - p_g: SubgroupPoint, - zip216_enabled: bool, - ) -> bool { - // c = H*(Rbar || M) - let c = h_star(&sig.rbar[..], msg); - - // Signature checks: - // R != invalid - let r = { - let r = if zip216_enabled { - ExtendedPoint::from_bytes(&sig.rbar) - } else { - AffinePoint::from_bytes_pre_zip216_compatibility(sig.rbar).map(|p| p.to_extended()) - }; - if r.is_none().into() { - return false; - } - r.unwrap() - }; - // S < order(G) - // (jubjub::Scalar guarantees its representation is in the field) - let s = match read_scalar::<&[u8]>(&sig.sbar[..]) { - Ok(s) => s, - Err(_) => return false, - }; - // 0 = h_G(-S . P_G + R + c . vk) - ((self.0 * c) + r - (p_g * s)) - .mul_by_cofactor() - .is_identity() - .into() - } -} - -pub struct BatchEntry<'a> { - vk: PublicKey, - msg: &'a [u8], - sig: Signature, -} - -// TODO: #82: This is a naive implementation currently, -// and doesn't use multiexp. -pub fn batch_verify<'a, R: RngCore>( - mut rng: &mut R, - batch: &[BatchEntry<'a>], - p_g: SubgroupPoint, -) -> bool { - let mut acc = ExtendedPoint::identity(); - - for entry in batch { - let mut r = { - let r = ExtendedPoint::from_bytes(&entry.sig.rbar); - if r.is_none().into() { - return false; - } - r.unwrap() - }; - let mut s = match read_scalar::<&[u8]>(&entry.sig.sbar[..]) { - Ok(s) => s, - Err(_) => return false, - }; - - let mut c = h_star(&entry.sig.rbar[..], entry.msg); - - let z = jubjub::Fr::random(&mut rng); - - s.mul_assign(&z); - s = s.neg(); - - r *= z; - - c.mul_assign(&z); - - acc = acc + r + (entry.vk.0 * c) + (p_g * s); - } - - acc.mul_by_cofactor().is_identity().into() -} - -#[cfg(test)] -mod tests { - use group::Group; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - - use super::*; - use crate::sapling::constants::SPENDING_KEY_GENERATOR; - - #[test] - fn test_batch_verify() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let p_g = SPENDING_KEY_GENERATOR; - - let sk1 = PrivateKey(jubjub::Fr::random(&mut rng)); - let vk1 = PublicKey::from_private(&sk1, p_g); - let msg1 = b"Foo bar"; - let sig1 = sk1.sign(msg1, &mut rng, p_g); - assert!(vk1.verify(msg1, &sig1, p_g)); - - let sk2 = PrivateKey(jubjub::Fr::random(&mut rng)); - let vk2 = PublicKey::from_private(&sk2, p_g); - let msg2 = b"Foo bar"; - let sig2 = sk2.sign(msg2, &mut rng, p_g); - assert!(vk2.verify(msg2, &sig2, p_g)); - - let mut batch = vec![ - BatchEntry { - vk: vk1, - msg: msg1, - sig: sig1, - }, - BatchEntry { - vk: vk2, - msg: msg2, - sig: sig2, - }, - ]; - - assert!(batch_verify(&mut rng, &batch, p_g)); - - batch[0].sig = sig2; - - assert!(!batch_verify(&mut rng, &batch, p_g)); - } - - #[test] - fn cofactor_check() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let zero = jubjub::ExtendedPoint::identity(); - let p_g = SPENDING_KEY_GENERATOR; - - let jubjub_modulus_bytes = [ - 0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, - 0x68, 0xa6, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, - 0xea, 0xb4, 0x7d, 0x0e, - ]; - - // Get a point of order 8 - let p8 = loop { - let r = jubjub::ExtendedPoint::random(&mut rng) - .to_niels() - .multiply_bits(&jubjub_modulus_bytes); - - let r2 = r.double(); - let r4 = r2.double(); - let r8 = r4.double(); - - if r2 != zero && r4 != zero && r8 == zero { - break r; - } - }; - - let sk = PrivateKey(jubjub::Fr::random(&mut rng)); - let vk = PublicKey::from_private(&sk, p_g); - - // TODO: This test will need to change when #77 is fixed - let msg = b"Foo bar"; - let sig = sk.sign(msg, &mut rng, p_g); - assert!(vk.verify(msg, &sig, p_g)); - - let vktorsion = PublicKey(vk.0 + p8); - assert!(vktorsion.verify(msg, &sig, p_g)); - } - - #[test] - fn round_trip_serialization() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let p_g = SPENDING_KEY_GENERATOR; - - for _ in 0..1000 { - let sk = PrivateKey(jubjub::Fr::random(&mut rng)); - let vk = PublicKey::from_private(&sk, p_g); - let msg = b"Foo bar"; - let sig = sk.sign(msg, &mut rng, p_g); - - let mut sk_bytes = [0u8; 32]; - let mut vk_bytes = [0u8; 32]; - let mut sig_bytes = [0u8; 64]; - sk.write(&mut sk_bytes[..]).unwrap(); - vk.write(&mut vk_bytes[..]).unwrap(); - sig.write(&mut sig_bytes[..]).unwrap(); - - let sk_2 = PrivateKey::read(&sk_bytes[..]).unwrap(); - let vk_2 = PublicKey::from_private(&sk_2, p_g); - let mut vk_2_bytes = [0u8; 32]; - vk_2.write(&mut vk_2_bytes[..]).unwrap(); - assert!(vk_bytes == vk_2_bytes); - - let vk_2 = PublicKey::read(&vk_bytes[..]).unwrap(); - let sig_2 = Signature::read(&sig_bytes[..]).unwrap(); - assert!(vk.verify(msg, &sig_2, p_g)); - assert!(vk_2.verify(msg, &sig, p_g)); - assert!(vk_2.verify(msg, &sig_2, p_g)); - } - } - - #[test] - fn random_signatures() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let p_g = SPENDING_KEY_GENERATOR; - - for _ in 0..1000 { - let sk = PrivateKey(jubjub::Fr::random(&mut rng)); - let vk = PublicKey::from_private(&sk, p_g); - - let msg1 = b"Foo bar"; - let msg2 = b"Spam eggs"; - - let sig1 = sk.sign(msg1, &mut rng, p_g); - let sig2 = sk.sign(msg2, &mut rng, p_g); - - assert!(vk.verify(msg1, &sig1, p_g)); - assert!(vk.verify(msg2, &sig2, p_g)); - assert!(!vk.verify(msg1, &sig2, p_g)); - assert!(!vk.verify(msg2, &sig1, p_g)); - - let alpha = jubjub::Fr::random(&mut rng); - let rsk = sk.randomize(alpha); - let rvk = vk.randomize(alpha, p_g); - - let sig1 = rsk.sign(msg1, &mut rng, p_g); - let sig2 = rsk.sign(msg2, &mut rng, p_g); - - assert!(rvk.verify(msg1, &sig1, p_g)); - assert!(rvk.verify(msg2, &sig2, p_g)); - assert!(!rvk.verify(msg1, &sig2, p_g)); - assert!(!rvk.verify(msg2, &sig1, p_g)); - } - } -} diff --git a/zcash_primitives/src/sapling/value.rs b/zcash_primitives/src/sapling/value.rs index 7c61fe21f..16bab4aa5 100644 --- a/zcash_primitives/src/sapling/value.rs +++ b/zcash_primitives/src/sapling/value.rs @@ -228,9 +228,7 @@ mod tests { use super::{ testing::{arb_note_value_bounded, arb_trapdoor}, CommitmentSum, OverflowError, TrapdoorSum, ValueCommitment, ValueSum, - VALUE_COMMITMENT_RANDOMNESS_GENERATOR, }; - use crate::sapling::redjubjub; proptest! { #[test] @@ -260,8 +258,7 @@ mod tests { .sum::() .into_bvk(value_balance); - assert_eq!(redjubjub::PublicKey::from_private( - &bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR).0, bvk.0); + assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk); } } } diff --git a/zcash_primitives/src/sapling/value/sums.rs b/zcash_primitives/src/sapling/value/sums.rs index 46cc92b05..69bfc9237 100644 --- a/zcash_primitives/src/sapling/value/sums.rs +++ b/zcash_primitives/src/sapling/value/sums.rs @@ -2,8 +2,11 @@ use core::fmt::{self, Debug}; use core::iter::Sum; use core::ops::{Add, AddAssign, Sub, SubAssign}; +use group::GroupEncoding; +use redjubjub::Binding; + use super::{NoteValue, ValueCommitTrapdoor, ValueCommitment}; -use crate::sapling::{constants::VALUE_COMMITMENT_VALUE_GENERATOR, redjubjub}; +use crate::sapling::constants::VALUE_COMMITMENT_VALUE_GENERATOR; /// A value operation overflowed. #[derive(Debug)] @@ -88,8 +91,9 @@ impl TrapdoorSum { /// Transform this trapdoor sum into the corresponding RedJubjub private key. /// /// This is public for access by `zcash_proofs`. - pub fn into_bsk(self) -> redjubjub::PrivateKey { - redjubjub::PrivateKey(self.0) + pub fn into_bsk(self) -> redjubjub::SigningKey { + redjubjub::SigningKey::try_from(self.0.to_bytes()) + .expect("valid scalars are valid signing keys") } } @@ -156,7 +160,7 @@ impl CommitmentSum { /// Transform this value commitment sum into the corresponding RedJubjub public key. /// /// This is public for access by `zcash_proofs`. - pub fn into_bvk>(self, value_balance: V) -> redjubjub::PublicKey { + pub fn into_bvk>(self, value_balance: V) -> redjubjub::VerificationKey { let value: i64 = value_balance.into(); // Compute the absolute value. @@ -175,7 +179,8 @@ impl CommitmentSum { // Subtract `value_balance` from the sum to get the final bvk. let bvk = self.0 - VALUE_COMMITMENT_VALUE_GENERATOR * value_balance; - redjubjub::PublicKey(bvk) + redjubjub::VerificationKey::try_from(bvk.to_bytes()) + .expect("valid points are valid verification keys") } } diff --git a/zcash_primitives/src/sapling/verifier.rs b/zcash_primitives/src/sapling/verifier.rs index 68b3bf1ab..e03f07d7f 100644 --- a/zcash_primitives/src/sapling/verifier.rs +++ b/zcash_primitives/src/sapling/verifier.rs @@ -1,10 +1,10 @@ use bellman::{gadgets::multipack, groth16::Proof}; use bls12_381::Bls12; -use group::{ff::PrimeField, Curve, GroupEncoding}; +use group::{ff::PrimeField, Curve}; +use redjubjub::{Binding, SpendAuth}; use crate::sapling::{ note::ExtractedNoteCommitment, - redjubjub::{PublicKey, Signature}, value::{CommitmentSum, ValueCommitment}, }; @@ -36,18 +36,17 @@ impl SaplingVerificationContextInner { cv: &ValueCommitment, anchor: bls12_381::Scalar, nullifier: &[u8; 32], - rk: &PublicKey, - sighash_value: &[u8; 32], - spend_auth_sig: &Signature, + rk: &redjubjub::VerificationKey, zkproof: Proof, verifier_ctx: &mut C, - spend_auth_sig_verifier: impl FnOnce(&mut C, &PublicKey, [u8; 64], &Signature) -> bool, + spend_auth_sig_verifier: impl FnOnce(&mut C, &redjubjub::VerificationKey) -> bool, proof_verifier: impl FnOnce(&mut C, Proof, [bls12_381::Scalar; 7]) -> bool, ) -> bool { // The "cv is not small order" happens when a SpendDescription is deserialized. // This happens when transactions or blocks are received over the network, or when // mined blocks are introduced via the `submitblock` RPC method on full nodes. - if rk.0.is_small_order().into() { + let rk_affine = jubjub::AffinePoint::from_bytes((*rk).into()).unwrap(); + if rk_affine.is_small_order().into() { return false; } @@ -57,14 +56,8 @@ impl SaplingVerificationContextInner { // Grab the nullifier as a sequence of bytes let nullifier = &nullifier[..]; - // Compute the signature's message for rk/spend_auth_sig - let mut data_to_be_signed = [0u8; 64]; - data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes()); - data_to_be_signed[32..64].copy_from_slice(&sighash_value[..]); - // Verify the spend_auth_sig - let rk_affine = rk.0.to_affine(); - if !spend_auth_sig_verifier(verifier_ctx, rk, data_to_be_signed, spend_auth_sig) { + if !spend_auth_sig_verifier(verifier_ctx, rk) { return false; } @@ -145,19 +138,12 @@ impl SaplingVerificationContextInner { fn final_check>( &self, value_balance: V, - sighash_value: &[u8; 32], - binding_sig: Signature, - binding_sig_verifier: impl FnOnce(PublicKey, [u8; 64], Signature) -> bool, + binding_sig_verifier: impl FnOnce(redjubjub::VerificationKey) -> bool, ) -> bool { // Compute the final bvk. let bvk = self.cv_sum.into_bvk(value_balance); - // Compute the signature's message for bvk/binding_sig - let mut data_to_be_signed = [0u8; 64]; - data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes()); - data_to_be_signed[32..64].copy_from_slice(&sighash_value[..]); - // Verify the binding_sig - binding_sig_verifier(bvk, data_to_be_signed, binding_sig) + binding_sig_verifier(bvk) } } diff --git a/zcash_primitives/src/sapling/verifier/batch.rs b/zcash_primitives/src/sapling/verifier/batch.rs index 2cf8a5ed6..950be192b 100644 --- a/zcash_primitives/src/sapling/verifier/batch.rs +++ b/zcash_primitives/src/sapling/verifier/batch.rs @@ -68,21 +68,11 @@ impl BatchValidator { *spend.anchor(), &spend.nullifier().0, spend.rk(), - &sighash, - spend.spend_auth_sig(), zkproof, self, - |this, rk, _, spend_auth_sig| { - let rk = redjubjub::VerificationKeyBytes::::from( - rk.0.to_bytes(), - ); - let spend_auth_sig = { - let mut buf = [0; 64]; - spend_auth_sig.write(&mut buf[..]).unwrap(); - redjubjub::Signature::::from(buf) - }; - - this.signatures.queue((rk, spend_auth_sig, &sighash)); + |this, rk| { + this.signatures + .queue(((*rk).into(), *spend.spend_auth_sig(), &sighash)); true }, |this, proof, public_inputs| { @@ -125,23 +115,11 @@ impl BatchValidator { } // Check the whole-bundle consensus rules, and batch the binding signature. - ctx.final_check( - *bundle.value_balance(), - &sighash, - bundle.authorization().binding_sig, - |bvk, _, binding_sig| { - let bvk = - redjubjub::VerificationKeyBytes::::from(bvk.0.to_bytes()); - let binding_sig = { - let mut buf = [0; 64]; - binding_sig.write(&mut buf[..]).unwrap(); - redjubjub::Signature::::from(buf) - }; - - self.signatures.queue((bvk, binding_sig, &sighash)); - true - }, - ) + ctx.final_check(*bundle.value_balance(), |bvk| { + self.signatures + .queue((bvk.into(), bundle.authorization().binding_sig, &sighash)); + true + }) } /// Batch-validates the accumulated bundles. diff --git a/zcash_primitives/src/sapling/verifier/single.rs b/zcash_primitives/src/sapling/verifier/single.rs index ce535852a..79af455b1 100644 --- a/zcash_primitives/src/sapling/verifier/single.rs +++ b/zcash_primitives/src/sapling/verifier/single.rs @@ -1,27 +1,25 @@ use bellman::groth16::{verify_proof, Proof}; use bls12_381::Bls12; +use redjubjub::{Binding, SpendAuth}; use super::SaplingVerificationContextInner; use crate::sapling::{ circuit::{PreparedOutputVerifyingKey, PreparedSpendVerifyingKey}, - constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, note::ExtractedNoteCommitment, - redjubjub::{PublicKey, Signature}, value::ValueCommitment, }; /// A context object for verifying the Sapling components of a single Zcash transaction. pub struct SaplingVerificationContext { inner: SaplingVerificationContextInner, - zip216_enabled: bool, } impl SaplingVerificationContext { /// Construct a new context to be used with a single transaction. - pub fn new(zip216_enabled: bool) -> Self { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { SaplingVerificationContext { inner: SaplingVerificationContextInner::new(), - zip216_enabled, } } @@ -33,25 +31,20 @@ impl SaplingVerificationContext { cv: &ValueCommitment, anchor: bls12_381::Scalar, nullifier: &[u8; 32], - rk: PublicKey, + rk: redjubjub::VerificationKey, sighash_value: &[u8; 32], - spend_auth_sig: Signature, + spend_auth_sig: redjubjub::Signature, zkproof: Proof, verifying_key: &PreparedSpendVerifyingKey, ) -> bool { - let zip216_enabled = self.zip216_enabled; self.inner.check_spend( cv, anchor, nullifier, &rk, - sighash_value, - &spend_auth_sig, zkproof, &mut (), - |_, rk, msg, spend_auth_sig| { - rk.verify_with_zip216(&msg, spend_auth_sig, SPENDING_KEY_GENERATOR, zip216_enabled) - }, + |_, rk| rk.verify(sighash_value, &spend_auth_sig).is_ok(), |_, proof, public_inputs| { verify_proof(&verifying_key.0, &proof, &public_inputs[..]).is_ok() }, @@ -81,20 +74,10 @@ impl SaplingVerificationContext { &self, value_balance: V, sighash_value: &[u8; 32], - binding_sig: Signature, + binding_sig: redjubjub::Signature, ) -> bool { - self.inner.final_check( - value_balance, - sighash_value, - binding_sig, - |bvk, msg, binding_sig| { - bvk.verify_with_zip216( - &msg, - &binding_sig, - VALUE_COMMITMENT_RANDOMNESS_GENERATOR, - self.zip216_enabled, - ) - }, - ) + self.inner.final_check(value_balance, |bvk| { + bvk.verify(sighash_value, &binding_sig).is_ok() + }) } } diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9ef6cd00c..448e1c832 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -6,6 +6,7 @@ use std::fmt; use std::sync::mpsc::Sender; use rand::{rngs::OsRng, CryptoRng, RngCore}; +use redjubjub::SpendAuth; use crate::{ consensus::{self, BlockHeight, BranchId, NetworkUpgrade}, @@ -15,7 +16,7 @@ use crate::{ self, builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata}, prover::{OutputProver, SpendProver}, - redjubjub, Note, PaymentAddress, + Note, PaymentAddress, }, transaction::{ components::{ @@ -162,7 +163,7 @@ pub struct Builder<'a, P, R> { // `add_sapling_spend` or `add_orchard_spend`, we will build an unauthorized, unproven // transaction, and then the caller will be responsible for using the spending keys or their // derivatives for proving and signing to complete transaction creation. - sapling_asks: Vec, + sapling_asks: Vec>, orchard_saks: Vec, #[cfg(feature = "zfuture")] tze_builder: TzeBuilder<'a, TransactionData>, @@ -334,8 +335,11 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { self.sapling_builder .add_spend(&mut self.rng, &extsk, note, merkle_path)?; - self.sapling_asks - .push(redjubjub::PrivateKey(extsk.expsk.ask)); + // TODO: store a `redjubjub::SigningKey` inside `extsk`. + self.sapling_asks.push( + redjubjub::SigningKey::try_from(extsk.expsk.ask.to_bytes()) + .expect("valid scalar is valid signing key"), + ); Ok(()) } diff --git a/zcash_primitives/src/transaction/components/sapling.rs b/zcash_primitives/src/transaction/components/sapling.rs index e36b811ae..a0acd4147 100644 --- a/zcash_primitives/src/transaction/components/sapling.rs +++ b/zcash_primitives/src/transaction/components/sapling.rs @@ -1,4 +1,5 @@ use ff::PrimeField; +use redjubjub::SpendAuth; use std::io::{self, Read, Write}; @@ -12,7 +13,6 @@ use crate::{ SpendDescription, SpendDescriptionV5, }, note::ExtractedNoteCommitment, - redjubjub::{self, PublicKey, Signature}, value::ValueCommitment, Nullifier, }, @@ -81,15 +81,20 @@ fn read_nullifier(mut reader: R) -> io::Result { /// Consensus rules (§4.4): /// - Canonical encoding is enforced here. /// - "Not small order" is enforced in SaplingVerificationContext::check_spend() -fn read_rk(mut reader: R) -> io::Result { - PublicKey::read(&mut reader) +fn read_rk(mut reader: R) -> io::Result> { + let mut bytes = [0; 32]; + reader.read_exact(&mut bytes)?; + redjubjub::VerificationKey::try_from(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } /// Consensus rules (§4.4): /// - Canonical encoding is enforced here. /// - Signature validity is enforced in SaplingVerificationContext::check_spend() -fn read_spend_auth_sig(mut reader: R) -> io::Result { - Signature::read(&mut reader) +fn read_spend_auth_sig(mut reader: R) -> io::Result> { + let mut sig = [0; 64]; + reader.read_exact(&mut sig)?; + Ok(redjubjub::Signature::from(sig)) } #[cfg(feature = "temporary-zcashd")] @@ -127,9 +132,9 @@ fn write_spend_v4(mut writer: W, spend: &SpendDescription) writer.write_all(&spend.cv().to_bytes())?; writer.write_all(spend.anchor().to_repr().as_ref())?; writer.write_all(&spend.nullifier().0)?; - spend.rk().write(&mut writer)?; + writer.write_all(&<[u8; 32]>::from(*spend.rk()))?; writer.write_all(spend.zkproof())?; - spend.spend_auth_sig().write(&mut writer) + writer.write_all(&<[u8; 64]>::from(*spend.spend_auth_sig())) } fn write_spend_v5_without_witness_data( @@ -138,7 +143,7 @@ fn write_spend_v5_without_witness_data( ) -> io::Result<()> { writer.write_all(&spend.cv().to_bytes())?; writer.write_all(&spend.nullifier().0)?; - spend.rk().write(&mut writer) + writer.write_all(&<[u8; 32]>::from(*spend.rk())) } fn read_spend_v5(mut reader: &mut R) -> io::Result { @@ -350,7 +355,9 @@ pub(crate) fn read_v5_bundle( let v_output_proofs = Array::read(&mut reader, n_outputs, |r| read_zkproof(r))?; let binding_sig = if n_spends > 0 || n_outputs > 0 { - Some(redjubjub::Signature::read(&mut reader)?) + let mut sig = [0; 64]; + reader.read_exact(&mut sig)?; + Some(redjubjub::Signature::from(sig)) } else { None }; @@ -413,7 +420,7 @@ pub(crate) fn write_v5_bundle( Array::write( &mut writer, bundle.shielded_spends().iter().map(|s| s.spend_auth_sig()), - |w, e| e.write(w), + |w, e| w.write_all(&<[u8; 64]>::from(**e)), )?; Array::write( @@ -423,7 +430,7 @@ pub(crate) fn write_v5_bundle( )?; if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) { - bundle.authorization().binding_sig.write(&mut writer)?; + writer.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))?; } } else { CompactSize::write(&mut writer, 0)?; diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index b9ff932af..963e03049 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -23,7 +23,7 @@ use zcash_encoding::{CompactSize, Vector}; use crate::{ consensus::{BlockHeight, BranchId}, - sapling::{self, builder as sapling_builder, redjubjub}, + sapling::{self, builder as sapling_builder}, }; use self::{ @@ -624,7 +624,9 @@ impl Transaction { let binding_sig = if version.has_sapling() && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) { - Some(redjubjub::Signature::read(&mut reader)?) + let mut sig = [0; 64]; + reader.read_exact(&mut sig)?; + Some(redjubjub::Signature::from(sig)) } else { None }; @@ -784,7 +786,7 @@ impl Transaction { if self.version.has_sapling() { if let Some(bundle) = self.sapling_bundle.as_ref() { - bundle.authorization().binding_sig.write(&mut writer)?; + writer.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))?; } } diff --git a/zcash_primitives/src/transaction/sighash_v4.rs b/zcash_primitives/src/transaction/sighash_v4.rs index 1f1a06eb9..e50c6902f 100644 --- a/zcash_primitives/src/transaction/sighash_v4.rs +++ b/zcash_primitives/src/transaction/sighash_v4.rs @@ -112,7 +112,7 @@ fn shielded_spends_hash< data.extend_from_slice(&s_spend.cv().to_bytes()); data.extend_from_slice(s_spend.anchor().to_repr().as_ref()); data.extend_from_slice(s_spend.nullifier().as_ref()); - s_spend.rk().write(&mut data).unwrap(); + data.extend_from_slice(&<[u8; 32]>::from(*s_spend.rk())); data.extend_from_slice(s_spend.zkproof()); } Blake2bParams::new() diff --git a/zcash_primitives/src/transaction/txid.rs b/zcash_primitives/src/transaction/txid.rs index 929977aa7..ba3b45d37 100644 --- a/zcash_primitives/src/transaction/txid.rs +++ b/zcash_primitives/src/transaction/txid.rs @@ -154,7 +154,7 @@ pub(crate) fn hash_sapling_spends( nh.write_all(&s_spend.cv().to_bytes()).unwrap(); nh.write_all(&s_spend.anchor().to_repr()).unwrap(); - s_spend.rk().write(&mut nh).unwrap(); + nh.write_all(&<[u8; 32]>::from(*s_spend.rk())).unwrap(); } let compact_digest = ch.finalize(); @@ -476,14 +476,16 @@ impl TransactionDigest for BlockTxCommitmentDigester { } for spend in bundle.shielded_spends() { - spend.spend_auth_sig().write(&mut h).unwrap(); + h.write_all(&<[u8; 64]>::from(*spend.spend_auth_sig())) + .unwrap(); } for output in bundle.shielded_outputs() { h.write_all(output.zkproof()).unwrap(); } - bundle.authorization().binding_sig.write(&mut h).unwrap(); + h.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig)) + .unwrap(); } h.finalize() }