aries_askar/kms/
entry.rs

1use super::local_key::LocalKey;
2use crate::{
3    crypto::{alg::AnyKey, alg::KeyAlg, buffer::SecretBytes, jwk::FromJwk},
4    entry::{Entry, EntryTag},
5    error::Error,
6};
7use std::str::FromStr;
8
9/// Key reference variant
10#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
11pub enum KeyReference {
12    /// Stored in a mobile secure element
13    MobileSecureElement,
14
15    /// Any other reference as fallback
16    Any(String),
17}
18
19impl From<&str> for KeyReference {
20    fn from(value: &str) -> Self {
21        match value {
22            "mobile_secure_element" => Self::MobileSecureElement,
23            any => Self::Any(String::from(any)),
24        }
25    }
26}
27
28impl From<KeyReference> for String {
29    fn from(key_reference: KeyReference) -> Self {
30        match key_reference {
31            KeyReference::MobileSecureElement => String::from("mobile_secure_element"),
32            KeyReference::Any(s) => s,
33        }
34    }
35}
36
37/// Parameters defining a stored key
38#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
39pub struct KeyParams {
40    /// Associated key metadata
41    #[serde(default, rename = "meta", skip_serializing_if = "Option::is_none")]
42    pub metadata: Option<String>,
43
44    /// An optional external reference for the key
45    #[serde(default, rename = "ref", skip_serializing_if = "Option::is_none")]
46    pub reference: Option<KeyReference>,
47
48    /// The associated key data
49    /// - Stored as a JWK for software-backed keys
50    /// - Stored as a key id for hardware-backed keys
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub data: Option<SecretBytes>,
53}
54
55impl KeyParams {
56    pub(crate) fn to_bytes(&self) -> Result<SecretBytes, Error> {
57        let mut bytes = Vec::new();
58        ciborium::into_writer(self, &mut bytes)
59            .map_err(|e| err_msg!(Unexpected, "Error serializing key params: {}", e))?;
60
61        Ok(SecretBytes::from(bytes))
62    }
63
64    pub(crate) fn to_id(&self) -> Result<String, Error> {
65        self.data
66            .as_ref()
67            .and_then(|d| d.as_opt_str().map(ToOwned::to_owned))
68            .ok_or(err_msg!(
69                Input,
70                "Could not convert key data to string for id"
71            ))
72    }
73
74    pub(crate) fn from_slice(params: &[u8]) -> Result<KeyParams, Error> {
75        ciborium::from_reader(params)
76            .map_err(|e| err_msg!(Unexpected, "Error deserializing key params: {}", e))
77    }
78}
79
80/// A stored key entry
81#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct KeyEntry {
83    /// The key entry identifier
84    pub(crate) name: String,
85    /// The parameters defining the key
86    pub(crate) params: KeyParams,
87    /// Key algorithm
88    pub(crate) alg: Option<String>,
89    /// Thumbprints for the key
90    pub(crate) thumbprints: Vec<String>,
91    /// Thumbprints for the key
92    pub(crate) tags: Vec<EntryTag>,
93}
94
95impl KeyEntry {
96    /// Accessor for the key identity
97    pub fn algorithm(&self) -> Option<&str> {
98        self.alg.as_ref().map(String::as_ref)
99    }
100
101    /// Accessor for the stored key metadata
102    pub fn metadata(&self) -> Option<&str> {
103        self.params.metadata.as_ref().map(String::as_ref)
104    }
105
106    /// Accessor for the key identity
107    pub fn name(&self) -> &str {
108        self.name.as_str()
109    }
110
111    /// Accessor for the key tags
112    pub fn tags_as_slice(&self) -> &[EntryTag] {
113        self.tags.as_slice()
114    }
115
116    /// Determine if a key entry refers to a local or external key
117    pub fn is_local(&self) -> bool {
118        self.params.reference.is_none()
119    }
120
121    pub(crate) fn from_entry(entry: Entry) -> Result<Self, Error> {
122        let params = KeyParams::from_slice(&entry.value)?;
123        let mut alg = None;
124        let mut thumbprints = Vec::new();
125        let mut tags = entry.tags;
126        let mut idx = 0;
127        while idx < tags.len() {
128            let tag = &mut tags[idx];
129            let name = tag.name();
130            if name.starts_with("user:") {
131                tag.update_name(|tag| tag.replace_range(0..5, ""));
132                idx += 1;
133            } else if name == "alg" {
134                alg.replace(tags.remove(idx).into_value());
135            } else if name == "thumb" {
136                thumbprints.push(tags.remove(idx).into_value());
137            } else {
138                // unrecognized tag
139                tags.remove(idx).into_value();
140            }
141        }
142        // keep sorted for checking equality
143        thumbprints.sort();
144        tags.sort();
145        Ok(Self {
146            name: entry.name,
147            params,
148            alg,
149            thumbprints,
150            tags,
151        })
152    }
153
154    /// Create a local key instance from this key storage entry
155    pub fn load_local_key(&self) -> Result<LocalKey, Error> {
156        if let Some(key_data) = self.params.data.as_ref() {
157            match &self.params.reference {
158                Some(KeyReference::MobileSecureElement) => {
159                    let id = self.params.to_id()?;
160                    let alg = self
161                        .alg
162                        .as_ref()
163                        .ok_or(err_msg!(Input, "Algorithm is required to get key by id"))?;
164                    let alg = KeyAlg::from_str(alg)?;
165                    Ok(LocalKey::from_id(alg, &id)?)
166                }
167                _ => Ok(LocalKey {
168                    inner: Box::<AnyKey>::from_jwk_slice(key_data.as_ref())?,
169                    ephemeral: false,
170                }),
171            }
172        } else {
173            Err(err_msg!("Missing key data"))
174        }
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn key_params_roundtrip() {
184        let params = KeyParams {
185            metadata: Some("meta".to_string()),
186            reference: None,
187            data: Some(SecretBytes::from(vec![0, 0, 0, 0])),
188        };
189        let enc_params = params.to_bytes().unwrap();
190        let p2 = KeyParams::from_slice(&enc_params).unwrap();
191        assert_eq!(p2, params);
192    }
193}