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