bitwarden_crypto/traits/
key_id.rs

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