bitwarden_crypto/traits/
key_id.rs

1use std::{fmt::Debug, hash::Hash};
2
3use zeroize::ZeroizeOnDrop;
4
5use crate::{AsymmetricCryptoKey, CryptoKey, 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 = AsymmetricCryptoKey>]
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
29/// Represents a set of all the key identifiers that need to be defined to use a key store.
30/// At the moment it's just symmetric and asymmetric keys.
31pub trait KeyIds {
32    #[allow(missing_docs)]
33    type Symmetric: KeyId<KeyValue = SymmetricCryptoKey>;
34    #[allow(missing_docs)]
35    type Asymmetric: KeyId<KeyValue = AsymmetricCryptoKey>;
36    /// Signing keys are used to create detached signatures and to sign objects.
37    type Signing: KeyId<KeyValue = SigningKey>;
38}
39
40/// Just a small derive_like macro that can be used to generate the key identifier enums.
41/// Example usage:
42/// ```rust
43/// use bitwarden_crypto::key_ids;
44/// key_ids! {
45///     #[symmetric]
46///     pub enum SymmKeyId {
47///         User,
48///         Org(uuid::Uuid),
49///         #[local]
50///         Local(&'static str),
51///     }
52///
53///     #[asymmetric]
54///     pub enum AsymmKeyId {
55///         PrivateKey,
56///     }
57///
58///     #[signing]
59///     pub enum SigningKeyId {
60///        SigningKey,
61///     }
62///
63///     pub Ids => SymmKeyId, AsymmKeyId, SigningKeyId;
64/// }
65#[macro_export]
66macro_rules! key_ids {
67    ( $(
68        #[$meta_type:tt]
69        $vis:vis enum $name:ident {
70            $(
71                $( #[$variant_tag:tt] )?
72                $variant:ident $( ( $inner:ty ) )?
73            ),*
74            $(,)?
75        }
76    )+
77    $ids_vis:vis $ids_name:ident => $symm_name:ident, $asymm_name:ident, $signing_name:ident;
78    ) => {
79        $(
80            #[derive(std::fmt::Debug, Clone, Copy, std::hash::Hash, Eq, PartialEq, Ord, PartialOrd)]
81            #[allow(missing_docs)]
82            $vis enum $name { $(
83                $variant  $( ($inner) )?,
84            )* }
85
86            impl $crate::KeyId for $name {
87                type KeyValue = key_ids!(@key_type $meta_type);
88
89                fn is_local(&self) -> bool {
90                    use $name::*;
91                    match self { $(
92                        key_ids!(@variant_match $variant $( ( $inner ) )?) =>
93                            key_ids!(@variant_value $( $variant_tag )? ),
94                    )* }
95                }
96            }
97        )+
98
99        #[allow(missing_docs)]
100        $ids_vis struct $ids_name;
101        impl $crate::KeyIds for $ids_name {
102            type Symmetric = $symm_name;
103            type Asymmetric = $asymm_name;
104            type Signing = $signing_name;
105        }
106    };
107
108    ( @key_type symmetric ) => { $crate::SymmetricCryptoKey };
109    ( @key_type asymmetric ) => { $crate::AsymmetricCryptoKey };
110    ( @key_type signing ) => { $crate::SigningKey };
111
112    ( @variant_match $variant:ident ( $inner:ty ) ) => { $variant (_) };
113    ( @variant_match $variant:ident ) => { $variant };
114
115    ( @variant_value local ) => { true };
116    ( @variant_value ) => { false };
117}
118
119#[cfg(test)]
120pub(crate) mod tests {
121    use crate::{
122        traits::tests::{TestAsymmKey, TestSigningKey, TestSymmKey},
123        KeyId,
124    };
125
126    #[test]
127    fn test_local() {
128        assert!(!TestSymmKey::A(0).is_local());
129        assert!(!TestSymmKey::B((4, 10)).is_local());
130        assert!(TestSymmKey::C(8).is_local());
131
132        assert!(!TestAsymmKey::A(0).is_local());
133        assert!(!TestAsymmKey::B.is_local());
134        assert!(TestAsymmKey::C("test").is_local());
135
136        assert!(!TestSigningKey::A(0).is_local());
137        assert!(!TestSigningKey::B.is_local());
138        assert!(TestSigningKey::C("test").is_local());
139    }
140}