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