1use crate::kem::{Compress, Error, SharedSecret, IBKEM, SS_BYTES};
39use core::slice::Iter;
40use rand::{CryptoRng, Rng};
41use subtle::CtOption;
42
43use aes_gcm::aead::{Nonce, Tag};
44use aes_gcm::{AeadInPlace, Aes128Gcm, KeyInit};
45
46#[cfg(feature = "cgwfo")]
47use crate::kem::cgw_fo::CGWFO;
48
49#[cfg(feature = "cgwkv")]
50use crate::kem::cgw_kv::CGWKV;
51
52#[cfg(feature = "kv1")]
53use crate::kem::kiltz_vahlis_one::KV1;
54
55const TAG_SIZE: usize = 16;
56const NONCE_SIZE: usize = 12;
57const KEY_SIZE: usize = 16;
58
59impl SharedSecret {
60 fn random<R: Rng + CryptoRng>(r: &mut R) -> Self {
62 let mut ss_bytes = [0u8; SS_BYTES];
63 r.fill_bytes(&mut ss_bytes);
64
65 SharedSecret(ss_bytes)
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct Ciphertext<K: IBKEM> {
72 ct_asymm: K::Ct,
73 ct_symm: [u8; SS_BYTES],
74 tag: Tag<Aes128Gcm>,
75 nonce: Nonce<Aes128Gcm>,
76}
77
78#[derive(Debug)]
80pub struct Ciphertexts<'a, K: IBKEM, R> {
81 ss: SharedSecret,
82 pk: &'a K::Pk,
83 ids: Iter<'a, K::Id>,
84 rng: &'a mut R,
85}
86
87impl<'a, K, R> Iterator for Ciphertexts<'a, K, R>
88where
89 K: IBKEM,
90 R: Rng + CryptoRng,
91{
92 type Item = Ciphertext<K>;
93
94 fn next(&mut self) -> Option<Self::Item> {
95 let id = self.ids.next()?;
96
97 let (ct_asymm, kek) = <K as IBKEM>::encaps(self.pk, id, self.rng);
98
99 let aead = Aes128Gcm::new_from_slice(&kek.0[..KEY_SIZE]).unwrap();
100 let nonce_bytes = self.rng.gen::<[u8; NONCE_SIZE]>();
101 let nonce = Nonce::<Aes128Gcm>::from_slice(&nonce_bytes);
102
103 let mut shared_key = self.ss.0;
104
105 let tag = aead
106 .encrypt_in_place_detached(nonce, b"", &mut shared_key)
107 .unwrap();
108
109 Some(Ciphertext::<K> {
110 ct_asymm,
111 ct_symm: shared_key,
112 nonce: *nonce,
113 tag,
114 })
115 }
116}
117
118pub trait MultiRecipient: IBKEM {
120 fn multi_encaps<'a, R: Rng + CryptoRng>(
122 pk: &'a <Self as IBKEM>::Pk,
123 ids: impl IntoIterator<IntoIter = Iter<'a, Self::Id>>,
124 rng: &'a mut R,
125 ) -> (Ciphertexts<'a, Self, R>, SharedSecret) {
126 let ss = SharedSecret::random(rng);
127
128 let cts = Ciphertexts {
129 ss,
130 pk,
131 rng,
132 ids: ids.into_iter(),
133 };
134
135 (cts, ss)
136 }
137
138 fn multi_decaps(
145 mpk: Option<&Self::Pk>,
146 usk: &Self::Usk,
147 ct: &Ciphertext<Self>,
148 ) -> Result<SharedSecret, Error> {
149 let kek = <Self as IBKEM>::decaps(mpk, usk, &ct.ct_asymm)?;
150 let aead = Aes128Gcm::new_from_slice(&kek.0[..KEY_SIZE]).unwrap();
151 let mut shared_key = ct.ct_symm;
152 aead.decrypt_in_place_detached(&ct.nonce, b"", &mut shared_key, &ct.tag)
153 .map_err(|_e| Error)?;
154
155 Ok(SharedSecret(shared_key))
156 }
157}
158
159macro_rules! impl_mkemct_compress {
160 ($scheme: ident) => {
161 impl Compress for Ciphertext<$scheme> {
162 const OUTPUT_SIZE: usize = $scheme::CT_BYTES + SS_BYTES + TAG_SIZE + NONCE_SIZE;
163 type Output = [u8; Self::OUTPUT_SIZE];
164
165 fn to_bytes(&self) -> Self::Output {
166 use arrayref::mut_array_refs;
167
168 let mut res = [0u8; Self::OUTPUT_SIZE];
169 let (ct_asymm, ct_symm, tag, nonce) =
170 mut_array_refs![&mut res, $scheme::CT_BYTES, SS_BYTES, TAG_SIZE, NONCE_SIZE];
171
172 *ct_asymm = self.ct_asymm.to_bytes();
173 *ct_symm = self.ct_symm;
174 *tag = self.tag.into();
175 *nonce = self.nonce.into();
176
177 res
178 }
179
180 fn from_bytes(output: &Self::Output) -> CtOption<Self> {
181 use arrayref::array_refs;
182
183 let (ct_asymm, ct_symm, tag, nonce) =
184 array_refs![&output, $scheme::CT_BYTES, SS_BYTES, TAG_SIZE, NONCE_SIZE];
185
186 let ct_asymm = <$scheme as IBKEM>::Ct::from_bytes(ct_asymm);
187 let tag = Tag::<Aes128Gcm>::from_slice(tag);
188 let nonce = Nonce::<Aes128Gcm>::from_slice(nonce);
189
190 ct_asymm.map(|ct_asymm| Ciphertext {
191 ct_asymm,
192 ct_symm: *ct_symm,
193 tag: *tag,
194 nonce: *nonce,
195 })
196 }
197 }
198 };
199}
200
201#[cfg(feature = "cgwkv")]
202impl_mkemct_compress!(CGWKV);
203
204#[cfg(feature = "cgwfo")]
205impl_mkemct_compress!(CGWFO);
206
207#[cfg(feature = "kv1")]
208impl_mkemct_compress!(KV1);