Skip to main content

bitwarden_crypto/keys/
key_id.rs

1use rand::Rng;
2use subtle::ConstantTimeEq;
3use zeroize::Zeroize;
4
5/// Since `KeyId` is a wrapper around UUIDs, this is statically 16 bytes.
6pub(crate) const KEY_ID_SIZE: usize = 16;
7
8/// A key id is a unique identifier for a single key. There is a 1:1 mapping between key ID and key
9/// bytes, so something like a user key rotation is replacing the key with ID A with a new key with
10/// ID B.
11#[derive(Clone, PartialEq, Zeroize)]
12pub struct KeyId([u8; KEY_ID_SIZE]);
13
14// Constant time here is not implemented because the key-id itself is secret, it is not.
15// Instead, it is implemented to correctly allow other things that implement ct_eq to correctly
16// implement the ct_eq contract. This is the case for COSE keys that have key material and a key id.
17impl ConstantTimeEq for KeyId {
18    fn ct_eq(&self, other: &Self) -> subtle::Choice {
19        self.0.ct_eq(&other.0)
20    }
21}
22
23/// Fixed length identifiers for keys.
24/// These are intended to be unique and constant per-key.
25///
26/// Currently these are randomly generated 16 byte identifiers, which is considered safe to randomly
27/// generate with vanishingly small collision chance. However, the generation of IDs is an internal
28/// concern and may change in the future.
29impl KeyId {
30    /// Creates a new random key ID randomly, sampled from the crates CSPRNG.
31    pub fn make() -> Self {
32        let mut rng = rand::thread_rng();
33        let mut key_id = [0u8; KEY_ID_SIZE];
34        rng.fill(&mut key_id);
35        Self(key_id)
36    }
37
38    pub fn as_slice(&self) -> &[u8] {
39        &self.0
40    }
41}
42
43impl TryFrom<&[u8]> for KeyId {
44    type Error = &'static str;
45
46    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
47        if value.len() != KEY_ID_SIZE {
48            return Err("Invalid length for KeyId");
49        }
50        let mut key_id = [0u8; KEY_ID_SIZE];
51        key_id.copy_from_slice(value);
52        Ok(Self(key_id))
53    }
54}
55
56impl From<KeyId> for [u8; KEY_ID_SIZE] {
57    fn from(key_id: KeyId) -> Self {
58        key_id.0
59    }
60}
61
62impl From<&KeyId> for Vec<u8> {
63    fn from(key_id: &KeyId) -> Self {
64        key_id.0.as_slice().to_vec()
65    }
66}
67
68impl From<[u8; KEY_ID_SIZE]> for KeyId {
69    fn from(bytes: [u8; KEY_ID_SIZE]) -> Self {
70        Self(bytes)
71    }
72}
73
74impl std::fmt::Debug for KeyId {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, "KeyId({})", hex::encode(self.0))
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    #[ignore = "Manual test to verify debug format"]
86    fn test_key_id_debug() {
87        let key_id = KeyId::make();
88        println!("{:?}", key_id);
89    }
90}