Skip to main content

bitwarden_crypto/keys/
key_id.rs

1use rand::RngExt;
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::rng();
33        let mut key_id = [0u8; KEY_ID_SIZE];
34        rng.fill(&mut key_id);
35        Self(key_id)
36    }
37
38    /// Returns the key ID as a slice of bytes.
39    pub fn as_slice(&self) -> &[u8] {
40        &self.0
41    }
42}
43
44impl TryFrom<&[u8]> for KeyId {
45    type Error = &'static str;
46
47    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
48        if value.len() != KEY_ID_SIZE {
49            return Err("Invalid length for KeyId");
50        }
51        let mut key_id = [0u8; KEY_ID_SIZE];
52        key_id.copy_from_slice(value);
53        Ok(Self(key_id))
54    }
55}
56
57impl From<KeyId> for [u8; KEY_ID_SIZE] {
58    fn from(key_id: KeyId) -> Self {
59        key_id.0
60    }
61}
62
63impl From<&KeyId> for Vec<u8> {
64    fn from(key_id: &KeyId) -> Self {
65        key_id.0.as_slice().to_vec()
66    }
67}
68
69impl From<[u8; KEY_ID_SIZE]> for KeyId {
70    fn from(bytes: [u8; KEY_ID_SIZE]) -> Self {
71        Self(bytes)
72    }
73}
74
75impl std::fmt::Debug for KeyId {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        write!(f, "KeyId({})", hex::encode(self.0))
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    #[ignore = "Manual test to verify debug format"]
87    fn test_key_id_debug() {
88        let key_id = KeyId::make();
89        println!("{:?}", key_id);
90    }
91}