bc_components/encrypted_key/
encrypted_key_impl.rs1use anyhow::{Error, Result};
7use dcbor::prelude::*;
8
9use super::SSHAgentParams;
10use crate::{
11 Argon2idParams, EncryptedMessage, HKDFParams, KeyDerivation,
12 KeyDerivationMethod, KeyDerivationParams, PBKDF2Params, ScryptParams,
13 SymmetricKey, tags,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct EncryptedKey {
65 params: KeyDerivationParams,
66 encrypted_message: EncryptedMessage,
67}
68
69impl EncryptedKey {
70 pub fn lock_opt(
71 mut params: KeyDerivationParams,
72 secret: impl AsRef<[u8]>,
73 content_key: &SymmetricKey,
74 ) -> Result<Self> {
75 let encrypted_message = params.lock(content_key, secret)?;
76 Ok(Self { params, encrypted_message })
77 }
78
79 pub fn lock(
80 method: KeyDerivationMethod,
81 secret: impl AsRef<[u8]>,
82 content_key: &SymmetricKey,
83 ) -> Result<Self> {
84 match method {
85 KeyDerivationMethod::HKDF => Self::lock_opt(
86 KeyDerivationParams::HKDF(HKDFParams::new()),
87 secret,
88 content_key,
89 ),
90 KeyDerivationMethod::PBKDF2 => Self::lock_opt(
91 KeyDerivationParams::PBKDF2(PBKDF2Params::new()),
92 secret,
93 content_key,
94 ),
95 KeyDerivationMethod::Scrypt => Self::lock_opt(
96 KeyDerivationParams::Scrypt(ScryptParams::new()),
97 secret,
98 content_key,
99 ),
100 KeyDerivationMethod::Argon2id => Self::lock_opt(
101 KeyDerivationParams::Argon2id(Argon2idParams::new()),
102 secret,
103 content_key,
104 ),
105 KeyDerivationMethod::SSHAgent => Self::lock_opt(
106 KeyDerivationParams::SSHAgent(SSHAgentParams::new()),
107 secret,
108 content_key,
109 ),
110 }
111 }
112
113 pub fn encrypted_message(&self) -> &EncryptedMessage {
114 &self.encrypted_message
115 }
116
117 pub fn aad_cbor(&self) -> Result<CBOR> {
118 self.encrypted_message()
119 .aad_cbor()
120 .ok_or_else(|| Error::msg("Missing AAD CBOR in EncryptedMessage"))
121 }
122
123 pub fn unlock(&self, secret: impl AsRef<[u8]>) -> Result<SymmetricKey> {
124 let encrypted_message = &self.encrypted_message();
125 let cbor = self.aad_cbor()?;
126 let array = cbor.clone().try_into_array()?;
127 let method = array
128 .get(0)
129 .ok_or_else(|| Error::msg("Missing method"))?
130 .try_into()?;
131 match method {
132 KeyDerivationMethod::HKDF => {
133 let params = HKDFParams::try_from(cbor)?;
134 params.unlock(&encrypted_message, secret)
135 }
136 KeyDerivationMethod::PBKDF2 => {
137 let params = PBKDF2Params::try_from(cbor)?;
138 params.unlock(&encrypted_message, secret)
139 }
140 KeyDerivationMethod::Scrypt => {
141 let params = ScryptParams::try_from(cbor)?;
142 params.unlock(&encrypted_message, secret)
143 }
144 KeyDerivationMethod::Argon2id => {
145 let params = Argon2idParams::try_from(cbor)?;
146 params.unlock(&encrypted_message, secret)
147 }
148 KeyDerivationMethod::SSHAgent => {
149 let params = SSHAgentParams::try_from(cbor)?;
150 params.unlock(&encrypted_message, secret)
151 }
152 }
153 }
154
155 pub fn is_password_based(&self) -> bool {
156 self.params.is_password_based()
157 }
158
159 pub fn is_ssh_agent(&self) -> bool {
160 self.params.is_ssh_agent()
161 }
162}
163
164impl std::fmt::Display for EncryptedKey {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(f, "EncryptedKey({})", self.params)
167 }
168}
169
170impl CBORTagged for EncryptedKey {
171 fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_ENCRYPTED_KEY]) }
172}
173
174impl From<EncryptedKey> for CBOR {
175 fn from(value: EncryptedKey) -> Self { value.tagged_cbor() }
176}
177
178impl CBORTaggedEncodable for EncryptedKey {
179 fn untagged_cbor(&self) -> CBOR {
180 return self.encrypted_message().clone().into();
181 }
182}
183
184impl TryFrom<CBOR> for EncryptedKey {
185 type Error = dcbor::Error;
186
187 fn try_from(value: CBOR) -> dcbor::Result<Self> {
188 Self::from_tagged_cbor(value)
189 }
190}
191
192impl CBORTaggedDecodable for EncryptedKey {
193 fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
194 let encrypted_key: EncryptedMessage = untagged_cbor.try_into()?;
195 let params_cbor = CBOR::try_from_data(encrypted_key.aad())?;
196 let params = params_cbor.try_into()?;
197 Ok(Self { params, encrypted_message: encrypted_key })
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 fn test_secret() -> &'static [u8] { b"correct horse battery staple" }
206
207 fn test_content_key() -> SymmetricKey { SymmetricKey::new() }
208
209 #[test]
210 fn test_encrypted_key_hkdf_roundtrip() {
211 crate::register_tags();
212 let secret = test_secret();
213 let content_key = test_content_key();
214
215 let encrypted =
216 EncryptedKey::lock(KeyDerivationMethod::HKDF, secret, &content_key)
217 .unwrap();
218 assert_eq!(format!("{}", encrypted), "EncryptedKey(HKDF(SHA256))");
219 let cbor = encrypted.clone().to_cbor();
220 let encrypted2 = EncryptedKey::try_from(cbor).unwrap();
221 let decrypted = EncryptedKey::unlock(&encrypted2, secret).unwrap();
222
223 assert_eq!(content_key, decrypted);
224 }
225
226 #[test]
227 fn test_encrypted_key_pbkdf2_roundtrip() {
228 let secret = test_secret();
229 let content_key = test_content_key();
230
231 let encrypted = EncryptedKey::lock(
232 KeyDerivationMethod::PBKDF2,
233 secret,
234 &content_key,
235 )
236 .unwrap();
237 assert_eq!(format!("{}", encrypted), "EncryptedKey(PBKDF2(SHA256))");
238 let cbor = encrypted.clone().to_cbor();
239 let encrypted2 = EncryptedKey::try_from(cbor).unwrap();
240 let decrypted = EncryptedKey::unlock(&encrypted2, secret).unwrap();
241
242 assert_eq!(content_key, decrypted);
243 }
244
245 #[test]
246 fn test_encrypted_key_scrypt_roundtrip() {
247 let secret = test_secret();
248 let content_key = test_content_key();
249
250 let encrypted = EncryptedKey::lock(
251 KeyDerivationMethod::Scrypt,
252 secret,
253 &content_key,
254 )
255 .unwrap();
256 assert_eq!(format!("{}", encrypted), "EncryptedKey(Scrypt)");
257 let cbor = encrypted.clone().to_cbor();
258 let encrypted2 = EncryptedKey::try_from(cbor).unwrap();
259 let decrypted = EncryptedKey::unlock(&encrypted2, secret).unwrap();
260
261 assert_eq!(content_key, decrypted);
262 }
263
264 #[test]
265 fn test_encrypted_key_argon2id_roundtrip() {
266 let secret = test_secret();
267 let content_key = test_content_key();
268
269 let argon2id = EncryptedKey::lock(
270 KeyDerivationMethod::Argon2id,
271 secret,
272 &content_key,
273 )
274 .unwrap();
275 assert_eq!(format!("{}", argon2id), "EncryptedKey(Argon2id)");
276 let cbor = argon2id.clone().to_cbor();
277 let argon2id2 = EncryptedKey::try_from(cbor).unwrap();
278 let decrypted = EncryptedKey::unlock(&argon2id2, secret).unwrap();
279
280 assert_eq!(content_key, decrypted);
281 }
282
283 #[test]
284 fn test_encrypted_key_wrong_secret_fails() {
285 let secret = test_secret();
286 let wrong_secret = b"wrong secret";
287 let content_key = test_content_key();
288
289 let encrypted =
290 EncryptedKey::lock(KeyDerivationMethod::HKDF, secret, &content_key)
291 .unwrap();
292 let result = EncryptedKey::unlock(&encrypted, wrong_secret);
293 assert!(result.is_err());
294
295 let encrypted = EncryptedKey::lock(
296 KeyDerivationMethod::PBKDF2,
297 secret,
298 &content_key,
299 )
300 .unwrap();
301 let result = EncryptedKey::unlock(&encrypted, wrong_secret);
302 assert!(result.is_err());
303
304 let encrypted = EncryptedKey::lock(
305 KeyDerivationMethod::Scrypt,
306 secret,
307 &content_key,
308 )
309 .unwrap();
310 let result = EncryptedKey::unlock(&encrypted, wrong_secret);
311 assert!(result.is_err());
312
313 let encrypted = EncryptedKey::lock(
314 KeyDerivationMethod::Argon2id,
315 secret,
316 &content_key,
317 )
318 .unwrap();
319 let result = EncryptedKey::unlock(&encrypted, wrong_secret);
320 assert!(result.is_err());
321 }
322
323 #[test]
324 fn test_encrypted_key_params_variant() {
325 let secret = test_secret();
326 let content_key = test_content_key();
327
328 let hkdf =
329 EncryptedKey::lock(KeyDerivationMethod::HKDF, secret, &content_key)
330 .unwrap();
331 matches!(hkdf.params, KeyDerivationParams::HKDF(_));
332
333 let pbkdf2 = EncryptedKey::lock(
334 KeyDerivationMethod::PBKDF2,
335 secret,
336 &content_key,
337 )
338 .unwrap();
339 matches!(pbkdf2.params, KeyDerivationParams::PBKDF2(_));
340
341 let scrypt = EncryptedKey::lock(
342 KeyDerivationMethod::Scrypt,
343 secret,
344 &content_key,
345 )
346 .unwrap();
347 matches!(scrypt.params, KeyDerivationParams::Scrypt(_));
348
349 let argon2id = EncryptedKey::lock(
350 KeyDerivationMethod::Argon2id,
351 secret,
352 &content_key,
353 )
354 .unwrap();
355 matches!(argon2id.params, KeyDerivationParams::Argon2id(_));
356 }
357}