Merge pull request #452 from zcash/zcash_note_encryption-batchdomain
Introduce `zcash_note_encryption::BatchDomain` trait
This commit is contained in:
commit
c48bb4def2
|
@ -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" }
|
||||
|
|
|
@ -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<D: Domain, Output: ShieldedOutput<D>>(
|
||||
pub fn try_note_decryption<D: BatchDomain, Output: ShieldedOutput<D>>(
|
||||
ivks: &[D::IncomingViewingKey],
|
||||
outputs: &[(D, Output)],
|
||||
) -> Vec<Option<(D::Note, D::Recipient, D::Memo)>> {
|
||||
|
@ -21,14 +21,14 @@ pub fn try_note_decryption<D: Domain, Output: ShieldedOutput<D>>(
|
|||
/// 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<D: Domain, Output: ShieldedOutput<D>>(
|
||||
pub fn try_compact_note_decryption<D: BatchDomain, Output: ShieldedOutput<D>>(
|
||||
ivks: &[D::IncomingViewingKey],
|
||||
outputs: &[(D, Output)],
|
||||
) -> Vec<Option<(D::Note, D::Recipient)>> {
|
||||
batch_note_decryption(ivks, outputs, try_compact_note_decryption_inner)
|
||||
}
|
||||
|
||||
fn batch_note_decryption<D: Domain, Output: ShieldedOutput<D>, F, FR>(
|
||||
fn batch_note_decryption<D: BatchDomain, Output: ShieldedOutput<D>, F, FR>(
|
||||
ivks: &[D::IncomingViewingKey],
|
||||
outputs: &[(D, Output)],
|
||||
decrypt_inner: F,
|
||||
|
|
|
@ -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<Item = (Option<Self::SharedSecret>, &'a EphemeralKeyBytes)>,
|
||||
) -> Vec<Option<Self::SymmetricKey>> {
|
||||
// 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<Self::EphemeralPublicKey>;
|
||||
|
||||
/// 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<Item = EphemeralKeyBytes>,
|
||||
) -> Vec<(Option<Self::EphemeralPublicKey>, EphemeralKeyBytes)> {
|
||||
// Default implementation: do the non-batched thing.
|
||||
ephemeral_keys
|
||||
.map(|ephemeral_key| (Self::epk(&ephemeral_key), ephemeral_key))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn check_epk_bytes<F: Fn(&Self::EphemeralSecretKey) -> NoteValidity>(
|
||||
note: &Self::Note,
|
||||
check: F,
|
||||
|
@ -203,6 +174,37 @@ pub trait Domain {
|
|||
fn extract_esk(out_plaintext: &[u8; OUT_PLAINTEXT_SIZE]) -> Option<Self::EphemeralSecretKey>;
|
||||
}
|
||||
|
||||
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<Item = (Option<Self::SharedSecret>, &'a EphemeralKeyBytes)>,
|
||||
) -> Vec<Option<Self::SymmetricKey>> {
|
||||
// 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<Item = EphemeralKeyBytes>,
|
||||
) -> Vec<(Option<Self::EphemeralPublicKey>, EphemeralKeyBytes)> {
|
||||
// Default implementation: do the non-batched thing.
|
||||
ephemeral_keys
|
||||
.map(|ephemeral_key| (Self::epk(&ephemeral_key), ephemeral_key))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ShieldedOutput<D: Domain> {
|
||||
fn ephemeral_key(&self) -> EphemeralKeyBytes;
|
||||
fn cmstar_bytes(&self) -> D::ExtractedCommitmentBytes;
|
||||
|
|
|
@ -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<P: consensus::Parameters> Domain for SaplingDomain<P> {
|
|||
kdf_sapling(dhsecret, epk)
|
||||
}
|
||||
|
||||
fn batch_kdf<'a>(
|
||||
items: impl Iterator<Item = (Option<Self::SharedSecret>, &'a EphemeralKeyBytes)>,
|
||||
) -> Vec<Option<Self::SymmetricKey>> {
|
||||
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<P: consensus::Parameters> Domain for SaplingDomain<P> {
|
|||
jubjub::ExtendedPoint::from_bytes(&ephemeral_key.0).into()
|
||||
}
|
||||
|
||||
fn batch_epk(
|
||||
ephemeral_keys: impl Iterator<Item = EphemeralKeyBytes>,
|
||||
) -> Vec<(Option<Self::EphemeralPublicKey>, 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<F: FnOnce(&Self::EphemeralSecretKey) -> NoteValidity>(
|
||||
note: &Note,
|
||||
check: F,
|
||||
|
@ -359,6 +315,52 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters> BatchDomain for SaplingDomain<P> {
|
||||
fn batch_kdf<'a>(
|
||||
items: impl Iterator<Item = (Option<Self::SharedSecret>, &'a EphemeralKeyBytes)>,
|
||||
) -> Vec<Option<Self::SymmetricKey>> {
|
||||
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<Item = EphemeralKeyBytes>,
|
||||
) -> Vec<(Option<Self::EphemeralPublicKey>, 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
|
||||
|
|
Loading…
Reference in New Issue