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 #[error("no key bundles found")]
199 KeyBundlesNotFound,
200}
201
202#[cfg(test)]
203mod tests {
204 use crate::crypto::Rng;
205 use crate::crypto::x25519::SecretKey;
206 use crate::crypto::xeddsa::xeddsa_sign;
207 use crate::key_bundle::{Lifetime, LongTermKeyBundle, OneTimePreKey, PreKey};
208 use crate::traits::KeyBundle;
209
210 use super::OneTimeKeyBundle;
211
212 #[test]
213 fn verify() {
214 let rng = Rng::from_seed([1; 32]);
215
216 let secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
217 let identity_key = secret_key.verifying_key().unwrap();
218
219 let signed_prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
220 let signed_prekey = PreKey::new(
221 signed_prekey_secret.verifying_key().unwrap(),
222 Lifetime::default(),
223 );
224 let prekey_signature = xeddsa_sign(signed_prekey.as_bytes(), &secret_key, &rng).unwrap();
225
226 let onetime_prekey_secret = SecretKey::from_bytes(rng.random_array().unwrap());
227 let onetime_prekey = OneTimePreKey::new(onetime_prekey_secret.verifying_key().unwrap(), 1);
228
229 assert!(
231 OneTimeKeyBundle::new(
232 identity_key,
233 signed_prekey,
234 prekey_signature,
235 Some(onetime_prekey.clone()),
236 )
237 .verify()
238 .is_ok()
239 );
240 assert!(
241 LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
242 .verify()
243 .is_ok()
244 );
245
246 let signed_prekey = PreKey::new(
248 signed_prekey_secret.verifying_key().unwrap(),
249 Lifetime::from_range(0, 0),
250 );
251 assert!(
252 OneTimeKeyBundle::new(
253 identity_key,
254 signed_prekey,
255 prekey_signature,
256 Some(onetime_prekey.clone()),
257 )
258 .verify()
259 .is_err()
260 );
261 assert!(
262 LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
263 .verify()
264 .is_err()
265 );
266
267 let prekey_signature = xeddsa_sign(b"wrong payload", &secret_key, &rng).unwrap();
269 assert!(
270 OneTimeKeyBundle::new(
271 identity_key,
272 signed_prekey,
273 prekey_signature,
274 Some(onetime_prekey.clone()),
275 )
276 .verify()
277 .is_err()
278 );
279 assert!(
280 LongTermKeyBundle::new(identity_key, signed_prekey, prekey_signature)
281 .verify()
282 .is_err()
283 );
284 }
285}