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#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
11pub enum KeyReference {
12 MobileSecureElement,
14
15 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#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
39pub struct KeyParams {
40 #[serde(default, rename = "meta", skip_serializing_if = "Option::is_none")]
42 pub metadata: Option<String>,
43
44 #[serde(default, rename = "ref", skip_serializing_if = "Option::is_none")]
46 pub reference: Option<KeyReference>,
47
48 #[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#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct KeyEntry {
83 pub(crate) name: String,
85 pub(crate) params: KeyParams,
87 pub(crate) alg: Option<String>,
89 pub(crate) thumbprints: Vec<String>,
91 pub(crate) tags: Vec<EntryTag>,
93}
94
95impl KeyEntry {
96 pub fn algorithm(&self) -> Option<&str> {
98 self.alg.as_ref().map(String::as_ref)
99 }
100
101 pub fn metadata(&self) -> Option<&str> {
103 self.params.metadata.as_ref().map(String::as_ref)
104 }
105
106 pub fn name(&self) -> &str {
108 self.name.as_str()
109 }
110
111 pub fn tags_as_slice(&self) -> &[EntryTag] {
113 self.tags.as_slice()
114 }
115
116 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 tags.remove(idx).into_value();
140 }
141 }
142 thumbprints.sort();
144 tags.sort();
145 Ok(Self {
146 name: entry.name,
147 params,
148 alg,
149 thumbprints,
150 tags,
151 })
152 }
153
154 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}