diff --git a/Cargo.toml b/Cargo.toml index 0d23a678f..05ecd466b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ codegen-units = 1 [patch.crates-io] # In development. -orchard = { git = "https://github.com/zcash/orchard.git", rev = "2c8241f25b943aa05203eacf9905db117c69bd29" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "8c018eff7e795b16fc68aed22d0fd4eebe2710ec" } incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" } zcash_encoding = { path = "components/zcash_encoding" } zcash_note_encryption = { path = "components/zcash_note_encryption" } diff --git a/components/zcash_note_encryption/src/batch.rs b/components/zcash_note_encryption/src/batch.rs index 40e81d94c..cb5719ab2 100644 --- a/components/zcash_note_encryption/src/batch.rs +++ b/components/zcash_note_encryption/src/batch.rs @@ -3,7 +3,7 @@ use std::iter; use crate::{ - try_compact_note_decryption_inner, try_note_decryption_inner, Domain, EphemeralKeyBytes, + try_compact_note_decryption_inner, try_note_decryption_inner, BatchDomain, EphemeralKeyBytes, ShieldedOutput, }; @@ -11,7 +11,7 @@ use crate::{ /// /// This is the batched version of [`crate::try_note_decryption`]. #[allow(clippy::type_complexity)] -pub fn try_note_decryption>( +pub fn try_note_decryption>( ivks: &[D::IncomingViewingKey], outputs: &[(D, Output)], ) -> Vec> { @@ -21,14 +21,14 @@ pub fn try_note_decryption>( /// Trial decryption of a batch of notes for light clients with a set of recipients. /// /// This is the batched version of [`crate::try_compact_note_decryption`]. -pub fn try_compact_note_decryption>( +pub fn try_compact_note_decryption>( ivks: &[D::IncomingViewingKey], outputs: &[(D, Output)], ) -> Vec> { batch_note_decryption(ivks, outputs, try_compact_note_decryption_inner) } -fn batch_note_decryption, F, FR>( +fn batch_note_decryption, F, FR>( ivks: &[D::IncomingViewingKey], outputs: &[(D, Output)], decrypt_inner: F, diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index 439e5185a..fcbaee7eb 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -116,19 +116,6 @@ pub trait Domain { fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey; - /// Computes `Self::kdf` on a batch of items. - /// - /// For each item in the batch, if the shared secret is `None`, this returns `None` at - /// that position. - fn batch_kdf<'a>( - items: impl Iterator, &'a EphemeralKeyBytes)>, - ) -> Vec> { - // Default implementation: do the non-batched thing. - items - .map(|(secret, ephemeral_key)| secret.map(|secret| Self::kdf(secret, ephemeral_key))) - .collect() - } - // for right now, we just need `recipient` to get `d`; in the future when we // can get that from a Sapling note, the recipient parameter will be able // to be removed. @@ -154,22 +141,6 @@ pub trait Domain { fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option; - /// Computes `Self::epk` on a batch of ephemeral keys. - /// - /// This is useful for protocols where the underlying curve requires an inversion to - /// parse an encoded point. - /// - /// For usability, this returns tuples of the ephemeral keys and the result of parsing - /// them. - fn batch_epk( - ephemeral_keys: impl Iterator, - ) -> Vec<(Option, EphemeralKeyBytes)> { - // Default implementation: do the non-batched thing. - ephemeral_keys - .map(|ephemeral_key| (Self::epk(&ephemeral_key), ephemeral_key)) - .collect() - } - fn check_epk_bytes NoteValidity>( note: &Self::Note, check: F, @@ -203,6 +174,37 @@ pub trait Domain { fn extract_esk(out_plaintext: &[u8; OUT_PLAINTEXT_SIZE]) -> Option; } +pub trait BatchDomain: Domain { + /// Computes `Self::kdf` on a batch of items. + /// + /// For each item in the batch, if the shared secret is `None`, this returns `None` at + /// that position. + fn batch_kdf<'a>( + items: impl Iterator, &'a EphemeralKeyBytes)>, + ) -> Vec> { + // Default implementation: do the non-batched thing. + items + .map(|(secret, ephemeral_key)| secret.map(|secret| Self::kdf(secret, ephemeral_key))) + .collect() + } + + /// Computes `Self::epk` on a batch of ephemeral keys. + /// + /// This is useful for protocols where the underlying curve requires an inversion to + /// parse an encoded point. + /// + /// For usability, this returns tuples of the ephemeral keys and the result of parsing + /// them. + fn batch_epk( + ephemeral_keys: impl Iterator, + ) -> Vec<(Option, EphemeralKeyBytes)> { + // Default implementation: do the non-batched thing. + ephemeral_keys + .map(|ephemeral_key| (Self::epk(&ephemeral_key), ephemeral_key)) + .collect() + } +} + pub trait ShieldedOutput { fn ephemeral_key(&self) -> EphemeralKeyBytes; fn cmstar_bytes(&self) -> D::ExtractedCommitmentBytes; diff --git a/zcash_primitives/src/sapling/note_encryption.rs b/zcash_primitives/src/sapling/note_encryption.rs index a1de00bf8..e56be70b2 100644 --- a/zcash_primitives/src/sapling/note_encryption.rs +++ b/zcash_primitives/src/sapling/note_encryption.rs @@ -9,9 +9,9 @@ use std::convert::TryInto; use zcash_note_encryption::{ try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ock, - try_output_recovery_with_ovk, Domain, EphemeralKeyBytes, NoteEncryption, NotePlaintextBytes, - NoteValidity, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, COMPACT_NOTE_SIZE, - NOTE_PLAINTEXT_SIZE, OUT_PLAINTEXT_SIZE, + try_output_recovery_with_ovk, BatchDomain, Domain, EphemeralKeyBytes, NoteEncryption, + NotePlaintextBytes, NoteValidity, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, + COMPACT_NOTE_SIZE, NOTE_PLAINTEXT_SIZE, OUT_PLAINTEXT_SIZE, }; use crate::{ @@ -185,37 +185,6 @@ impl Domain for SaplingDomain

{ kdf_sapling(dhsecret, epk) } - fn batch_kdf<'a>( - items: impl Iterator, &'a EphemeralKeyBytes)>, - ) -> Vec> { - let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip(); - - let secrets: Vec<_> = shared_secrets - .iter() - .filter_map(|s| s.map(ExtendedPoint::from)) - .collect(); - let mut secrets_affine = vec![AffinePoint::identity(); shared_secrets.len()]; - group::Curve::batch_normalize(&secrets, &mut secrets_affine); - - let mut secrets_affine = secrets_affine.into_iter(); - shared_secrets - .into_iter() - .map(|s| s.and_then(|_| secrets_affine.next())) - .zip(ephemeral_keys.into_iter()) - .map(|(secret, ephemeral_key)| { - secret.map(|dhsecret| { - Blake2bParams::new() - .hash_length(32) - .personal(KDF_SAPLING_PERSONALIZATION) - .to_state() - .update(&dhsecret.to_bytes()) - .update(ephemeral_key.as_ref()) - .finalize() - }) - }) - .collect() - } - fn note_plaintext_bytes( note: &Self::Note, to: &Self::Recipient, @@ -278,19 +247,6 @@ impl Domain for SaplingDomain

{ jubjub::ExtendedPoint::from_bytes(&ephemeral_key.0).into() } - fn batch_epk( - ephemeral_keys: impl Iterator, - ) -> Vec<(Option, EphemeralKeyBytes)> { - let ephemeral_keys: Vec<_> = ephemeral_keys.collect(); - let epks = jubjub::AffinePoint::batch_from_bytes(ephemeral_keys.iter().map(|b| b.0)); - epks.into_iter() - .zip(ephemeral_keys.into_iter()) - .map(|(epk, ephemeral_key)| { - (epk.map(jubjub::ExtendedPoint::from).into(), ephemeral_key) - }) - .collect() - } - fn check_epk_bytes NoteValidity>( note: &Note, check: F, @@ -359,6 +315,52 @@ impl Domain for SaplingDomain

{ } } +impl BatchDomain for SaplingDomain

{ + fn batch_kdf<'a>( + items: impl Iterator, &'a EphemeralKeyBytes)>, + ) -> Vec> { + let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip(); + + let secrets: Vec<_> = shared_secrets + .iter() + .filter_map(|s| s.map(ExtendedPoint::from)) + .collect(); + let mut secrets_affine = vec![AffinePoint::identity(); shared_secrets.len()]; + group::Curve::batch_normalize(&secrets, &mut secrets_affine); + + let mut secrets_affine = secrets_affine.into_iter(); + shared_secrets + .into_iter() + .map(|s| s.and_then(|_| secrets_affine.next())) + .zip(ephemeral_keys.into_iter()) + .map(|(secret, ephemeral_key)| { + secret.map(|dhsecret| { + Blake2bParams::new() + .hash_length(32) + .personal(KDF_SAPLING_PERSONALIZATION) + .to_state() + .update(&dhsecret.to_bytes()) + .update(ephemeral_key.as_ref()) + .finalize() + }) + }) + .collect() + } + + fn batch_epk( + ephemeral_keys: impl Iterator, + ) -> Vec<(Option, EphemeralKeyBytes)> { + let ephemeral_keys: Vec<_> = ephemeral_keys.collect(); + let epks = jubjub::AffinePoint::batch_from_bytes(ephemeral_keys.iter().map(|b| b.0)); + epks.into_iter() + .zip(ephemeral_keys.into_iter()) + .map(|(epk, ephemeral_key)| { + (epk.map(jubjub::ExtendedPoint::from).into(), ephemeral_key) + }) + .collect() + } +} + /// Creates a new encryption context for the given note. /// /// Setting `ovk` to `None` represents the `ovk = ⊥` case, where the note cannot be