bitwarden_crypto/traits/
key_id.rs

1use std::{fmt::Debug, hash::Hash};
2
3use zeroize::ZeroizeOnDrop;
4
5use crate::{CryptoKey, PrivateKey, SigningKey, SymmetricCryptoKey};
6
7/// Represents a key identifier that can be used to identify cryptographic keys in the
8/// key store. It is used to avoid exposing the key material directly in the public API.
9///
10/// This trait is user-implemented, and the recommended implementation is using enums with variants
11/// for each expected key purpose. We provide a macro ([crate::key_ids]) that simplifies the trait
12/// implementation
13///
14/// To implement it manually, note that you need a few types:
15/// - One implementing [KeyId<KeyValue = SymmetricCryptoKey>]
16/// - One implementing [KeyId<KeyValue = PrivateKey>]
17/// - One implementing [KeyIds]
18pub trait KeyId:
19    Debug + Clone + Copy + Hash + Eq + PartialEq + Ord + PartialOrd + Send + Sync + 'static
20{
21    #[allow(missing_docs)]
22    type KeyValue: CryptoKey + Send + Sync + ZeroizeOnDrop;
23
24    /// Returns whether the key is local to the current context or shared globally by the
25    /// key store. See [crate::store::KeyStoreContext] for more information.
26    fn is_local(&self) -> bool;
27
28    /// Creates a new unique local key identifier.
29    fn new_local(id: LocalId) -> Self;
30}
31
32/// Represents a set of all the key identifiers that need to be defined to use a key store.
33pub trait KeyIds {
34    #[allow(missing_docs)]
35    type Symmetric: KeyId<KeyValue = SymmetricCryptoKey>;
36    #[allow(missing_docs)]
37    type Private: KeyId<KeyValue = PrivateKey>;
38    /// Signing keys are used to create detached signatures and to sign objects.
39    type Signing: KeyId<KeyValue = SigningKey>;
40}
41
42/// An opaque identifier for a local key. Currently only contains a unique ID, but it can be
43/// extended to contain scope information to allow cleanup on scope exit.
44#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
45pub struct LocalId(pub(crate) uuid::Uuid);
46
47impl LocalId {
48    pub(crate) fn new() -> Self {
49        LocalId(uuid::Uuid::new_v4())
50    }
51}
52
53/// Just a small derive_like macro that can be used to generate the key identifier enums.
54/// Example usage:
55/// ```rust
56/// use bitwarden_crypto::key_ids;
57/// key_ids! {
58///     #[symmetric]
59///     pub enum SymmKeyId {
60///         User,
61///         Org(uuid::Uuid),
62///         #[local]
63///         Local(LocalId),
64///     }
65///
66///     #[private]
67///     pub enum PrivateKeyId {
68///         PrivateKey,
69///         #[local]
70///         Local(LocalId),
71///     }
72///
73///     #[signing]
74///     pub enum SigningKeyId {
75///        SigningKey,
76///        #[local]
77///        Local(LocalId),
78///     }
79///
80///     pub Ids => SymmKeyId, PrivateKeyId, SigningKeyId;
81/// }
82#[macro_export]
83macro_rules! key_ids {
84    ( $(
85        #[$meta_type:tt]
86        $vis:vis enum $name:ident {
87            $(
88                $( #[$variant_tag:tt] )?
89                $variant:ident $( ( $inner:ty ) )?
90            ),*
91            $(,)?
92        }
93    )+
94    $ids_vis:vis $ids_name:ident => $symm_name:ident, $private_name:ident, $signing_name:ident;
95    ) => {
96
97        use $crate::LocalId;
98
99        $(
100            #[derive(std::fmt::Debug, Clone, Copy, std::hash::Hash, Eq, PartialEq, Ord, PartialOrd)]
101            #[allow(missing_docs)]
102            $vis enum $name { $(
103                $variant  $( ($inner) )?,
104            )* }
105
106            impl $crate::KeyId for $name {
107                type KeyValue = key_ids!(@key_type $meta_type);
108
109                fn is_local(&self) -> bool {
110                    use $name::*;
111                    match self { $(
112                        key_ids!(@variant_match $variant $( ( $inner ) )?) =>
113                            key_ids!(@variant_value $( $variant_tag )? ),
114                    )* }
115                }
116
117                fn new_local(id: LocalId) -> Self {
118                    $(
119                        { key_ids!(@new_local $variant  id $( $variant_tag )? ) }
120                    )*
121                }
122            }
123        )+
124
125        #[allow(missing_docs)]
126        $ids_vis struct $ids_name;
127        impl $crate::KeyIds for $ids_name {
128            type Symmetric = $symm_name;
129            type Private = $private_name;
130            type Signing = $signing_name;
131        }
132    };
133
134    ( @key_type symmetric ) => { $crate::SymmetricCryptoKey };
135    ( @key_type private ) => { $crate::PrivateKey };
136    ( @key_type signing ) => { $crate::SigningKey };
137
138    ( @variant_match $variant:ident ( $inner:ty ) ) => { $variant (_) };
139    ( @variant_match $variant:ident ) => { $variant };
140
141    ( @variant_value local ) => { true };
142    ( @variant_value ) => { false };
143
144    ( @new_local $variant:ident $id:ident local  ) => { Self::$variant($id) };
145    ( @new_local $variant:ident $id:ident ) => {{}};
146}
147
148#[cfg(test)]
149pub(crate) mod tests {
150
151    use crate::{
152        KeyId, LocalId,
153        traits::tests::{TestPrivateKey, TestSigningKey, TestSymmKey},
154    };
155
156    #[test]
157    fn test_local() {
158        let local = LocalId::new();
159
160        assert!(!TestSymmKey::A(0).is_local());
161        assert!(!TestSymmKey::B((4, 10)).is_local());
162        assert!(TestSymmKey::C(local).is_local());
163
164        assert!(!TestPrivateKey::A(0).is_local());
165        assert!(!TestPrivateKey::B.is_local());
166        assert!(TestPrivateKey::C(local).is_local());
167
168        assert!(!TestSigningKey::A(0).is_local());
169        assert!(!TestSigningKey::B.is_local());
170        assert!(TestSigningKey::C(local).is_local());
171    }
172}