diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index 79ecd0095..9484122c7 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -241,9 +241,6 @@ pub fn decode_extended_full_viewing_key( /// /// ``` /// use group::Group; -/// use jubjub::SubgroupPoint; -/// use rand_core::SeedableRng; -/// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ /// encoding::encode_payment_address, /// }; @@ -252,15 +249,12 @@ pub fn decode_extended_full_viewing_key( /// sapling::{Diversifier, PaymentAddress}, /// }; /// -/// let rng = &mut XorShiftRng::from_seed([ -/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, -/// 0xbc, 0xe5, -/// ]); -/// -/// let pa = PaymentAddress::from_parts( -/// Diversifier([0u8; 11]), -/// SubgroupPoint::random(rng), -/// ) +/// let pa = PaymentAddress::from_bytes(&[ +/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x8e, 0x11, +/// 0x9d, 0x72, 0x99, 0x2b, 0x56, 0x0d, 0x26, 0x50, 0xff, 0xe0, 0xbe, 0x7f, 0x35, 0x42, +/// 0xfd, 0x97, 0x00, 0x3c, 0xb7, 0xcc, 0x3a, 0xbf, 0xf8, 0x1a, 0x7f, 0x90, 0x37, 0xf3, +/// 0xea, +/// ]) /// .unwrap(); /// /// assert_eq!( @@ -291,9 +285,6 @@ pub fn encode_payment_address_p( /// /// ``` /// use group::Group; -/// use jubjub::SubgroupPoint; -/// use rand_core::SeedableRng; -/// use rand_xorshift::XorShiftRng; /// use zcash_client_backend::{ /// encoding::decode_payment_address, /// }; @@ -302,15 +293,12 @@ pub fn encode_payment_address_p( /// sapling::{Diversifier, PaymentAddress}, /// }; /// -/// let rng = &mut XorShiftRng::from_seed([ -/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, -/// 0xbc, 0xe5, -/// ]); -/// -/// let pa = PaymentAddress::from_parts( -/// Diversifier([0u8; 11]), -/// SubgroupPoint::random(rng), -/// ) +/// let pa = PaymentAddress::from_bytes(&[ +/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x8e, 0x11, +/// 0x9d, 0x72, 0x99, 0x2b, 0x56, 0x0d, 0x26, 0x50, 0xff, 0xe0, 0xbe, 0x7f, 0x35, 0x42, +/// 0xfd, 0x97, 0x00, 0x3c, 0xb7, 0xcc, 0x3a, 0xbf, 0xf8, 0x1a, 0x7f, 0x90, 0x37, 0xf3, +/// 0xea, +/// ]) /// .unwrap(); /// /// assert_eq!( @@ -461,14 +449,7 @@ pub fn decode_transparent_address( #[cfg(test)] mod tests { - use group::Group; - use rand_core::SeedableRng; - use rand_xorshift::XorShiftRng; - use zcash_primitives::{ - constants, - sapling::{Diversifier, PaymentAddress}, - zip32::ExtendedSpendingKey, - }; + use zcash_primitives::{constants, sapling::PaymentAddress, zip32::ExtendedSpendingKey}; use super::{ decode_extended_full_viewing_key, decode_extended_spending_key, decode_payment_address, @@ -559,14 +540,13 @@ mod tests { #[test] fn payment_address() { - let rng = &mut XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - let addr = - PaymentAddress::from_parts(Diversifier([0u8; 11]), jubjub::SubgroupPoint::random(rng)) - .unwrap(); + let addr = PaymentAddress::from_bytes(&[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x8e, 0x11, + 0x9d, 0x72, 0x99, 0x2b, 0x56, 0x0d, 0x26, 0x50, 0xff, 0xe0, 0xbe, 0x7f, 0x35, 0x42, + 0xfd, 0x97, 0x00, 0x3c, 0xb7, 0xcc, 0x3a, 0xbf, 0xf8, 0x1a, 0x7f, 0x90, 0x37, 0xf3, + 0xea, + ]) + .unwrap(); let encoded_main = "zs1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75c8v35z"; diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index e74035ca6..747cfb03e 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -8,6 +8,7 @@ and this library adheres to Rust's notion of ## [Unreleased] ### Added - `zcash_primitives::sapling`: + - `keys::DiversifiedTransmissionKey` - `keys::{EphemeralSecretKey, EphemeralPublicKey, SharedSecret}` - `note`, a module containing types related to Sapling notes. The existing `Note` and `Rseed` types are re-exported here, and new types are added. @@ -27,6 +28,11 @@ and this library adheres to Rust's notion of - `zcash_primitives::sapling`: - `PaymentAddress::from_parts` now rejects invalid diversifiers. - `PaymentAddress::create_note` is now infallible. + - `DiversifiedTransmissionKey` is now used instead of `jubjub::SubgroupPoint` + in the following places: + - `PaymentAddress::from_parts` + - `PaymentAddress::pk_d` + - `note_encryption::SaplingDomain::DiversifiedTransmissionKey` - Note commitments now use `zcash_primitives::sapling::note::ExtractedNoteCommitment` instead of `bls12_381::Scalar` in the following places: @@ -65,6 +71,7 @@ and this library adheres to Rust's notion of - `NoteValue` (use `zcash_primitives::sapling::value::NoteValue` instead). - `ValueCommitment` (use `zcash_primitives::sapling::value::ValueCommitment` or `zcash_proofs::circuit::sapling::ValueCommitmentPreimage` instead). + - `note_encryption::sapling_ka_agree` - `testing::{arb_note_value, arb_positive_note_value}` (use the methods in `zcash_primitives::sapling::value::testing` instead). - `zcash_primitives::transaction::components`: diff --git a/zcash_primitives/benches/note_decryption.rs b/zcash_primitives/benches/note_decryption.rs index 7d7e2ec55..4827badce 100644 --- a/zcash_primitives/benches/note_decryption.rs +++ b/zcash_primitives/benches/note_decryption.rs @@ -14,7 +14,7 @@ use zcash_primitives::{ }, prover::mock::MockTxProver, value::NoteValue, - Diversifier, PaymentAddress, SaplingIvk, + Diversifier, SaplingIvk, }, transaction::components::sapling::{ builder::SaplingBuilder, CompactOutputDescription, GrothProofBytes, OutputDescription, @@ -34,8 +34,7 @@ fn bench_note_decryption(c: &mut Criterion) { // Construct a Sapling output. let output: OutputDescription = { let diversifier = Diversifier([0; 11]); - let pk_d = diversifier.g_d().unwrap() * valid_ivk.0; - let pa = PaymentAddress::from_parts(diversifier, pk_d).unwrap(); + let pa = valid_ivk.to_payment_address(diversifier).unwrap(); let mut builder = SaplingBuilder::new(TEST_NETWORK, height); builder diff --git a/zcash_primitives/src/sapling/address.rs b/zcash_primitives/src/sapling/address.rs index 573ee49d3..55a457a06 100644 --- a/zcash_primitives/src/sapling/address.rs +++ b/zcash_primitives/src/sapling/address.rs @@ -1,7 +1,5 @@ -use group::{Group, GroupEncoding}; - use super::{ - keys::Diversifier, + keys::{DiversifiedTransmissionKey, Diversifier}, note::{Note, Rseed}, value::NoteValue, }; @@ -15,7 +13,7 @@ use super::{ /// and not the identity). #[derive(Clone, Copy, Debug)] pub struct PaymentAddress { - pk_d: jubjub::SubgroupPoint, + pk_d: DiversifiedTransmissionKey, diversifier: Diversifier, } @@ -34,30 +32,17 @@ impl PaymentAddress { /// Note that we cannot verify in this constructor that `pk_d` is derived from /// `diversifier`, so addresses for which these values have no known relationship /// (and therefore no-one can receive funds at them) can still be constructed. - pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option { + pub fn from_parts(diversifier: Diversifier, pk_d: DiversifiedTransmissionKey) -> Option { // Check that the diversifier is valid diversifier.g_d()?; - if pk_d.is_identity().into() { + if pk_d.is_identity() { None } else { Some(PaymentAddress { pk_d, diversifier }) } } - /// Constructs a PaymentAddress from a diversifier and a Jubjub point. - /// - /// Only for test code, as this explicitly bypasses the invariant. [`Self::g_d`] will - /// panic on a `PaymentAddress` that has been constructed by passing an invalid - /// diversifier to this method. - #[cfg(test)] - pub(crate) fn from_parts_unchecked( - diversifier: Diversifier, - pk_d: jubjub::SubgroupPoint, - ) -> Self { - PaymentAddress { pk_d, diversifier } - } - /// Parses a PaymentAddress from bytes. pub fn from_bytes(bytes: &[u8; 43]) -> Option { let diversifier = { @@ -66,7 +51,7 @@ impl PaymentAddress { Diversifier(tmp) }; - let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap()); + let pk_d = DiversifiedTransmissionKey::from_bytes(bytes[11..43].try_into().unwrap()); if pk_d.is_some().into() { // The remaining invariants are checked here. PaymentAddress::from_parts(diversifier, pk_d.unwrap()) @@ -89,7 +74,7 @@ impl PaymentAddress { } /// Returns `pk_d` for this `PaymentAddress`. - pub fn pk_d(&self) -> &jubjub::SubgroupPoint { + pub fn pk_d(&self) -> &DiversifiedTransmissionKey { &self.pk_d } diff --git a/zcash_primitives/src/sapling/keys.rs b/zcash_primitives/src/sapling/keys.rs index 51a8adba7..0d20ebf2e 100644 --- a/zcash_primitives/src/sapling/keys.rs +++ b/zcash_primitives/src/sapling/keys.rs @@ -13,7 +13,7 @@ use super::{ }, spec::{ crh_ivk, diversify_hash, ka_sapling_agree, ka_sapling_agree_prepared, - ka_sapling_derive_public, + ka_sapling_derive_public, ka_sapling_derive_public_subgroup_prepared, PreparedBaseSubgroup, }, }; use crate::{ @@ -24,7 +24,7 @@ use crate::{ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use ff::PrimeField; use group::{Curve, Group, GroupEncoding}; -use subtle::{ConstantTimeEq, CtOption}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_note_encryption::EphemeralKeyBytes; /// Errors that can occur in the decoding of Sapling spending keys. @@ -241,11 +241,9 @@ pub struct SaplingIvk(pub jubjub::Fr); impl SaplingIvk { pub fn to_payment_address(&self, diversifier: Diversifier) -> Option { - diversifier.g_d().and_then(|g_d| { - let pk_d = g_d * self.0; - - PaymentAddress::from_parts(diversifier, pk_d) - }) + let prepared_ivk = PreparedIncomingViewingKey::new(self); + DiversifiedTransmissionKey::derive(&prepared_ivk, &diversifier) + .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d)) } pub fn to_repr(&self) -> [u8; 32] { @@ -262,6 +260,62 @@ impl Diversifier { } } +/// The diversified transmission key for a given payment address. +/// +/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents]. +/// +/// Note that this type is allowed to be the identity in the protocol, but we reject this +/// in [`PaymentAddress::from_parts`]. +/// +/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct DiversifiedTransmissionKey(jubjub::SubgroupPoint); + +impl DiversifiedTransmissionKey { + /// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents]. + /// + /// Returns `None` if `d` is an invalid diversifier. + /// + /// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents + pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Option { + d.g_d() + .map(PreparedBaseSubgroup::new) + .map(|g_d| ka_sapling_derive_public_subgroup_prepared(ivk.inner(), &g_d)) + .map(DiversifiedTransmissionKey) + } + + /// $abst_J(bytes)$ + pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { + jubjub::SubgroupPoint::from_bytes(bytes).map(DiversifiedTransmissionKey) + } + + /// $repr_J(self)$ + pub(crate) fn to_bytes(self) -> [u8; 32] { + self.0.to_bytes() + } + + /// Returns true if this is the identity. + pub(crate) fn is_identity(&self) -> bool { + self.0.is_identity().into() + } + + /// Exposes the inner Jubjub point. + /// + /// This API is exposed for `zcash_proof` usage, and will be removed when this type is + /// refactored into the `sapling-crypto` crate. + pub fn inner(&self) -> jubjub::SubgroupPoint { + self.0 + } +} + +impl ConditionallySelectable for DiversifiedTransmissionKey { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + DiversifiedTransmissionKey(jubjub::SubgroupPoint::conditional_select( + &a.0, &b.0, choice, + )) + } +} + /// An ephemeral secret key used to encrypt an output note on-chain. /// /// `esk` is "ephemeral" in the sense that each secret key is only used once. In @@ -290,8 +344,8 @@ impl EphemeralSecretKey { EphemeralPublicKey(ka_sapling_derive_public(&self.0, &g_d)) } - pub(crate) fn agree(&self, pk_d: &jubjub::ExtendedPoint) -> SharedSecret { - SharedSecret(ka_sapling_agree(&self.0, pk_d)) + pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret { + SharedSecret(ka_sapling_agree(&self.0, &pk_d.0.into())) } } diff --git a/zcash_primitives/src/sapling/note_encryption.rs b/zcash_primitives/src/sapling/note_encryption.rs index c21cdfe39..8d0f4a6e7 100644 --- a/zcash_primitives/src/sapling/note_encryption.rs +++ b/zcash_primitives/src/sapling/note_encryption.rs @@ -5,7 +5,6 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, WriteBytesExt}; use ff::PrimeField; -use group::GroupEncoding; use memuse::DynamicUsage; use rand_core::RngCore; @@ -20,8 +19,11 @@ use crate::{ consensus::{self, BlockHeight, NetworkUpgrade::Canopy, ZIP212_GRACE_PERIOD}, memo::MemoBytes, sapling::{ - keys::{EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey, SharedSecret}, - spec::{PreparedBase, PreparedBaseSubgroup, PreparedScalar}, + keys::{ + DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey, + SharedSecret, + }, + spec::{PreparedBase, PreparedScalar}, value::ValueCommitment, Diversifier, Note, PaymentAddress, Rseed, SaplingIvk, }, @@ -73,13 +75,6 @@ impl PreparedEphemeralPublicKey { } } -/// Sapling key agreement for note encryption. -/// -/// Implements section 5.4.4.3 of the Zcash Protocol Specification. -pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint { - EphemeralSecretKey(*esk).agree(pk_d).into_inner() -} - /// Sapling PRF^ock. /// /// Implemented per section 5.4.2 of the Zcash Protocol Specification. @@ -111,7 +106,7 @@ fn sapling_parse_note_plaintext_without_memo( get_validated_pk_d: F, ) -> Option<(Note, PaymentAddress)> where - F: FnOnce(&Diversifier) -> Option, + F: FnOnce(&Diversifier) -> Option, { assert!(plaintext.len() >= COMPACT_NOTE_SIZE); @@ -176,7 +171,7 @@ impl Domain for SaplingDomain

{ type SymmetricKey = Blake2bHash; type Note = Note; type Recipient = PaymentAddress; - type DiversifiedTransmissionKey = jubjub::SubgroupPoint; + type DiversifiedTransmissionKey = DiversifiedTransmissionKey; type IncomingViewingKey = PreparedIncomingViewingKey; type OutgoingViewingKey = OutgoingViewingKey; type ValueCommitment = ValueCommitment; @@ -209,7 +204,7 @@ impl Domain for SaplingDomain

{ esk: &Self::EphemeralSecretKey, pk_d: &Self::DiversifiedTransmissionKey, ) -> Self::SharedSecret { - EphemeralSecretKey(*esk).agree(pk_d.into()).into_inner() + EphemeralSecretKey(*esk).agree(pk_d).into_inner() } fn ka_agree_dec( @@ -296,7 +291,7 @@ impl Domain for SaplingDomain

{ plaintext: &[u8], ) -> Option<(Self::Note, Self::Recipient)> { sapling_parse_note_plaintext_without_memo(self, plaintext, |diversifier| { - Some(&PreparedBaseSubgroup::new(diversifier.g_d()?) * &ivk.0) + DiversifiedTransmissionKey::derive(ivk, diversifier) }) } @@ -326,7 +321,7 @@ impl Domain for SaplingDomain

{ } fn extract_pk_d(op: &OutPlaintextBytes) -> Option { - jubjub::SubgroupPoint::from_bytes( + DiversifiedTransmissionKey::from_bytes( op.0[0..32].try_into().expect("slice is the correct length"), ) .into() @@ -404,15 +399,15 @@ impl BatchDomain for SaplingDomain

{ /// note_encryption::sapling_note_encryption, /// util::generate_random_rseed, /// value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, -/// Diversifier, PaymentAddress, Rseed, +/// Diversifier, PaymentAddress, Rseed, SaplingIvk, /// }, /// }; /// /// let mut rng = OsRng; /// +/// let ivk = SaplingIvk(jubjub::Scalar::random(&mut rng)); /// let diversifier = Diversifier([0; 11]); -/// let pk_d = diversifier.g_d().unwrap(); -/// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap(); +/// let to = ivk.to_payment_address(diversifier).unwrap(); /// let ovk = Some(OutgoingViewingKey([0; 32])); /// /// let value = NoteValue::from_raw(1000); @@ -549,7 +544,7 @@ mod tests { }; use ff::{Field, PrimeField}; use group::Group; - use group::{cofactor::CofactorGroup, GroupEncoding}; + use group::GroupEncoding; use rand_core::OsRng; use rand_core::{CryptoRng, RngCore}; @@ -573,7 +568,7 @@ mod tests { keys::OutgoingViewingKey, memo::MemoBytes, sapling::{ - keys::{EphemeralPublicKey, EphemeralSecretKey}, + keys::{DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey}, note::ExtractedNoteCommitment, note_encryption::PreparedIncomingViewingKey, util::generate_random_rseed, @@ -632,8 +627,7 @@ mod tests { OutputDescription, ) { let diversifier = Diversifier([0; 11]); - let pk_d = diversifier.g_d().unwrap() * ivk.0; - let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d); + let pa = ivk.to_payment_address(diversifier).unwrap(); // Construct the value commitment for the proof instance let value = NoteValue::from_raw(100); @@ -669,6 +663,40 @@ mod tests { (ovk, ock, output) } + fn reencrypt_out_ciphertext( + ovk: &OutgoingViewingKey, + cv: &ValueCommitment, + cmu: &ExtractedNoteCommitment, + ephemeral_key: &EphemeralKeyBytes, + out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE], + modify_plaintext: impl Fn(&mut [u8; OUT_PLAINTEXT_SIZE]), + ) -> [u8; OUT_CIPHERTEXT_SIZE] { + let ock = prf_ock(ovk, cv, &cmu.to_bytes(), ephemeral_key); + + let mut op = [0; OUT_PLAINTEXT_SIZE]; + op.copy_from_slice(&out_ciphertext[..OUT_PLAINTEXT_SIZE]); + + ChaCha20Poly1305::new(ock.as_ref().into()) + .decrypt_in_place_detached( + [0u8; 12][..].into(), + &[], + &mut op, + out_ciphertext[OUT_PLAINTEXT_SIZE..].into(), + ) + .unwrap(); + + modify_plaintext(&mut op); + + let tag = ChaCha20Poly1305::new(ock.as_ref().into()) + .encrypt_in_place_detached([0u8; 12][..].into(), &[], &mut op) + .unwrap(); + + let mut out_ciphertext = [0u8; OUT_CIPHERTEXT_SIZE]; + out_ciphertext[..OUT_PLAINTEXT_SIZE].copy_from_slice(&op); + out_ciphertext[OUT_PLAINTEXT_SIZE..].copy_from_slice(&tag); + out_ciphertext + } + fn reencrypt_enc_ciphertext( ovk: &OutgoingViewingKey, cv: &ValueCommitment, @@ -692,11 +720,11 @@ mod tests { ) .unwrap(); - let pk_d = jubjub::SubgroupPoint::from_bytes(&op[0..32].try_into().unwrap()).unwrap(); + let pk_d = DiversifiedTransmissionKey::from_bytes(&op[0..32].try_into().unwrap()).unwrap(); let esk = jubjub::Fr::from_repr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()).unwrap(); - let shared_secret = EphemeralSecretKey(esk).agree(&pk_d.into()); + let shared_secret = EphemeralSecretKey(esk).agree(&pk_d); let key = shared_secret.kdf_sapling(ephemeral_key); let mut plaintext = [0; NOTE_PLAINTEXT_SIZE]; @@ -1362,9 +1390,16 @@ mod tests { ]; for &height in heights.iter() { - let ivk = SaplingIvk(jubjub::Fr::zero()); - let (ovk, ock, output) = random_enc_ciphertext_with(height, &ivk, &mut rng); + let (ovk, ock, _, mut output) = random_enc_ciphertext(height, &mut rng); + *output.out_ciphertext_mut() = reencrypt_out_ciphertext( + &ovk, + output.cv(), + output.cmu(), + output.ephemeral_key(), + output.out_ciphertext(), + |pt| pt[0..32].copy_from_slice(&jubjub::ExtendedPoint::random(rng).to_bytes()), + ); assert_eq!( try_sapling_output_recovery(&TEST_NETWORK, height, &ovk, &output,), None @@ -1392,9 +1427,9 @@ mod tests { }}; } - macro_rules! read_point { + macro_rules! read_pk_d { ($field:expr) => { - jubjub::ExtendedPoint::from_bytes(&$field).unwrap() + DiversifiedTransmissionKey::from_bytes(&$field).unwrap() }; } @@ -1412,7 +1447,7 @@ mod tests { // let ivk = PreparedIncomingViewingKey::new(&SaplingIvk(read_jubjub_scalar!(tv.ivk))); - let pk_d = read_point!(tv.default_pk_d).into_subgroup().unwrap(); + let pk_d = read_pk_d!(tv.default_pk_d); let rcm = read_jubjub_scalar!(tv.rcm); let cv = read_cv!(tv.cv); let cmu = read_cmu!(tv.cmu); @@ -1423,7 +1458,7 @@ mod tests { // Test the individual components // - let shared_secret = EphemeralSecretKey(esk).agree(&pk_d.into()); + let shared_secret = EphemeralSecretKey(esk).agree(&pk_d); assert_eq!(shared_secret.to_bytes(), tv.shared_secret); let k_enc = shared_secret.kdf_sapling(&ephemeral_key); diff --git a/zcash_primitives/src/sapling/spec.rs b/zcash_primitives/src/sapling/spec.rs index dbcad98f5..0e7807326 100644 --- a/zcash_primitives/src/sapling/spec.rs +++ b/zcash_primitives/src/sapling/spec.rs @@ -98,6 +98,19 @@ pub(crate) fn ka_sapling_derive_public_prepared( b * sk } +/// This is defined implicitly by [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents] +/// which uses $KA^\mathsf{Sapling}.\mathsf{DerivePublic}$ to produce a diversified +/// transmission key with type $KA^\mathsf{Sapling}.\mathsf{PublicPrimeSubgroup}$. +/// +/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents +pub(crate) fn ka_sapling_derive_public_subgroup_prepared( + sk: &PreparedScalar, + b: &PreparedBaseSubgroup, +) -> jubjub::SubgroupPoint { + // [sk] b + b * sk +} + /// Defined in [Zcash Protocol Spec § 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement]. /// /// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement diff --git a/zcash_primitives/src/transaction/components/sapling/builder.rs b/zcash_primitives/src/transaction/components/sapling/builder.rs index 60d8450c2..af7f6c3ed 100644 --- a/zcash_primitives/src/transaction/components/sapling/builder.rs +++ b/zcash_primitives/src/transaction/components/sapling/builder.rs @@ -13,7 +13,7 @@ use crate::{ memo::MemoBytes, merkle_tree::MerklePath, sapling::{ - keys::EphemeralSecretKey, + keys::{EphemeralSecretKey, SaplingIvk}, note_encryption::sapling_note_encryption, prover::TxProver, redjubjub::{PrivateKey, Signature}, @@ -454,26 +454,15 @@ impl SaplingBuilder

{ } else { // This is a dummy output let dummy_note = { - let (diversifier, g_d) = { - let mut diversifier; - let g_d; + let payment_address = { + let mut diversifier = Diversifier([0; 11]); loop { - let mut d = [0; 11]; - rng.fill_bytes(&mut d); - diversifier = Diversifier(d); - if let Some(val) = diversifier.g_d() { - g_d = val; - break; + rng.fill_bytes(&mut diversifier.0); + let dummy_ivk = SaplingIvk(jubjub::Fr::random(&mut rng)); + if let Some(addr) = dummy_ivk.to_payment_address(diversifier) { + break addr; } } - (diversifier, g_d) - }; - let payment_address = loop { - let dummy_ivk = jubjub::Fr::random(&mut rng); - let pk_d = g_d * dummy_ivk; - if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d) { - break addr; - } }; let rseed = diff --git a/zcash_proofs/src/circuit/sapling.rs b/zcash_proofs/src/circuit/sapling.rs index ee9502d7c..5107918f7 100644 --- a/zcash_proofs/src/circuit/sapling.rs +++ b/zcash_proofs/src/circuit/sapling.rs @@ -476,7 +476,7 @@ impl Circuit for Output { let pk_d = self .payment_address .as_ref() - .map(|e| jubjub::ExtendedPoint::from(*e.pk_d()).to_affine()); + .map(|e| jubjub::ExtendedPoint::from(e.pk_d().inner()).to_affine()); // Witness the v-coordinate, encoded as little // endian bits (to match the representation)