p2panda_encryption/key_bundle/
prekey.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use serde::{Deserialize, Serialize};
4
5use crate::crypto::Rng;
6use crate::crypto::x25519::{PUBLIC_KEY_SIZE, PublicKey, SecretKey};
7use crate::crypto::xeddsa::{XEdDSAError, XSignature, xeddsa_sign};
8use crate::key_bundle::{Lifetime, LifetimeError};
9
10/// Pre-key with key material for X3DH key agreement to be used until it's lifetime has expired.
11#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
12pub struct PreKey(PublicKey, Lifetime);
13
14/// Unique identifier of a member's pre-key which can be used to address long-term key bundles.
15pub type PreKeyId = PublicKey;
16
17impl PreKey {
18    pub fn new(prekey: PublicKey, lifetime: Lifetime) -> Self {
19        Self(prekey, lifetime)
20    }
21
22    pub fn key(&self) -> &PublicKey {
23        &self.0
24    }
25
26    pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
27        self.0.as_bytes()
28    }
29
30    pub fn to_bytes(self) -> [u8; PUBLIC_KEY_SIZE] {
31        self.0.to_bytes()
32    }
33
34    pub fn sign(&self, secret_key: &SecretKey, rng: &Rng) -> Result<XSignature, XEdDSAError> {
35        xeddsa_sign(self.0.as_bytes(), secret_key, rng)
36    }
37
38    pub fn lifetime(&self) -> &Lifetime {
39        &self.1
40    }
41
42    pub fn verify_lifetime(&self) -> Result<(), LifetimeError> {
43        self.1.verify()
44    }
45}
46
47/// Unique identifier of a member's one-time pre-key.
48pub type OneTimePreKeyId = u64;
49
50/// Pre-key with key material for X3DH key agreement to be used exactly _once_.
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub struct OneTimePreKey(PublicKey, OneTimePreKeyId);
53
54impl OneTimePreKey {
55    pub fn new(onetime_prekey: PublicKey, id: OneTimePreKeyId) -> Self {
56        Self(onetime_prekey, id)
57    }
58
59    pub fn key(&self) -> &PublicKey {
60        &self.0
61    }
62
63    pub fn id(&self) -> OneTimePreKeyId {
64        self.1
65    }
66
67    pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
68        self.0.as_bytes()
69    }
70
71    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
72        self.0.to_bytes()
73    }
74}
75
76/// Helper method to identify the "latest" (valid and with furthest expiry date) pre-key from a
77/// set. Returns `None` if no valid key was given.
78pub fn latest_prekey<'a>(prekeys: Vec<&'a PreKey>) -> Option<&'a PreKey> {
79    let mut latest: Option<&'a PreKey> = None;
80
81    for prekey in prekeys {
82        // Remove all prekeys which are _too early_ or _too late_ (expired).
83        //
84        //                   Now
85        // too late --> [---] |
86        //                    | [----] <-- too early
87        //              [-----|----] <-- valid
88        //                    |
89        //
90        //                  t -->
91        //
92        if prekey.lifetime().verify().is_err() {
93            continue;
94        }
95
96        // Of all other, valid ones, find the one which has the "furthest" expiry date and is
97        // therefore the "latest" key bundle.
98        //
99        //                   Now
100        //                    |
101        //                  [-|---------]
102        //              [-----|------------] <-- "latest"
103        //          [---------|-----]
104        //                    |
105        //
106        //                  t -->
107        //
108        match latest {
109            Some(current_prekey) => {
110                if prekey.lifetime() > current_prekey.lifetime() {
111                    latest = Some(prekey);
112                }
113            }
114            None => {
115                latest = Some(prekey);
116            }
117        }
118    }
119
120    latest
121}