diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index 1e7b91a331..3eac233a94 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -45,6 +45,8 @@ and this library adheres to Rust's notion of - `circuit::{SpendVerifyingKey, PreparedSpendVerifyingKey}` - `circuit::{OutputVerifyingKey, PreparedOutputVerifyingKey}` - `constants` module. + - `keys::SpendAuthorizingKey` + - `keys::SpendValidatingKey` - `note_encryption::CompactOutputDescription` (moved from `zcash_primitives::transaction::components::sapling`). - `note_encryption::SaplingDomain::new` @@ -100,6 +102,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,11 +133,23 @@ 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` instead of as a bare `u64`. - `keys::DecodingError` has a new variant `UnsupportedChildIndex`. + - `keys::ExpandedSpendingKey.ask` now has type `SpendAuthorizingKey`. + - `keys::ProofGenerationKey.ak` now has type `SpendValidatingKey`. + - `keys::ViewingKey.ak` now has type `SpendValidatingKey`. - `note_encryption`: - `SaplingDomain` no longer has a `P: consensus::Parameters` type parameter. - The following methods now take a `Zip212Enforcement` argument instead of a @@ -150,6 +168,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 +215,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`): @@ -217,6 +242,12 @@ and this library adheres to Rust's notion of - `ChildIndex::NonHardened` - `sapling::ExtendedFullViewingKey::derive_child` +### Fixed +- `zcash_primitives::keys::ExpandedSpendingKey::from_spending_key` now panics if the + spending key expands to `ask = 0`. This has a negligible probability of occurring. +- `zcash_primitives::zip32::ExtendedSpendingKey::derive_child` now panics if the + child key has `ask = 0`. This has a negligible probability of occurring. + ## [0.13.0] - 2023-09-25 ### Added - `zcash_primitives::consensus::BlockHeight::saturating_sub` diff --git a/zcash_primitives/src/sapling.rs b/zcash_primitives/src/sapling.rs index 74e8ff5be7..03e019f3b5 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::{ @@ -96,3 +34,6 @@ pub mod testing { note::testing::arb_note, tree::testing::arb_node, }; } + +#[cfg(test)] +mod test_vectors; diff --git a/zcash_primitives/src/sapling/builder.rs b/zcash_primitives/src/sapling/builder.rs index 6568940574..9a7cdab319 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; 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, + keys::{OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey}, 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,10 @@ 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 = self.proof_generation_key.ak.clone(); // 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 +504,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 +670,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 +690,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: SpendValidatingKey, /// The randomization needed to derive the actual signing key for this note. alpha: jubjub::Scalar, } @@ -711,7 +698,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 +707,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 +741,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 +759,7 @@ impl Bundle, V> { self, mut rng: R, sighash: [u8; 32], - signing_keys: &[PrivateKey], + signing_keys: &[SpendAuthorizingKey], ) -> Result, Error> { signing_keys .iter() @@ -786,18 +771,18 @@ 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: &SpendAuthorizingKey) -> Self { + let expected_ak = ask.into(); 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 => { + MaybeSigned::Signature(ask.randomize(&parts.alpha).sign(&mut rng, &sighash)) } s => s, }, @@ -805,16 +790,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 +810,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 +861,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, @@ -926,11 +914,7 @@ pub mod testing { bundle.create_proofs(&MockSpendProver, &MockOutputProver, &mut rng, None); bundle - .apply_signatures( - &mut rng, - fake_sighash_bytes, - &[PrivateKey(extsk.expsk.ask)], - ) + .apply_signatures(&mut rng, fake_sighash_bytes, &[extsk.expsk.ask]) .unwrap() }, ) diff --git a/zcash_primitives/src/sapling/bundle.rs b/zcash_primitives/src/sapling/bundle.rs index 6158087049..a95bd96464 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/circuit.rs b/zcash_primitives/src/sapling/circuit.rs index 4683f052c2..cbdf0573e5 100644 --- a/zcash_primitives/src/sapling/circuit.rs +++ b/zcash_primitives/src/sapling/circuit.rs @@ -160,7 +160,7 @@ impl Circuit for Spend { // Prover witnesses ak (ensures that it's on the curve) let ak = ecc::EdwardsPoint::witness( cs.namespace(|| "ak"), - self.proof_generation_key.as_ref().map(|k| k.ak.into()), + self.proof_generation_key.as_ref().map(|k| (&k.ak).into()), )?; // There are no sensible attacks on small order points @@ -634,9 +634,12 @@ pub struct PreparedOutputVerifyingKey(pub(crate) groth16::PreparedVerifyingKey); + +impl PartialEq for SpendAuthorizingKey { + fn eq(&self, other: &Self) -> bool { + <[u8; 32]>::from(self.0) + .ct_eq(&<[u8; 32]>::from(other.0)) + .into() + } +} + +impl Eq for SpendAuthorizingKey {} + +impl From<&SpendValidatingKey> for jubjub::ExtendedPoint { + fn from(spend_validating_key: &SpendValidatingKey) -> jubjub::ExtendedPoint { + jubjub::ExtendedPoint::from_bytes(&spend_validating_key.to_bytes()).unwrap() + } +} + +impl SpendAuthorizingKey { + /// Derives ask from sk. Internal use only, does not enforce all constraints. + fn derive_inner(sk: &[u8]) -> jubjub::Scalar { + jubjub::Scalar::from_bytes_wide(prf_expand(sk, &[0x00]).as_array()) + } + + /// Constructs a `SpendAuthorizingKey` from a raw scalar. + pub(crate) fn from_scalar(ask: jubjub::Scalar) -> Option { + if ask.is_zero().into() { + None + } else { + Some(SpendAuthorizingKey(ask.to_bytes().try_into().unwrap())) + } + } + + /// Derives a `SpendAuthorizingKey` from a spending key. + fn from_spending_key(sk: &[u8]) -> Option { + Self::from_scalar(Self::derive_inner(sk)) + } + + /// Parses a `SpendAuthorizingKey` from its encoded form. + pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + <[u8; 32]>::try_from(bytes) + .ok() + .and_then(|b| { + // RedJubjub.Private permits the full set of Jubjub scalars including + // zero. However, a SpendAuthorizingKey is further restricted within the + // Sapling key tree to be a non-zero scalar. + jubjub::Scalar::from_repr(b) + .and_then(|s| { + CtOption::new( + redjubjub::SigningKey::try_from(b) + .expect("RedJubjub permits the set of valid SpendAuthorizingKeys"), + !s.is_zero(), + ) + }) + .into() + }) + .map(SpendAuthorizingKey) + } + + /// Converts this spend authorizing key to its serialized form. + pub(crate) fn to_bytes(&self) -> [u8; 32] { + <[u8; 32]>::from(self.0) + } + + /// Converts this spend authorizing key to a raw scalar. + /// + /// Only used for ZIP 32 child derivation. + pub(crate) fn to_scalar(&self) -> jubjub::Scalar { + jubjub::Scalar::from_repr(self.0.into()).unwrap() + } + + /// Randomizes this spend authorizing key with the given `randomizer`. + /// + /// The resulting key can be used to actually sign a spend. + pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::SigningKey { + self.0.randomize(randomizer) + } +} + +/// A key used to validate spend authorization signatures. +/// +/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents]. +/// +/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents +#[derive(Clone, Debug)] +pub struct SpendValidatingKey(redjubjub::VerificationKey); + +impl From<&SpendAuthorizingKey> for SpendValidatingKey { + fn from(ask: &SpendAuthorizingKey) -> Self { + SpendValidatingKey((&ask.0).into()) + } +} + +impl PartialEq for SpendValidatingKey { + fn eq(&self, other: &Self) -> bool { + <[u8; 32]>::from(self.0) + .ct_eq(&<[u8; 32]>::from(other.0)) + .into() + } +} + +impl Eq for SpendValidatingKey {} + +impl SpendValidatingKey { + /// For circuit tests only. + #[cfg(test)] + pub(crate) fn fake_random(mut rng: R) -> Self { + loop { + if let Some(k) = Self::from_bytes(&jubjub::SubgroupPoint::random(&mut rng).to_bytes()) { + break k; + } + } + } + + /// Only exposed for `zcashd` unit tests. + #[cfg(feature = "temporary-zcashd")] + pub fn temporary_zcash_from_bytes(bytes: &[u8]) -> Option { + Self::from_bytes(bytes) + } + + /// Parses a `SpendValidatingKey` from its encoded form. + pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + <[u8; 32]>::try_from(bytes) + .ok() + .and_then(|b| { + // RedJubjub.Public permits the full set of Jubjub points including the + // identity and cofactors; this is the type used for `rk` in Spend + // descriptions. However, a SpendValidatingKey is further restricted + // within the Sapling key tree to be a non-identity element of the + // prime-order subgroup. + jubjub::SubgroupPoint::from_bytes(&b) + .and_then(|p| { + CtOption::new( + redjubjub::VerificationKey::try_from(b) + .expect("RedJubjub permits the set of valid SpendValidatingKeys"), + !p.is_identity(), + ) + }) + .into() + }) + .map(SpendValidatingKey) + } + + /// Converts this spend validating key to its serialized form, + /// `LEBS2OSP_256(repr_J(ak))`. + pub(crate) fn to_bytes(&self) -> [u8; 32] { + <[u8; 32]>::from(self.0) + } + + /// Randomizes this spend validating key with the given `randomizer`. + pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::VerificationKey { + self.0.randomize(randomizer) + } +} + /// An outgoing viewing key #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct OutgoingViewingKey(pub [u8; 32]); @@ -45,7 +210,7 @@ pub struct OutgoingViewingKey(pub [u8; 32]); /// A Sapling expanded spending key #[derive(Clone)] pub struct ExpandedSpendingKey { - pub ask: jubjub::Fr, + pub ask: SpendAuthorizingKey, pub nsk: jubjub::Fr, pub ovk: OutgoingViewingKey, } @@ -58,8 +223,15 @@ impl fmt::Debug for ExpandedSpendingKey { } impl ExpandedSpendingKey { + /// Expands a spending key into its components. + /// + /// # Panics + /// + /// Panics if this spending key expands to `ask = 0`. This has a negligible + /// probability of occurring. pub fn from_spending_key(sk: &[u8]) -> Self { - let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array()); + let ask = + SpendAuthorizingKey::from_spending_key(sk).expect("negligible chance of ask == 0"); let nsk = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x01]).as_array()); let mut ovk = OutgoingViewingKey([0u8; 32]); ovk.0 @@ -69,7 +241,7 @@ impl ExpandedSpendingKey { pub fn proof_generation_key(&self) -> ProofGenerationKey { ProofGenerationKey { - ak: SPENDING_KEY_GENERATOR * self.ask, + ak: (&self.ask).into(), nsk: self.nsk, } } @@ -85,8 +257,7 @@ impl ExpandedSpendingKey { }); } - let ask = Option::from(jubjub::Fr::from_repr(b[0..32].try_into().unwrap())) - .ok_or(DecodingError::InvalidAsk)?; + let ask = SpendAuthorizingKey::from_bytes(&b[0..32]).ok_or(DecodingError::InvalidAsk)?; let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap())) .ok_or(DecodingError::InvalidNsk)?; let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap()); @@ -119,7 +290,7 @@ impl ExpandedSpendingKey { /// [ZIP 32](https://zips.z.cash/zip-0032) pub fn to_bytes(&self) -> [u8; 96] { let mut result = [0u8; 96]; - result[0..32].copy_from_slice(&self.ask.to_repr()); + result[0..32].copy_from_slice(&self.ask.to_bytes()); result[32..64].copy_from_slice(&self.nsk.to_repr()); result[64..96].copy_from_slice(&self.ovk.0); result @@ -128,7 +299,7 @@ impl ExpandedSpendingKey { #[derive(Clone)] pub struct ProofGenerationKey { - pub ak: jubjub::SubgroupPoint, + pub ak: SpendValidatingKey, pub nsk: jubjub::Fr, } @@ -143,7 +314,7 @@ impl fmt::Debug for ProofGenerationKey { impl ProofGenerationKey { pub fn to_viewing_key(&self) -> ViewingKey { ViewingKey { - ak: self.ak, + ak: self.ak.clone(), nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk), } } @@ -155,13 +326,13 @@ pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint); #[derive(Debug, Clone)] pub struct ViewingKey { - pub ak: jubjub::SubgroupPoint, + pub ak: SpendValidatingKey, pub nk: NullifierDerivingKey, } impl ViewingKey { - pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint { - self.ak + constants::SPENDING_KEY_GENERATOR * ar + pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey { + self.ak.randomize(&ar) } pub fn ivk(&self) -> SaplingIvk { @@ -184,7 +355,7 @@ impl Clone for FullViewingKey { fn clone(&self) -> Self { FullViewingKey { vk: ViewingKey { - ak: self.vk.ak, + ak: self.vk.ak.clone(), nk: self.vk.nk, }, ovk: self.ovk, @@ -196,7 +367,7 @@ impl FullViewingKey { pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self { FullViewingKey { vk: ViewingKey { - ak: SPENDING_KEY_GENERATOR * expsk.ask, + ak: (&expsk.ask).into(), nk: NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * expsk.nsk), }, ovk: expsk.ovk, @@ -207,14 +378,14 @@ impl FullViewingKey { let ak = { let mut buf = [0u8; 32]; reader.read_exact(&mut buf)?; - jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity())) + SpendValidatingKey::from_bytes(&buf) }; let nk = { let mut buf = [0u8; 32]; reader.read_exact(&mut buf)?; jubjub::SubgroupPoint::from_bytes(&buf) }; - if ak.is_none().into() { + if ak.is_none() { return Err(io::Error::new( io::ErrorKind::InvalidInput, "ak not of prime order", @@ -520,8 +691,8 @@ pub mod testing { mod tests { use group::{Group, GroupEncoding}; - use super::FullViewingKey; - use crate::sapling::constants::SPENDING_KEY_GENERATOR; + use super::{FullViewingKey, SpendAuthorizingKey, SpendValidatingKey}; + use crate::sapling::{constants::SPENDING_KEY_GENERATOR, test_vectors}; #[test] fn ak_must_be_prime_order() { @@ -545,4 +716,31 @@ mod tests { // nk is allowed to be the identity. assert!(FullViewingKey::read(&buf[..]).is_ok()); } + + #[test] + fn spend_auth_sig_test_vectors() { + for tv in test_vectors::signatures::make_test_vectors() { + let sk = SpendAuthorizingKey::from_bytes(&tv.sk).unwrap(); + let vk = SpendValidatingKey::from_bytes(&tv.vk).unwrap(); + let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap(); + let sig = redjubjub::Signature::from(tv.sig); + let rsig = redjubjub::Signature::from(tv.rsig); + + let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap(); + + assert_eq!(<[u8; 32]>::from(sk.randomize(&alpha)), tv.rsk); + assert_eq!(vk.randomize(&alpha), rvk); + + // assert_eq!(vk.0.verify(&tv.m, &sig), Ok(())); + // assert_eq!(rvk.verify(&tv.m, &rsig), Ok(())); + assert_eq!( + vk.0.verify(&tv.m, &rsig), + Err(redjubjub::Error::InvalidSignature), + ); + assert_eq!( + rvk.verify(&tv.m, &sig), + Err(redjubjub::Error::InvalidSignature), + ); + } + } } diff --git a/zcash_primitives/src/sapling/redjubjub.rs b/zcash_primitives/src/sapling/redjubjub.rs deleted file mode 100644 index 9df1d07290..0000000000 --- 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/test_vectors.rs b/zcash_primitives/src/sapling/test_vectors.rs new file mode 100644 index 0000000000..8232fe04ec --- /dev/null +++ b/zcash_primitives/src/sapling/test_vectors.rs @@ -0,0 +1 @@ +pub(crate) mod signatures; diff --git a/zcash_primitives/src/sapling/test_vectors/signatures.rs b/zcash_primitives/src/sapling/test_vectors/signatures.rs new file mode 100644 index 0000000000..7ce341cfff --- /dev/null +++ b/zcash_primitives/src/sapling/test_vectors/signatures.rs @@ -0,0 +1,476 @@ +pub(crate) struct TestVector { + pub(crate) sk: [u8; 32], + pub(crate) vk: [u8; 32], + pub(crate) alpha: [u8; 32], + pub(crate) rsk: [u8; 32], + pub(crate) rvk: [u8; 32], + pub(crate) m: [u8; 32], + pub(crate) sig: [u8; 64], + pub(crate) rsig: [u8; 64], +} + +pub(crate) fn make_test_vectors() -> Vec { + // From https://github.com/zcash/zcash-test-vectors/blob/master/zcash_test_vectors/sapling/redjubjub.py + vec![ + TestVector { + sk: [ + 0x18, 0xe2, 0x8d, 0xea, 0x5c, 0x11, 0x81, 0x7a, 0xee, 0xb2, 0x1a, 0x19, 0x98, 0x1d, + 0x28, 0x36, 0x8e, 0xc4, 0x38, 0xaf, 0xc2, 0x5a, 0x8d, 0xb9, 0x4e, 0xbe, 0x08, 0xd7, + 0xa0, 0x28, 0x8e, 0x09, + ], + vk: [ + 0x9b, 0x01, 0x53, 0xb0, 0x3d, 0x32, 0x0f, 0xe2, 0x3e, 0x28, 0x34, 0xd5, 0xd6, 0x1d, + 0xbb, 0x1f, 0x51, 0x9b, 0x3f, 0x41, 0xf8, 0xf9, 0x46, 0x15, 0x2b, 0xf0, 0xc3, 0xf2, + 0x47, 0xd1, 0x18, 0x07, + ], + alpha: [ + 0xff, 0xd1, 0xa1, 0x27, 0x32, 0x52, 0xb1, 0x87, 0xf4, 0xed, 0x32, 0x6d, 0xfc, 0x98, + 0x85, 0x3e, 0x29, 0x17, 0xc2, 0xb3, 0x63, 0x79, 0xb1, 0x75, 0xda, 0x63, 0xb9, 0xef, + 0x6d, 0xda, 0x6c, 0x08, + ], + rsk: [ + 0x60, 0x87, 0x38, 0x3b, 0x30, 0x55, 0x9b, 0x31, 0x60, 0x90, 0x85, 0xb9, 0x00, 0x96, + 0x45, 0xce, 0xb6, 0xa0, 0xc6, 0x61, 0x25, 0x99, 0xd7, 0x28, 0x80, 0x72, 0x8e, 0x61, + 0x24, 0x4e, 0x7d, 0x03, + ], + rvk: [ + 0xc1, 0xba, 0xbc, 0xb6, 0xea, 0xe2, 0xb9, 0x94, 0xee, 0x6d, 0x65, 0xc1, 0x0b, 0x9d, + 0xad, 0x59, 0x40, 0xdc, 0x73, 0x5b, 0x07, 0x50, 0x4d, 0xae, 0xd1, 0xe4, 0x6b, 0x07, + 0x09, 0xb4, 0x51, 0x36, + ], + m: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + sig: [ + 0xdc, 0xa3, 0xbb, 0x2c, 0xb8, 0xf0, 0x48, 0xcc, 0xab, 0x10, 0xae, 0xd7, 0x75, 0x46, + 0xc1, 0xdb, 0xb1, 0x0c, 0xc4, 0xfb, 0x15, 0xab, 0x02, 0xac, 0xae, 0xf9, 0x44, 0xdd, + 0xab, 0x8b, 0x67, 0x22, 0x54, 0x5f, 0xda, 0x4c, 0x62, 0x04, 0x6d, 0x69, 0xd9, 0x8f, + 0x92, 0x2f, 0x4e, 0x8c, 0x21, 0x0b, 0xc4, 0x7b, 0x4f, 0xdd, 0xe0, 0xa1, 0x94, 0x71, + 0x79, 0x80, 0x4c, 0x1a, 0xce, 0x56, 0x90, 0x05, + ], + rsig: [ + 0x70, 0xc2, 0x84, 0x50, 0x4e, 0x90, 0xf0, 0x00, 0x8e, 0x8e, 0xd2, 0x20, 0x8f, 0x49, + 0x69, 0x72, 0x7a, 0x41, 0x5e, 0xc3, 0x10, 0x2c, 0x29, 0x9e, 0x39, 0x8b, 0x6c, 0x16, + 0x57, 0x2b, 0xd9, 0x64, 0x3e, 0xe1, 0x01, 0x17, 0x66, 0x68, 0x1e, 0x40, 0x6e, 0xe6, + 0xbe, 0xe3, 0xd0, 0x3e, 0xe8, 0xf2, 0x71, 0x76, 0xe3, 0x2f, 0xba, 0xbd, 0xde, 0xd2, + 0x0b, 0x0d, 0x17, 0x86, 0xa4, 0xee, 0x18, 0x01, + ], + }, + TestVector { + sk: [ + 0x05, 0x96, 0x54, 0xf9, 0x61, 0x27, 0x3d, 0xaf, 0xda, 0x3b, 0x26, 0x77, 0xb3, 0x5c, + 0x18, 0xaf, 0x6b, 0x11, 0xad, 0xfb, 0x9e, 0xe9, 0x0b, 0x48, 0x93, 0x5e, 0x55, 0x7c, + 0x8d, 0x5d, 0x9c, 0x04, + ], + vk: [ + 0xfa, 0xf6, 0xc3, 0xb7, 0x37, 0xe8, 0xe6, 0x11, 0xaa, 0xfe, 0xa5, 0x2f, 0x03, 0xbb, + 0x27, 0x86, 0xe1, 0x83, 0x53, 0xeb, 0xe0, 0xd3, 0x13, 0x9e, 0x3c, 0x54, 0x49, 0x87, + 0x80, 0xc8, 0xc1, 0x99, + ], + alpha: [ + 0xc3, 0x0b, 0x96, 0x20, 0x8d, 0xa8, 0x00, 0xe1, 0x0a, 0xf0, 0x25, 0x42, 0xce, 0x69, + 0x4b, 0x7e, 0xd7, 0x6a, 0x28, 0x29, 0x9f, 0x85, 0x99, 0x8e, 0x5d, 0x61, 0x08, 0x12, + 0x68, 0x1b, 0xf0, 0x03, + ], + rsk: [ + 0xc8, 0xa1, 0xea, 0x19, 0xef, 0xcf, 0x3d, 0x90, 0xe5, 0x2b, 0x4c, 0xb9, 0x81, 0xc6, + 0x63, 0x2d, 0x43, 0x7c, 0xd5, 0x24, 0x3e, 0x6f, 0xa5, 0xd6, 0xf0, 0xbf, 0x5d, 0x8e, + 0xf5, 0x78, 0x8c, 0x08, + ], + rvk: [ + 0xd5, 0x24, 0xdc, 0xe7, 0x73, 0x40, 0x69, 0x75, 0x8a, 0x91, 0xf0, 0x07, 0xa8, 0x69, + 0x50, 0x5d, 0xfc, 0x4a, 0xba, 0x17, 0x20, 0x59, 0x4d, 0x4d, 0x74, 0xf0, 0x07, 0x70, + 0x0e, 0x62, 0xee, 0x00, + ], + m: [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + ], + sig: [ + 0xb5, 0xa1, 0xf3, 0x2d, 0x3d, 0x50, 0xfc, 0x73, 0x8b, 0x5c, 0x3b, 0x4e, 0x99, 0x60, + 0x72, 0x9c, 0xe4, 0x31, 0x6b, 0xa7, 0x72, 0x1a, 0x12, 0x68, 0x66, 0x04, 0xfe, 0xba, + 0x6b, 0xd7, 0x48, 0x45, 0x00, 0x70, 0xcb, 0x92, 0x24, 0x06, 0xfd, 0xfc, 0x5d, 0x60, + 0xde, 0xa9, 0xbe, 0x3a, 0x52, 0x6a, 0x16, 0xcf, 0xeb, 0x87, 0x77, 0x79, 0xfb, 0x78, + 0x2d, 0x5d, 0x41, 0x39, 0x5b, 0x45, 0x5f, 0x04, + ], + rsig: [ + 0x5a, 0x5a, 0x20, 0xd2, 0x00, 0xef, 0xdd, 0xd4, 0x98, 0xdf, 0xae, 0x2a, 0x9e, 0xf8, + 0xcf, 0x01, 0x28, 0x1a, 0x89, 0x19, 0x01, 0x8a, 0x82, 0x4c, 0xc7, 0xa4, 0x98, 0x3b, + 0x9a, 0x0d, 0x4a, 0x06, 0xff, 0x17, 0x20, 0x79, 0xe0, 0x13, 0xd4, 0x2a, 0x2a, 0x3a, + 0x88, 0xa6, 0x52, 0x0c, 0x86, 0xfc, 0xe3, 0xb9, 0x8e, 0x1e, 0xfa, 0xa3, 0x25, 0x83, + 0x2a, 0x6a, 0x56, 0x58, 0xd8, 0xdd, 0x7c, 0x0a, + ], + }, + TestVector { + sk: [ + 0xad, 0xe7, 0xab, 0xb5, 0x51, 0xc7, 0x9d, 0x0f, 0x0e, 0x42, 0xef, 0x7f, 0x12, 0x06, + 0xb8, 0x77, 0x12, 0xa8, 0x4a, 0x61, 0xde, 0xa3, 0xf3, 0x7b, 0x42, 0x49, 0x6d, 0x7e, + 0xfd, 0x12, 0x52, 0x0c, + ], + vk: [ + 0x36, 0x9e, 0xa7, 0x51, 0x76, 0x2f, 0x83, 0x9d, 0x25, 0x70, 0x1a, 0x5e, 0xeb, 0x55, + 0x1e, 0xc4, 0xf0, 0x6c, 0x12, 0x90, 0xb3, 0xb9, 0xc3, 0xa7, 0x24, 0x40, 0x2d, 0xec, + 0x02, 0x73, 0x92, 0x21, + ], + alpha: [ + 0x81, 0x92, 0x25, 0x29, 0xa6, 0x3e, 0xe7, 0x43, 0xfc, 0x4f, 0xbb, 0xac, 0x45, 0xc4, + 0x98, 0x83, 0x16, 0xbc, 0x9b, 0x6e, 0x42, 0x8b, 0x01, 0xa8, 0xd3, 0x1f, 0xc1, 0xc2, + 0xa6, 0xca, 0x62, 0x05, + ], + rsk: [ + 0x77, 0x4d, 0xda, 0x07, 0x99, 0xf7, 0xed, 0x82, 0x87, 0x81, 0xe2, 0x5f, 0xc4, 0xa9, + 0xe8, 0x54, 0x28, 0x29, 0xb2, 0xce, 0x1f, 0xf4, 0x8d, 0x1d, 0x6d, 0xb9, 0xfa, 0xdb, + 0xb9, 0x28, 0x37, 0x03, + ], + rvk: [ + 0x0d, 0x92, 0xad, 0x6d, 0x46, 0xed, 0xac, 0xd0, 0x23, 0xd4, 0xd2, 0xef, 0x70, 0x3a, + 0x6c, 0xa0, 0xa7, 0x92, 0xcf, 0xc4, 0xb7, 0xda, 0x11, 0xc2, 0x35, 0x3b, 0xc8, 0x45, + 0xa2, 0x7a, 0x97, 0x4d, + ], + m: [ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, + ], + sig: [ + 0x1f, 0x3e, 0x8a, 0x94, 0x31, 0x0c, 0x20, 0x71, 0xa7, 0x0f, 0x9d, 0xf5, 0xe7, 0x9a, + 0xa9, 0xe8, 0x48, 0x5d, 0xec, 0xcb, 0x17, 0x8b, 0xdf, 0xf9, 0x80, 0x5f, 0xcb, 0xe6, + 0xf7, 0xd5, 0x51, 0xee, 0xe3, 0xc3, 0x54, 0x2c, 0xa7, 0x5c, 0x9d, 0x8d, 0x4a, 0xdc, + 0x54, 0xd7, 0x2c, 0x3d, 0xbe, 0x28, 0x62, 0x6d, 0x20, 0x78, 0x5b, 0xb7, 0xf5, 0x88, + 0xc1, 0xa5, 0x82, 0xb8, 0x93, 0xdb, 0xb6, 0x01, + ], + rsig: [ + 0xd1, 0x36, 0x21, 0x4c, 0x5d, 0x52, 0x8e, 0xa3, 0xd4, 0xcb, 0x7b, 0x63, 0x1a, 0x6b, + 0xb0, 0x36, 0x06, 0x49, 0x73, 0xa1, 0x08, 0xb7, 0x33, 0xa5, 0xe3, 0xa4, 0x52, 0xab, + 0x52, 0xa6, 0x59, 0xe5, 0x67, 0xcb, 0x55, 0xd2, 0x64, 0x4e, 0x74, 0xb6, 0xe8, 0x42, + 0x6f, 0x2a, 0x7d, 0xd2, 0xa0, 0x4d, 0x2d, 0xda, 0x49, 0x35, 0xcc, 0x38, 0x20, 0xb7, + 0x7a, 0x9c, 0x1a, 0xb6, 0x19, 0x86, 0x3c, 0x05, + ], + }, + TestVector { + sk: [ + 0xc9, 0xd2, 0xae, 0x1f, 0x6d, 0x32, 0xa6, 0x75, 0xd0, 0x9e, 0xb0, 0x82, 0x3f, 0x46, + 0x7f, 0xa9, 0x21, 0xb3, 0x28, 0x4a, 0xcb, 0x35, 0xfa, 0xbd, 0xfc, 0x99, 0x4d, 0xe5, + 0x49, 0xb8, 0x59, 0x0d, + ], + vk: [ + 0x2d, 0x2f, 0x31, 0x6e, 0x5c, 0x36, 0x9a, 0xe4, 0xdd, 0x2c, 0x82, 0x5f, 0x3d, 0x86, + 0x46, 0x00, 0x58, 0x40, 0x71, 0x84, 0x60, 0x3b, 0x21, 0x2c, 0xf3, 0x45, 0x9f, 0x36, + 0xc8, 0x69, 0x7f, 0xd8, + ], + alpha: [ + 0xeb, 0xbc, 0x89, 0x03, 0x11, 0x07, 0xc4, 0x4f, 0x47, 0x88, 0x9e, 0xd4, 0xd4, 0x37, + 0x5a, 0x41, 0x14, 0xcf, 0x8a, 0x75, 0xdd, 0x33, 0xb9, 0x62, 0xf2, 0xd7, 0x59, 0xd3, + 0xf4, 0xc6, 0xdf, 0x06, + ], + rsk: [ + 0xfd, 0x62, 0x41, 0x4c, 0x1f, 0x2b, 0xd3, 0xf4, 0x94, 0x16, 0x87, 0x8a, 0x80, 0x5d, + 0x71, 0x44, 0x35, 0x47, 0x7f, 0xbe, 0xa7, 0x2e, 0x4c, 0x1a, 0x46, 0xc2, 0x73, 0x53, + 0x54, 0xca, 0xbb, 0x05, + ], + rvk: [ + 0xf0, 0x43, 0x0e, 0x95, 0x3b, 0xe6, 0x0b, 0xf4, 0x38, 0xdb, 0xdc, 0xc2, 0x30, 0x3f, + 0x0e, 0x32, 0xa6, 0xf7, 0xce, 0x2f, 0xbe, 0xdf, 0xb1, 0x3a, 0xc5, 0x18, 0xf7, 0x5a, + 0x3f, 0xd1, 0x0e, 0xb5, + ], + m: [ + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, + ], + sig: [ + 0x12, 0xc7, 0x8d, 0xdd, 0x20, 0xd3, 0x0a, 0x61, 0xf8, 0x93, 0x0c, 0x6f, 0xe0, 0x85, + 0x0f, 0xd1, 0x12, 0xbb, 0x7b, 0xe8, 0x8b, 0x12, 0x38, 0xea, 0x33, 0xd6, 0xbe, 0xf8, + 0x81, 0xc1, 0x02, 0xd1, 0x04, 0xaa, 0x36, 0x54, 0x4a, 0x78, 0x47, 0x1c, 0x9e, 0x28, + 0x42, 0xe6, 0xfd, 0x42, 0x55, 0x83, 0x46, 0xcf, 0xf4, 0x31, 0x27, 0x03, 0x26, 0x66, + 0xeb, 0x11, 0x6f, 0x44, 0x2a, 0x28, 0x48, 0x0c, + ], + rsig: [ + 0x01, 0xba, 0xaa, 0x26, 0x27, 0x4c, 0x14, 0x9a, 0xcf, 0x12, 0xe1, 0xcc, 0xf5, 0x50, + 0x7d, 0x56, 0x79, 0x04, 0x82, 0xf0, 0x67, 0xe5, 0xc9, 0x2b, 0x32, 0x19, 0xad, 0x6b, + 0xf9, 0x11, 0x18, 0xcc, 0x3f, 0xce, 0x8d, 0x2a, 0x23, 0x19, 0x8a, 0x3b, 0x29, 0x0a, + 0x7b, 0xf6, 0x8c, 0x2a, 0xc0, 0x7b, 0x5d, 0x90, 0x62, 0xb9, 0xf8, 0x68, 0x66, 0x2b, + 0xb2, 0x52, 0x49, 0x12, 0xd4, 0x85, 0x6e, 0x0c, + ], + }, + TestVector { + sk: [ + 0x33, 0xbc, 0xd2, 0x86, 0x45, 0x41, 0xb8, 0xbb, 0x7f, 0xdc, 0x77, 0xa1, 0x9d, 0x97, + 0x0f, 0x92, 0x4e, 0xae, 0xec, 0xf4, 0x10, 0x3c, 0x38, 0xc8, 0xd2, 0xb0, 0x66, 0x81, + 0x42, 0xf2, 0x7d, 0x09, + ], + vk: [ + 0x74, 0x17, 0x94, 0xe6, 0x2c, 0xf9, 0x32, 0x0c, 0x58, 0xba, 0xc5, 0x94, 0xa2, 0xb9, + 0x0e, 0x34, 0x0a, 0x6d, 0x8a, 0x68, 0x05, 0x6f, 0x6e, 0xd5, 0xc7, 0x86, 0x8c, 0x5f, + 0xf3, 0xe4, 0xd6, 0x16, + ], + alpha: [ + 0x7c, 0xe7, 0x25, 0xa5, 0xfe, 0xf6, 0x1b, 0xd4, 0xa1, 0xe9, 0xc7, 0x73, 0x28, 0xe8, + 0x21, 0x0e, 0xb7, 0x29, 0x2d, 0x95, 0x4c, 0x64, 0xe9, 0x9e, 0x8b, 0xed, 0xd0, 0x7a, + 0xb3, 0xab, 0x0e, 0x0d, + ], + rsk: [ + 0xf8, 0x76, 0x01, 0x55, 0xe5, 0x29, 0x3d, 0xbf, 0x9e, 0xb5, 0x77, 0x48, 0x32, 0x5f, + 0xc9, 0xf9, 0x04, 0x9d, 0xe5, 0x88, 0x5c, 0x65, 0xba, 0x60, 0xb5, 0xee, 0x03, 0x97, + 0x0b, 0xe9, 0x0e, 0x08, + ], + rvk: [ + 0x66, 0x62, 0xba, 0x09, 0x95, 0x0a, 0xcc, 0xd2, 0xce, 0xa3, 0xc7, 0xa8, 0x12, 0x90, + 0xcd, 0x59, 0x78, 0xa6, 0x2b, 0x5a, 0xc5, 0xbb, 0xc4, 0x8d, 0x9f, 0x58, 0x19, 0xcd, + 0xc9, 0x64, 0x6f, 0x0a, + ], + m: [ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, + ], + sig: [ + 0x77, 0x4a, 0xc4, 0x67, 0x3f, 0x09, 0xf3, 0xac, 0x57, 0x89, 0xb2, 0x86, 0xb5, 0xee, + 0xcb, 0xed, 0xb2, 0x57, 0x23, 0x4e, 0x8c, 0xdf, 0xd9, 0x3f, 0x02, 0x89, 0x09, 0x78, + 0xa6, 0xbb, 0xa6, 0x11, 0x69, 0xed, 0x48, 0xf9, 0xe1, 0xc9, 0xfd, 0x13, 0x19, 0xbd, + 0x33, 0x0d, 0x2c, 0xf5, 0xb4, 0x91, 0x01, 0x0d, 0x69, 0xb0, 0x43, 0xf4, 0x64, 0x8b, + 0xff, 0x55, 0x41, 0x62, 0xc6, 0xa6, 0xdc, 0x09, + ], + rsig: [ + 0x7c, 0x6c, 0x49, 0x8d, 0xe0, 0x01, 0x78, 0x61, 0x09, 0xb3, 0x03, 0xa4, 0xc5, 0xdc, + 0xb7, 0xfd, 0x07, 0x57, 0x50, 0xa0, 0xb9, 0xdf, 0x5e, 0x1e, 0x2a, 0x8e, 0x75, 0x47, + 0xb7, 0xed, 0x70, 0xcc, 0x0b, 0x56, 0xa5, 0xbf, 0xa9, 0x65, 0x78, 0x43, 0xef, 0xd8, + 0x9c, 0x66, 0xa8, 0x4f, 0x41, 0xd2, 0xb1, 0xb5, 0x07, 0x51, 0x19, 0x6b, 0x1e, 0x8c, + 0x0c, 0x44, 0x98, 0x60, 0x06, 0x96, 0xa4, 0x04, + ], + }, + TestVector { + sk: [ + 0xca, 0x35, 0x06, 0xd6, 0xaf, 0x77, 0x67, 0xb5, 0x79, 0x0e, 0xf0, 0xc5, 0x19, 0x0f, + 0xb3, 0xf3, 0x87, 0x7c, 0x4a, 0xab, 0x40, 0xe0, 0xdd, 0x65, 0x1a, 0xbb, 0xda, 0xcb, + 0x54, 0x4e, 0xd0, 0x05, + ], + vk: [ + 0xba, 0xb6, 0xcf, 0xb5, 0xc8, 0xea, 0x34, 0x91, 0x25, 0x1b, 0x46, 0xd5, 0x2a, 0xca, + 0x25, 0xd9, 0xe9, 0xaf, 0x69, 0xfa, 0xa9, 0xb4, 0xe4, 0x0b, 0x03, 0xad, 0x00, 0x86, + 0xde, 0x59, 0xb5, 0x1f, + ], + alpha: [ + 0xbe, 0xa3, 0x87, 0x20, 0x3f, 0x43, 0x76, 0x0a, 0xd3, 0x7d, 0x61, 0xde, 0x0e, 0xb5, + 0x9f, 0xca, 0x6c, 0xab, 0x75, 0x60, 0xdf, 0x64, 0xfa, 0xbb, 0x95, 0x11, 0x57, 0x9f, + 0x6f, 0x68, 0x26, 0x06, + ], + rsk: [ + 0x88, 0xd9, 0x8d, 0xf6, 0xee, 0xba, 0xdd, 0xbf, 0x4c, 0x8c, 0x51, 0xa4, 0x28, 0xc4, + 0x52, 0xbe, 0xf4, 0x27, 0xc0, 0x0b, 0x20, 0x45, 0xd8, 0x21, 0xb0, 0xcc, 0x31, 0x6b, + 0xc4, 0xb6, 0xf6, 0x0b, + ], + rvk: [ + 0x11, 0x26, 0x7d, 0x14, 0xd5, 0xe0, 0xb2, 0xbb, 0x3c, 0xe0, 0x99, 0xe8, 0xef, 0x84, + 0x49, 0x47, 0x1c, 0xbc, 0xfc, 0x69, 0x39, 0xa4, 0xb3, 0x48, 0xde, 0xa2, 0xc1, 0x73, + 0x56, 0xa1, 0xe8, 0xdd, + ], + m: [ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, + ], + sig: [ + 0x9a, 0x25, 0x42, 0x9f, 0x3e, 0xfd, 0x9b, 0x2f, 0x7d, 0xe2, 0x9e, 0x45, 0x12, 0x8d, + 0xd7, 0xb7, 0x60, 0xf0, 0x50, 0x8c, 0xd9, 0x58, 0x21, 0x82, 0xab, 0xaf, 0x53, 0xdd, + 0x76, 0xc0, 0x34, 0x2c, 0xe4, 0x1b, 0x4a, 0xcf, 0x8e, 0x0a, 0x48, 0x24, 0xe4, 0x11, + 0x08, 0xc2, 0x02, 0x65, 0x73, 0x11, 0x4b, 0x60, 0xbe, 0xec, 0xb1, 0x74, 0x01, 0x2a, + 0x2b, 0xdb, 0xee, 0xcb, 0xaa, 0x00, 0xb5, 0x06, + ], + rsig: [ + 0xcf, 0xf5, 0x83, 0x57, 0x13, 0xbe, 0x07, 0xfb, 0xe1, 0x25, 0xbb, 0xf2, 0x7a, 0x63, + 0x6a, 0xdd, 0x13, 0x1c, 0x90, 0x81, 0x71, 0x6c, 0x52, 0xfd, 0xa8, 0x75, 0x42, 0x6d, + 0x03, 0x98, 0x2c, 0xd2, 0x7e, 0xbd, 0x14, 0xb4, 0x22, 0x7b, 0x83, 0x96, 0x15, 0xfd, + 0x03, 0x71, 0xbf, 0xdb, 0x8a, 0x30, 0xab, 0xdd, 0xff, 0x74, 0xd7, 0x95, 0xf3, 0xe2, + 0x7d, 0x1d, 0x47, 0xc6, 0x29, 0x46, 0x9b, 0x08, + ], + }, + TestVector { + sk: [ + 0xbc, 0x27, 0x83, 0x8d, 0xe2, 0xa6, 0x14, 0xcf, 0xba, 0x6c, 0x3e, 0x92, 0x2a, 0x8f, + 0x84, 0x24, 0xd9, 0x85, 0x6f, 0x68, 0x16, 0xf3, 0xbc, 0x61, 0x02, 0x31, 0x3b, 0x7f, + 0xaf, 0x5c, 0x3a, 0x0c, + ], + vk: [ + 0xd7, 0x9b, 0xe9, 0xff, 0x22, 0x9a, 0x2e, 0x35, 0xf5, 0xbc, 0xa4, 0x48, 0xe5, 0xeb, + 0x4a, 0x8a, 0xa9, 0x7f, 0xb4, 0x18, 0x02, 0x91, 0x25, 0xcf, 0xba, 0xa7, 0x8a, 0x91, + 0xa3, 0x82, 0xb0, 0x94, + ], + alpha: [ + 0x21, 0xa7, 0x15, 0x0e, 0x19, 0x4f, 0xed, 0xfe, 0xf9, 0x0c, 0x5d, 0x10, 0xe4, 0x20, + 0x85, 0x8b, 0xca, 0x40, 0x04, 0x04, 0x0e, 0xb6, 0x81, 0xd1, 0x4e, 0x75, 0xc4, 0x47, + 0x13, 0x51, 0xcb, 0x02, + ], + rsk: [ + 0x26, 0xa2, 0xa1, 0xc4, 0x9c, 0xe7, 0x6a, 0xfd, 0x31, 0x69, 0xd3, 0xd5, 0x7a, 0x8f, + 0xa1, 0x09, 0xa3, 0x8b, 0x3f, 0x6b, 0x23, 0x6e, 0xd7, 0x2c, 0xa8, 0xf6, 0xcb, 0x61, + 0xd8, 0xf8, 0x87, 0x00, + ], + rvk: [ + 0x54, 0xbf, 0x1b, 0xe7, 0x2e, 0x6d, 0x41, 0x20, 0x8b, 0x8a, 0xec, 0x11, 0x61, 0xd3, + 0xba, 0x59, 0x51, 0x9f, 0xb9, 0x3d, 0xa0, 0x1a, 0x55, 0xe6, 0x78, 0xe2, 0x75, 0x20, + 0x06, 0x60, 0x36, 0xc9, + ], + m: [ + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, + ], + sig: [ + 0xbb, 0xe0, 0x23, 0x59, 0x87, 0xc6, 0xe0, 0xec, 0x68, 0x6d, 0xdb, 0x8a, 0x65, 0x72, + 0x66, 0xad, 0x60, 0x5f, 0x7b, 0x75, 0x95, 0x5b, 0xb0, 0xe8, 0x02, 0xf8, 0x81, 0x64, + 0xa0, 0xff, 0xe1, 0x0c, 0x3b, 0x73, 0x85, 0x04, 0xab, 0xb3, 0xd1, 0x05, 0x62, 0xb9, + 0x27, 0xb3, 0xd2, 0x9f, 0xe9, 0xb0, 0xd3, 0x56, 0x28, 0x6a, 0xea, 0xe5, 0xa2, 0xac, + 0x9e, 0x43, 0x5f, 0x20, 0x79, 0x1a, 0xf8, 0x00, + ], + rsig: [ + 0x6d, 0xe3, 0x2b, 0x54, 0x15, 0xd7, 0x7a, 0x90, 0x5f, 0x09, 0x03, 0x90, 0x2a, 0x11, + 0x7e, 0xda, 0x79, 0x3c, 0x70, 0x8e, 0x23, 0xa5, 0x42, 0x45, 0xba, 0x8a, 0x8d, 0x1f, + 0xe0, 0x26, 0x75, 0x23, 0x23, 0x15, 0x65, 0xe0, 0x57, 0x09, 0xae, 0xd9, 0x6c, 0x22, + 0x1f, 0xb1, 0xf3, 0xd0, 0x42, 0x04, 0x35, 0x03, 0xff, 0x33, 0x85, 0x85, 0xa9, 0xbb, + 0x98, 0x9c, 0x9d, 0xd4, 0x30, 0xd6, 0xd6, 0x0b, + ], + }, + TestVector { + sk: [ + 0xb2, 0x08, 0x59, 0xb8, 0x8e, 0xe3, 0x33, 0x8a, 0x64, 0x95, 0x4f, 0x8a, 0x9e, 0x8e, + 0x9b, 0xf3, 0xe7, 0x11, 0x5a, 0xcf, 0x7c, 0x6e, 0x7f, 0x01, 0x43, 0x2c, 0x5f, 0x76, + 0x96, 0xd2, 0xd0, 0x05, + ], + vk: [ + 0xa8, 0x1f, 0xe6, 0x84, 0x6d, 0xbe, 0x0a, 0x75, 0xc0, 0xf4, 0x9b, 0x21, 0x32, 0x32, + 0xbe, 0xad, 0xd1, 0xf9, 0xa5, 0x64, 0x67, 0x3d, 0x25, 0xb9, 0x1e, 0xe0, 0xf1, 0x7c, + 0xe9, 0xca, 0xa3, 0x63, + ], + alpha: [ + 0x44, 0xd9, 0x08, 0xe1, 0xc1, 0x5e, 0x6b, 0xd9, 0x38, 0x0a, 0x8b, 0x23, 0x5a, 0xce, + 0x02, 0xfa, 0xc1, 0xc0, 0x87, 0x94, 0x45, 0x4b, 0xcd, 0xb4, 0xa6, 0xf4, 0x8c, 0xea, + 0x78, 0xa7, 0x4a, 0x04, + ], + rsk: [ + 0xf6, 0xe1, 0x61, 0x99, 0x50, 0x42, 0x9f, 0x63, 0x9d, 0x9f, 0xda, 0xad, 0xf8, 0x5c, + 0x9e, 0xed, 0xa9, 0xd2, 0xe1, 0x63, 0xc2, 0xb9, 0x4c, 0xb6, 0xe9, 0x20, 0xec, 0x60, + 0x0f, 0x7a, 0x1b, 0x0a, + ], + rvk: [ + 0x0b, 0x68, 0xd5, 0x0f, 0x91, 0x3c, 0xd1, 0xb7, 0x8b, 0x59, 0x92, 0x1e, 0x16, 0x56, + 0xd5, 0x76, 0xb0, 0xeb, 0x17, 0x1e, 0xd3, 0x87, 0x0d, 0x39, 0xfe, 0xc6, 0x94, 0x41, + 0xb3, 0x4b, 0x25, 0x38, + ], + m: [ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, + ], + sig: [ + 0x44, 0x6d, 0x67, 0x7c, 0x4c, 0xfe, 0xfd, 0x02, 0x4b, 0x0a, 0xeb, 0x37, 0xa5, 0x98, + 0xcc, 0x2e, 0xb3, 0xd2, 0x9b, 0x02, 0x94, 0xfe, 0x5b, 0xb6, 0x97, 0x8e, 0x8b, 0x43, + 0xd3, 0x2b, 0x2e, 0x4f, 0x09, 0x56, 0xac, 0xd1, 0x3e, 0x7e, 0x3a, 0x63, 0xa1, 0x8f, + 0xca, 0x32, 0xd6, 0xab, 0x94, 0xb9, 0x4e, 0xd0, 0x33, 0xe9, 0xa1, 0x0f, 0xc5, 0x69, + 0x28, 0xbc, 0x8a, 0x0f, 0x4f, 0x8e, 0x95, 0x00, + ], + rsig: [ + 0x8d, 0xe0, 0x41, 0xe7, 0x09, 0xdb, 0x62, 0x4a, 0xe2, 0xbe, 0x16, 0x48, 0xb6, 0x62, + 0x23, 0x9c, 0xde, 0xdf, 0x85, 0xec, 0xd3, 0x82, 0x26, 0x8b, 0x0e, 0x35, 0x54, 0xbf, + 0xa0, 0xf2, 0x08, 0x1c, 0xd6, 0x41, 0xbc, 0xa0, 0x40, 0x78, 0xaa, 0x89, 0xf7, 0xdd, + 0x25, 0x40, 0x58, 0x7c, 0xed, 0x6b, 0x45, 0x89, 0x16, 0xb1, 0x3e, 0x4b, 0x6a, 0x36, + 0x30, 0xda, 0x69, 0x76, 0x46, 0xdb, 0xbf, 0x09, + ], + }, + TestVector { + sk: [ + 0x32, 0x16, 0xae, 0x47, 0xe9, 0xf5, 0x3e, 0x8a, 0x52, 0x79, 0x6f, 0x24, 0xb6, 0x24, + 0x60, 0x77, 0x6b, 0xd5, 0xf2, 0x05, 0xa7, 0x8e, 0x15, 0x95, 0xbc, 0x8e, 0xfe, 0xdc, + 0x51, 0x9d, 0x36, 0x0b, + ], + vk: [ + 0xdf, 0x74, 0xbf, 0x04, 0x79, 0x61, 0xcc, 0x5c, 0xda, 0xc8, 0x28, 0x90, 0xc7, 0x6e, + 0xc6, 0x75, 0xbd, 0x4e, 0x89, 0xea, 0xd2, 0x80, 0xc9, 0x52, 0xd7, 0xc3, 0x3e, 0xea, + 0xf2, 0xb5, 0xa6, 0x6b, + ], + alpha: [ + 0xc9, 0x61, 0xf2, 0xdd, 0x93, 0x68, 0x2a, 0xdb, 0x93, 0xf5, 0xc0, 0x5a, 0x73, 0xfd, + 0xbc, 0x6d, 0x43, 0xc7, 0x0e, 0x1b, 0x15, 0xe8, 0xd5, 0x3e, 0x3f, 0x17, 0xa8, 0x24, + 0x94, 0xe3, 0xf2, 0x09, + ], + rsk: [ + 0x44, 0x4b, 0xa9, 0x4e, 0x1e, 0x50, 0xd2, 0x94, 0x63, 0x5e, 0x68, 0xb2, 0x95, 0x01, + 0xb5, 0x3e, 0xae, 0x61, 0xcd, 0x1f, 0xbb, 0x3b, 0x84, 0xcd, 0x52, 0xf6, 0x72, 0x9c, + 0xfb, 0xcb, 0xab, 0x06, + ], + rvk: [ + 0x0a, 0xfb, 0xe4, 0x06, 0xa8, 0x91, 0xc3, 0xb8, 0xc3, 0x10, 0xc2, 0x15, 0xbc, 0x68, + 0xa9, 0x13, 0xde, 0x7c, 0xda, 0x06, 0xaf, 0x29, 0x42, 0x00, 0x56, 0x46, 0x8d, 0x0c, + 0x08, 0x85, 0x5b, 0x28, + ], + m: [ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, + ], + sig: [ + 0x99, 0x35, 0x80, 0xef, 0x93, 0x34, 0x9a, 0x1c, 0x9e, 0xe9, 0x60, 0xca, 0x3e, 0x7c, + 0xd0, 0x4c, 0x13, 0xb4, 0xa0, 0xec, 0x4f, 0xd1, 0x80, 0x53, 0xa1, 0x9c, 0xff, 0x77, + 0x63, 0x62, 0x09, 0x65, 0xfb, 0xee, 0x96, 0xc1, 0x64, 0x72, 0x30, 0xe3, 0x73, 0xcb, + 0x82, 0xb8, 0x1d, 0x00, 0x03, 0x92, 0x23, 0xd3, 0x0b, 0x39, 0x3e, 0xd1, 0x72, 0xc9, + 0xb3, 0xc5, 0x63, 0xc6, 0x11, 0x79, 0x22, 0x05, + ], + rsig: [ + 0xcc, 0x7a, 0xae, 0x1c, 0xed, 0xad, 0x2d, 0x7f, 0x6c, 0xe0, 0x4c, 0x19, 0xc5, 0xa5, + 0xb6, 0xb7, 0xa6, 0xa0, 0x82, 0x78, 0x5c, 0x54, 0x0c, 0x14, 0xf6, 0x30, 0x9b, 0x06, + 0x4d, 0x1f, 0xfa, 0x68, 0x17, 0x29, 0x53, 0xfb, 0xa0, 0xc2, 0xfc, 0xfb, 0x87, 0x5c, + 0xa7, 0xf7, 0xea, 0x98, 0xef, 0x55, 0xa0, 0x40, 0x2f, 0xd5, 0x29, 0xcf, 0xcd, 0xdf, + 0x99, 0x6c, 0xa2, 0xb8, 0xca, 0x89, 0x90, 0x0a, + ], + }, + TestVector { + sk: [ + 0x85, 0x83, 0x6f, 0x98, 0x32, 0xb2, 0x8d, 0xe7, 0xc6, 0x36, 0x13, 0xe2, 0xa6, 0xed, + 0x36, 0xfb, 0x1a, 0xb4, 0x4f, 0xb0, 0xc1, 0x3f, 0xa8, 0x79, 0x8c, 0xd9, 0xcd, 0x30, + 0x30, 0xd4, 0x55, 0x03, + ], + vk: [ + 0xbf, 0xd5, 0xbc, 0x00, 0xc7, 0xc0, 0x22, 0xaa, 0x89, 0x01, 0xae, 0x08, 0x3c, 0x12, + 0xd5, 0x4b, 0x82, 0xf0, 0xdd, 0xff, 0x8e, 0xd6, 0xdb, 0x9a, 0x12, 0xd5, 0x9a, 0x5e, + 0xf6, 0xa5, 0xa2, 0xe0, + ], + alpha: [ + 0xa2, 0xe8, 0xb9, 0xe1, 0x6d, 0x6f, 0xf3, 0xca, 0x6c, 0x53, 0xd4, 0xe8, 0x8a, 0xbb, + 0xb9, 0x9b, 0xe7, 0xaf, 0x7e, 0x36, 0x59, 0x63, 0x1f, 0x1e, 0xae, 0x1e, 0xff, 0x23, + 0x87, 0x4d, 0x8e, 0x0c, + ], + rsk: [ + 0x70, 0x3f, 0x32, 0xa3, 0x41, 0x13, 0xea, 0xe1, 0xb0, 0x79, 0x1f, 0xfe, 0x9d, 0x88, + 0x88, 0xf0, 0x01, 0x29, 0x9a, 0xe5, 0x19, 0x68, 0x60, 0x91, 0x91, 0x48, 0x99, 0xef, + 0xcc, 0x6c, 0x66, 0x01, + ], + rvk: [ + 0xeb, 0x92, 0x97, 0x03, 0x6c, 0xf5, 0x17, 0xe1, 0x5e, 0x9e, 0xfe, 0x39, 0x75, 0x32, + 0x8d, 0xb4, 0x8e, 0xe7, 0xc2, 0x69, 0x4e, 0x94, 0x6d, 0xb2, 0x5f, 0x52, 0x87, 0x88, + 0xf6, 0xa1, 0xdb, 0x14, + ], + m: [ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, + ], + sig: [ + 0xce, 0x90, 0xdd, 0xf4, 0xaf, 0x21, 0xaa, 0xc4, 0xd9, 0x41, 0x93, 0xea, 0x16, 0xff, + 0x35, 0xcd, 0x93, 0x79, 0x20, 0x4e, 0x7d, 0x8f, 0xf4, 0xc0, 0xf5, 0x41, 0x17, 0xab, + 0xb1, 0x6b, 0x7c, 0x85, 0xa0, 0xb1, 0x97, 0xcf, 0x13, 0xab, 0x14, 0xd7, 0xc3, 0xba, + 0x68, 0x01, 0x0a, 0xb8, 0x05, 0x12, 0x25, 0x91, 0x3b, 0xdb, 0xc3, 0x9a, 0x51, 0xf6, + 0x03, 0x7a, 0xfc, 0x6c, 0xee, 0xcb, 0x0b, 0x06, + ], + rsig: [ + 0xa8, 0x47, 0x74, 0x2e, 0x94, 0x01, 0xcf, 0x22, 0x39, 0x21, 0x3d, 0xc8, 0x81, 0x3e, + 0x97, 0x72, 0xe9, 0x7a, 0xf8, 0xd6, 0x7a, 0xdf, 0xfe, 0xab, 0xc8, 0xe6, 0x7f, 0x5d, + 0x2d, 0x90, 0xd0, 0xb4, 0x1b, 0xc2, 0x5b, 0x05, 0xf9, 0x4a, 0xce, 0x16, 0x8a, 0xec, + 0xc6, 0x58, 0x3e, 0x18, 0xf7, 0x63, 0x74, 0x92, 0xf3, 0x7a, 0x9c, 0xa3, 0x00, 0x20, + 0x2b, 0xc0, 0x65, 0xab, 0xd3, 0x80, 0xec, 0x00, + ], + }, + ] +} diff --git a/zcash_primitives/src/sapling/value.rs b/zcash_primitives/src/sapling/value.rs index 7c61fe21f3..16bab4aa54 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 46cc92b05a..69bfc92379 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 68b3bf1ab6..e03f07d7f0 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 2cf8a5ed6a..950be192b5 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 ce535852a1..79af455b1a 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/sapling/zip32.rs b/zcash_primitives/src/sapling/zip32.rs index 4971ef0df4..edcb25678b 100644 --- a/zcash_primitives/src/sapling/zip32.rs +++ b/zcash_primitives/src/sapling/zip32.rs @@ -16,7 +16,10 @@ use crate::{ keys::{prf_expand, prf_expand_vec}, sapling::{ constants::PROOF_GENERATION_KEY_GENERATOR, - keys::{DecodingError, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey}, + keys::{ + DecodingError, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey, + SpendAuthorizingKey, + }, SaplingIvk, }, zip32::{ChainCode, ChildIndex, DiversifierIndex, Scope}, @@ -98,7 +101,7 @@ pub fn sapling_derive_internal_fvk( ( FullViewingKey { vk: ViewingKey { - ak: fvk.vk.ak, + ak: fvk.vk.ak.clone(), nk: nk_internal, }, ovk: ovk_internal, @@ -408,6 +411,12 @@ impl ExtendedSpendingKey { xsk } + /// Derives the child key at the given (hardened) index. + /// + /// # Panics + /// + /// Panics if the child key has `ask = 0`. This has a negligible probability of + /// occurring. #[must_use] pub fn derive_child(&self, i: ChildIndex) -> Self { let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); @@ -431,10 +440,15 @@ impl ExtendedSpendingKey { expsk: { let mut ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array()); let mut nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array()); - ask.add_assign(&self.expsk.ask); + ask.add_assign(self.expsk.ask.to_scalar()); nsk.add_assign(&self.expsk.nsk); let ovk = derive_child_ovk(&self.expsk.ovk, i_l); - ExpandedSpendingKey { ask, nsk, ovk } + ExpandedSpendingKey { + ask: SpendAuthorizingKey::from_scalar(ask) + .expect("negligible chance of ask == 0"), + nsk, + ovk, + } }, dk: self.dk.derive_child(i_l), } @@ -474,7 +488,7 @@ impl ExtendedSpendingKey { child_index: self.child_index, chain_code: self.chain_code, expsk: ExpandedSpendingKey { - ask: self.expsk.ask, + ask: self.expsk.ask.clone(), nsk: nsk_internal, ovk: ovk_internal, }, @@ -1611,7 +1625,7 @@ mod tests { let xsks = [m, m_1h, m_1h_2h, m_1h_2h_3h]; for (xsk, tv) in xsks.iter().zip(test_vectors.iter()) { - assert_eq!(xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap()); + assert_eq!(xsk.expsk.ask.to_bytes(), tv.ask.unwrap()); assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap()); assert_eq!(xsk.expsk.ovk.0, tv.ovk); @@ -1623,7 +1637,7 @@ mod tests { assert_eq!(&ser[..], &tv.xsk.unwrap()[..]); let internal_xsk = xsk.derive_internal(); - assert_eq!(internal_xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap()); + assert_eq!(internal_xsk.expsk.ask.to_bytes(), tv.ask.unwrap()); assert_eq!( internal_xsk.expsk.nsk.to_repr().as_ref(), tv.internal_nsk.unwrap() diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 9ef6cd00c7..504ee2fdea 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -15,7 +15,7 @@ use crate::{ self, builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata}, prover::{OutputProver, SpendProver}, - redjubjub, Note, PaymentAddress, + Note, PaymentAddress, }, transaction::{ components::{ @@ -162,7 +162,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 +334,7 @@ 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)); + self.sapling_asks.push(extsk.expsk.ask); Ok(()) } diff --git a/zcash_primitives/src/transaction/components/sapling.rs b/zcash_primitives/src/transaction/components/sapling.rs index e36b811aee..a0acd4147a 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 b9ff932af3..963e03049e 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 1f1a06eb98..e50c6902f3 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 929977aa7e..ba3b45d377 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() }