zcash_note_encryption/
batch.rs1use alloc::vec::Vec; use crate::{
6 BatchDomain, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, EphemeralKeyBytes, ShieldedOutput,
7 try_compact_note_decryption_inner, try_note_decryption_inner,
8};
9
10#[allow(clippy::type_complexity)]
19pub fn try_note_decryption<D: BatchDomain, Output: ShieldedOutput<D, ENC_CIPHERTEXT_SIZE>>(
20 ivks: &[D::IncomingViewingKey],
21 outputs: &[(D, Output)],
22) -> Vec<Option<((D::Note, D::Recipient, D::Memo), usize)>> {
23 batch_note_decryption(ivks, outputs, try_note_decryption_inner)
24}
25
26#[allow(clippy::type_complexity)]
36pub fn try_compact_note_decryption<D: BatchDomain, Output: ShieldedOutput<D, COMPACT_NOTE_SIZE>>(
37 ivks: &[D::IncomingViewingKey],
38 outputs: &[(D, Output)],
39) -> Vec<Option<((D::Note, D::Recipient), usize)>> {
40 batch_note_decryption(ivks, outputs, try_compact_note_decryption_inner)
41}
42
43fn batch_note_decryption<D: BatchDomain, Output: ShieldedOutput<D, CS>, F, FR, const CS: usize>(
44 ivks: &[D::IncomingViewingKey],
45 outputs: &[(D, Output)],
46 decrypt_inner: F,
47) -> Vec<Option<(FR, usize)>>
48where
49 F: Fn(&D, &D::IncomingViewingKey, &EphemeralKeyBytes, &Output, &D::SymmetricKey) -> Option<FR>,
50{
51 if ivks.is_empty() {
52 return (0..outputs.len()).map(|_| None).collect();
53 };
54
55 let ephemeral_keys = D::batch_epk(outputs.iter().map(|(_, output)| output.ephemeral_key()));
57
58 let mut columns: Vec<_> = ivks
66 .iter()
67 .map(|ivk| {
68 D::batch_ka_agree_dec(ivk, ephemeral_keys.iter().map(|(epk, _)| epk.as_ref()))
69 .into_iter()
70 })
71 .collect();
72 let mut secrets: Vec<Option<D::SharedSecret>> =
73 Vec::with_capacity(ephemeral_keys.len() * ivks.len());
74 for _ in 0..ephemeral_keys.len() {
75 for column in columns.iter_mut() {
76 secrets.push(
77 column
78 .next()
79 .expect("all columns have one entry per output"),
80 );
81 }
82 }
83 #[allow(clippy::manual_repeat_n)]
85 let items = secrets.into_iter().zip(
86 ephemeral_keys
87 .iter()
88 .flat_map(|(_, ephemeral_key)| core::iter::repeat(ephemeral_key).take(ivks.len())),
89 );
90
91 let keys = D::batch_kdf(items);
93
94 keys.chunks(ivks.len())
96 .zip(ephemeral_keys.iter().zip(outputs.iter()))
97 .map(|(key_chunk, ((_, ephemeral_key), (domain, output)))| {
98 key_chunk
99 .iter()
100 .zip(ivks.iter().enumerate())
101 .find_map(|(key, (i, ivk))| {
102 key.as_ref()
103 .and_then(|key| decrypt_inner(domain, ivk, ephemeral_key, output, key))
104 .map(|out| (out, i))
105 })
106 })
107 .collect::<Vec<Option<_>>>()
108}