p2panda_encryption/key_bundle/
key_bundle.rs1use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6use crate::crypto::x25519::PublicKey;
7use crate::crypto::xeddsa::{XEdDSAError, XSignature, xeddsa_verify};
8use crate::key_bundle::{Lifetime, LifetimeError, OneTimePreKey, OneTimePreKeyId, PreKey};
9use crate::traits::KeyBundle;
10
11#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub struct OneTimeKeyBundle {
18 identity_key: PublicKey,
19 signed_prekey: PreKey,
20 prekey_signature: XSignature,
21 onetime_prekey: Option<OneTimePreKey>,
22}
23
24impl OneTimeKeyBundle {
25 pub fn new(
26 identity_key: PublicKey,
27 signed_prekey: PreKey,
28 prekey_signature: XSignature,
29 onetime_prekey: Option<OneTimePreKey>,
30 ) -> Self {
31 Self {
32 identity_key,
33 signed_prekey,
34 prekey_signature,
35 onetime_prekey,
36 }
37 }
38}
39
40impl KeyBundle for OneTimeKeyBundle {
41 fn identity_key(&self) -> &PublicKey {
42 &self.identity_key
43 }
44
45 fn signed_prekey(&self) -> &PublicKey {
46 self.signed_prekey.key()
47 }
48
49 fn onetime_prekey(&self) -> Option<&PublicKey> {
50 self.onetime_prekey.as_ref().map(|key| key.key())
51 }
52
53 fn onetime_prekey_id(&self) -> Option<OneTimePreKeyId> {
54 self.onetime_prekey.as_ref().map(|key| key.id())
55 }
56
57 fn lifetime(&self) -> &Lifetime {
58 self.signed_prekey.lifetime()
59 }
60
61 fn verify(&self) -> Result<(), KeyBundleError> {
62 self.signed_prekey.verify_lifetime()?;
64
65 xeddsa_verify(
67 self.signed_prekey.as_bytes(),
68 &self.identity_key,
69 &self.prekey_signature,
70 )?;
71
72 Ok(())
73 }
74}
75
76#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
82pub struct LongTermKeyBundle {
83 identity_key: PublicKey,
84 signed_prekey: PreKey,
85 prekey_signature: XSignature,
86}
87
88impl LongTermKeyBundle {
89 pub fn new(
90 identity_key: PublicKey,
91 signed_prekey: PreKey,
92 prekey_signature: XSignature,
93 ) -> Self {
94 Self {
95 identity_key,
96 signed_prekey,
97 prekey_signature,
98 }
99 }
100}
101
102impl KeyBundle for LongTermKeyBundle {
103 fn identity_key(&self) -> &PublicKey {
104 &self.identity_key
105 }
106
107 fn signed_prekey(&self) -> &PublicKey {
108 self.signed_prekey.key()
109 }
110
111 fn onetime_prekey(&self) -> Option<&PublicKey> {
112 None
114 }
115
116 fn onetime_prekey_id(&self) -> Option<OneTimePreKeyId> {
117 None
119 }
120
121 fn lifetime(&self) -> &Lifetime {
122 self.signed_prekey.lifetime()
123 }
124
125 fn verify(&self) -> Result<(), KeyBundleError> {
126 self.signed_prekey.verify_lifetime()?;
128
129 xeddsa_verify(
131 self.signed_prekey.as_bytes(),
132 &self.identity_key,
133 &self.prekey_signature,
134 )?;
135
136 Ok(())
137 }
138}
139
140pub fn latest_key_bundle<'a, KB>(bundles: &'a [KB]) -> Option<&'a KB>
143where
144 KB: KeyBundle,
145{
146 let mut latest: Option<&'a KB> = None;
147
148 for bundle in bundles {
149 if bundle.lifetime().verify().is_err() {
160 continue;
161 }
162
163 match latest {
176 Some(current_bundle) => {
177 if bundle.lifetime() > current_bundle.lifetime() {
178 latest = Some(bundle);
179 }
180 }
181 None => {
182 latest = Some(bundle);
183 }
184 }
185 }
186
187 latest
188}
189
190#[derive(Debug, Error)]
191pub enum KeyBundleError {
192 #[error(transparent)]
193 XEdDSA(#[from] XEdDSAError),
194
195 #[error(transparent)]
196 Lifetime(#[from] LifetimeError),
197}
198
199#[cfg(test)]
200mod tests {
201 use crate::crypto::Rng;
202 use crate::crypto::x25519::SecretKey;
203 use crate::crypto::xeddsa::xeddsa_sign;
204 use crate::key_bundle::{Lifetime, LongTermKeyBundle, OneTimePreKey, PreKey};
205 use crate::traits::KeyBundle;
206
207 use super::OneTimeKeyBundle;
208
209 #[test]
210 fn verify() {
211 let rng = Rng::from_seed([1; 32]);
212
213 let secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
214 let identity_key = secret_key.public_key().unwrap();
215
216 let signed_prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
217 let signed_prekey = PreKey::new(
218 signed_prekey_secret.public_key().unwrap(),
219 Lifetime::default(),
220 );
221 let prekey_signature = xeddsa_sign(signed_prekey.as_bytes(), &secret_key, &rng).unwrap();
222
223 let onetime_prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
224 let onetime_prekey = OneTimePreKey::new(onetime_prekey_secret.public_key().unwrap(), 1);
225
226 assert!(
228 OneTimeKeyBundle::new(
229 identity_key,
230 signed_prekey,
231 prekey_signature,
232 Some(onetime_prekey.clone()),
233 )
234 .verify()
235 .is_ok()
236 );
237 assert!(
238 LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
239 .verify()
240 .is_ok()
241 );
242
243 let signed_prekey = PreKey::new(
245 signed_prekey_secret.public_key().unwrap(),
246 Lifetime::from_range(0, 0),
247 );
248 assert!(
249 OneTimeKeyBundle::new(
250 identity_key,
251 signed_prekey,
252 prekey_signature,
253 Some(onetime_prekey.clone()),
254 )
255 .verify()
256 .is_err()
257 );
258 assert!(
259 LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
260 .verify()
261 .is_err()
262 );
263
264 let prekey_signature = xeddsa_sign(b"wrong payload", &secret_key, &rng).unwrap();
266 assert!(
267 OneTimeKeyBundle::new(
268 identity_key,
269 signed_prekey,
270 prekey_signature,
271 Some(onetime_prekey.clone()),
272 )
273 .verify()
274 .is_err()
275 );
276 assert!(
277 LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
278 .verify()
279 .is_err()
280 );
281 }
282}