1use std::collections::HashMap;
8use std::convert::Infallible;
9use std::fmt::Debug;
10use std::marker::PhantomData;
11
12use serde::{Deserialize, Serialize};
13use thiserror::Error;
14
15use crate::crypto::x25519::PublicKey;
16use crate::key_bundle::{KeyBundleError, LongTermKeyBundle, OneTimeKeyBundle, latest_key_bundle};
17use crate::traits::{IdentityHandle, IdentityRegistry, KeyBundle, PreKeyRegistry};
18
19#[derive(Clone, Debug)]
21pub struct KeyRegistry<ID> {
22 _marker: PhantomData<ID>,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
27pub struct KeyRegistryState<ID>
28where
29 ID: IdentityHandle,
30{
31 identities: HashMap<ID, PublicKey>,
32 onetime_bundles: HashMap<ID, Vec<OneTimeKeyBundle>>,
33 longterm_bundles: HashMap<ID, Vec<LongTermKeyBundle>>,
34}
35
36impl<ID> KeyRegistry<ID>
37where
38 ID: IdentityHandle + Serialize + for<'a> Deserialize<'a>,
39{
40 pub fn init() -> KeyRegistryState<ID> {
42 KeyRegistryState {
43 identities: HashMap::new(),
44 onetime_bundles: HashMap::new(),
45 longterm_bundles: HashMap::new(),
46 }
47 }
48
49 pub fn remove_expired(mut y: KeyRegistryState<ID>) -> KeyRegistryState<ID> {
51 y.longterm_bundles =
52 y.longterm_bundles
53 .into_iter()
54 .fold(HashMap::new(), |mut acc, (id, bundles)| {
55 let bundles = bundles
56 .into_iter()
57 .filter(|bundle| bundle.verify().is_ok())
58 .collect::<Vec<LongTermKeyBundle>>();
59 acc.insert(id, bundles);
60 acc
61 });
62
63 y.onetime_bundles =
64 y.onetime_bundles
65 .into_iter()
66 .fold(HashMap::new(), |mut acc, (id, bundles)| {
67 let bundles = bundles
68 .into_iter()
69 .filter(|bundle| bundle.verify().is_ok())
70 .collect::<Vec<OneTimeKeyBundle>>();
71 acc.insert(id, bundles);
72 acc
73 });
74 y
75 }
76
77 pub fn add_longterm_bundle(
81 mut y: KeyRegistryState<ID>,
82 id: ID,
83 key_bundle: LongTermKeyBundle,
84 ) -> Result<KeyRegistryState<ID>, KeyRegistryError> {
85 key_bundle.verify()?;
86 let existing = y.identities.insert(id, *key_bundle.identity_key());
87 if let Some(existing) = existing {
88 assert_eq!(&existing, key_bundle.identity_key());
90 }
91 y.longterm_bundles
92 .entry(id)
93 .and_modify(|bundles| bundles.push(key_bundle.clone()))
94 .or_insert(vec![key_bundle]);
95 Ok(y)
96 }
97
98 #[cfg(test)]
99 #[allow(non_snake_case)]
100 fn add_longterm_bundle_UNVERIFIED(
101 mut y: KeyRegistryState<ID>,
102 id: ID,
103 key_bundle: LongTermKeyBundle,
104 ) -> KeyRegistryState<ID> {
105 y.longterm_bundles
106 .entry(id)
107 .and_modify(|bundles| bundles.push(key_bundle.clone()))
108 .or_insert(vec![key_bundle]);
109 y
110 }
111
112 pub fn add_onetime_bundle(
116 mut y: KeyRegistryState<ID>,
117 id: ID,
118 key_bundle: OneTimeKeyBundle,
119 ) -> Result<KeyRegistryState<ID>, KeyRegistryError> {
120 key_bundle.verify()?;
121 let existing = y.identities.insert(id, *key_bundle.identity_key());
122 if let Some(existing) = existing {
123 assert_eq!(&existing, key_bundle.identity_key());
125 }
126 y.onetime_bundles
127 .entry(id)
128 .and_modify(|bundles| bundles.push(key_bundle.clone()))
129 .or_insert(vec![key_bundle]);
130 Ok(y)
131 }
132}
133
134impl<ID> PreKeyRegistry<ID, OneTimeKeyBundle> for KeyRegistry<ID>
135where
136 ID: IdentityHandle + Serialize + for<'a> Deserialize<'a>,
137{
138 type State = KeyRegistryState<ID>;
139
140 type Error = Infallible;
141
142 fn key_bundle(
143 mut y: Self::State,
144 id: &ID,
145 ) -> Result<(Self::State, Option<OneTimeKeyBundle>), Self::Error> {
146 let bundle = y
147 .onetime_bundles
148 .get_mut(id)
149 .and_then(|bundles| bundles.pop());
150 Ok((y, bundle))
151 }
152}
153
154impl<ID> PreKeyRegistry<ID, LongTermKeyBundle> for KeyRegistry<ID>
155where
156 ID: IdentityHandle + Serialize + for<'a> Deserialize<'a>,
157{
158 type State = KeyRegistryState<ID>;
159
160 type Error = KeyRegistryError;
161
162 fn key_bundle(
163 y: Self::State,
164 id: &ID,
165 ) -> Result<(Self::State, Option<LongTermKeyBundle>), Self::Error> {
166 let Some(bundles) = y.longterm_bundles.get(id) else {
167 return Ok((y, None));
168 };
169
170 let valid_bundle = latest_key_bundle(bundles).cloned();
171
172 if !bundles.is_empty() && valid_bundle.is_none() {
174 return Err(KeyRegistryError::KeyBundlesExpired);
175 }
176
177 Ok((y, valid_bundle))
178 }
179}
180
181impl<ID> IdentityRegistry<ID, KeyRegistryState<ID>> for KeyRegistry<ID>
182where
183 ID: IdentityHandle + Serialize + for<'a> Deserialize<'a>,
184{
185 type Error = Infallible;
186
187 fn identity_key(y: &KeyRegistryState<ID>, id: &ID) -> Result<Option<PublicKey>, Self::Error> {
188 let key = y.identities.get(id).cloned();
189 Ok(key)
190 }
191}
192
193#[derive(Debug, Error)]
194pub enum KeyRegistryError {
195 #[error(transparent)]
196 KeyBundle(#[from] KeyBundleError),
197
198 #[error("no key bundles found")]
199 KeyBundlesNotFound,
200
201 #[error("all available key bundles of this member expired")]
202 KeyBundlesExpired,
203}
204
205#[cfg(test)]
206mod tests {
207 use std::time::{SystemTime, UNIX_EPOCH};
208
209 use crate::Rng;
210 use crate::crypto::x25519::SecretKey;
211 use crate::key_bundle::{Lifetime, LongTermKeyBundle, PreKey};
212 use crate::traits::PreKeyRegistry;
213
214 use super::KeyRegistry;
215
216 #[test]
217 fn latest_key_bundle() {
218 let rng = Rng::from_seed([1; 32]);
219
220 let now = SystemTime::now()
221 .duration_since(UNIX_EPOCH)
222 .expect("SystemTime before UNIX EPOCH!")
223 .as_secs();
224
225 let member_id = 0;
226 let identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
227
228 let bundle_1 = {
230 let prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
231 let prekey = PreKey::new(
232 prekey_secret.verifying_key().unwrap(),
233 Lifetime::from_range(now - 60, now + 60),
234 );
235 let prekey_signature = prekey.sign(&identity_secret, &rng).unwrap();
236
237 LongTermKeyBundle::new(
238 identity_secret.verifying_key().unwrap(),
239 prekey,
240 prekey_signature,
241 )
242 };
243
244 let bundle_2 = {
246 let prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
247 let prekey = PreKey::new(
248 prekey_secret.verifying_key().unwrap(),
249 Lifetime::from_range(now - 60, now + 30),
250 );
251 let prekey_signature = prekey.sign(&identity_secret, &rng).unwrap();
252
253 LongTermKeyBundle::new(
254 identity_secret.verifying_key().unwrap(),
255 prekey,
256 prekey_signature,
257 )
258 };
259
260 let pki = {
262 let y = KeyRegistry::init();
263 let y = KeyRegistry::add_longterm_bundle(y, member_id, bundle_1.clone()).unwrap();
264 let y = KeyRegistry::add_longterm_bundle(y, member_id, bundle_2).unwrap();
265 y
266 };
267
268 assert_eq!(
270 KeyRegistry::key_bundle(pki, &member_id).unwrap().1,
271 Some(bundle_1)
272 );
273 }
274
275 #[test]
276 fn invalid_bundles() {
277 let rng = Rng::from_seed([1; 32]);
278
279 let now = SystemTime::now()
280 .duration_since(UNIX_EPOCH)
281 .expect("SystemTime before UNIX EPOCH!")
282 .as_secs();
283
284 let member_id = 0;
285 let identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
286
287 let invalid_bundle = {
288 let prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
289 let prekey = PreKey::new(
290 prekey_secret.verifying_key().unwrap(),
291 Lifetime::from_range(now - 60, now - 30),
292 );
293 let prekey_signature = prekey.sign(&identity_secret, &rng).unwrap();
294
295 LongTermKeyBundle::new(
296 identity_secret.verifying_key().unwrap(),
297 prekey,
298 prekey_signature,
299 )
300 };
301
302 let pki = KeyRegistry::init();
303
304 assert!(
306 KeyRegistry::add_longterm_bundle(pki.clone(), member_id, invalid_bundle.clone())
307 .is_err()
308 );
309
310 let pki =
311 KeyRegistry::add_longterm_bundle_UNVERIFIED(pki, member_id, invalid_bundle.clone());
312
313 assert_eq!(pki.longterm_bundles.get(&member_id).unwrap().len(), 1);
315 assert!(
316 <KeyRegistry<usize> as PreKeyRegistry<usize, LongTermKeyBundle>>::key_bundle(
317 pki.clone(),
318 &member_id
319 )
320 .is_err()
321 );
322 }
323
324 #[test]
325 fn garbage_collection() {
326 let rng = Rng::from_seed([1; 32]);
327
328 let now = SystemTime::now()
329 .duration_since(UNIX_EPOCH)
330 .expect("SystemTime before UNIX EPOCH!")
331 .as_secs();
332
333 let member_id = 0;
334 let identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
335
336 let invalid_bundle = {
337 let prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
338 let prekey = PreKey::new(
339 prekey_secret.verifying_key().unwrap(),
340 Lifetime::from_range(now - 60, now - 30),
341 );
342 let prekey_signature = prekey.sign(&identity_secret, &rng).unwrap();
343
344 LongTermKeyBundle::new(
345 identity_secret.verifying_key().unwrap(),
346 prekey,
347 prekey_signature,
348 )
349 };
350
351 let valid_bundle = {
352 let prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
353 let prekey = PreKey::new(
354 prekey_secret.verifying_key().unwrap(),
355 Lifetime::from_range(now - 60, now + 60),
356 );
357 let prekey_signature = prekey.sign(&identity_secret, &rng).unwrap();
358
359 LongTermKeyBundle::new(
360 identity_secret.verifying_key().unwrap(),
361 prekey,
362 prekey_signature,
363 )
364 };
365
366 let pki = {
367 let y = KeyRegistry::init();
368 let y =
369 KeyRegistry::add_longterm_bundle_UNVERIFIED(y, member_id, invalid_bundle.clone());
370 let y = KeyRegistry::add_longterm_bundle_UNVERIFIED(y, member_id, valid_bundle.clone());
371 y
372 };
373
374 assert_eq!(pki.longterm_bundles.get(&member_id).unwrap().len(), 2);
375
376 let pki = KeyRegistry::remove_expired(pki);
378 assert_eq!(pki.longterm_bundles.get(&member_id).unwrap().len(), 1);
379
380 assert_eq!(
382 KeyRegistry::key_bundle(pki, &member_id).unwrap().1,
383 Some(valid_bundle)
384 );
385 }
386}