1use std::{marker::PhantomData, num::TryFromIntError, str::FromStr};
17
18use argon2::Params;
19use bitwarden_encoding::{FromStrVisitor, B64};
20use ciborium::{value::Integer, Value};
21use coset::{CborSerializable, CoseError, Header, HeaderBuilder};
22use rand::RngCore;
23use serde::{Deserialize, Serialize};
24use thiserror::Error;
25
26use crate::{
27 cose::{
28 extract_bytes, extract_integer, CoseExtractError, ALG_ARGON2ID13, ARGON2_ITERATIONS,
29 ARGON2_MEMORY, ARGON2_PARALLELISM, ARGON2_SALT,
30 },
31 xchacha20, BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, EncodedSymmetricKey, KeyIds,
32 KeyStoreContext, SymmetricCryptoKey,
33};
34
35const ENVELOPE_ARGON2_SALT_SIZE: usize = 16;
38const ENVELOPE_ARGON2_OUTPUT_KEY_SIZE: usize = 32;
40
41pub struct PasswordProtectedKeyEnvelope<Ids: KeyIds> {
50 _phantom: PhantomData<Ids>,
51 cose_encrypt: coset::CoseEncrypt,
52}
53
54impl<Ids: KeyIds> PasswordProtectedKeyEnvelope<Ids> {
55 pub fn seal(
60 key_to_seal: Ids::Symmetric,
61 password: &str,
62 ctx: &KeyStoreContext<Ids>,
63 ) -> Result<Self, PasswordProtectedKeyEnvelopeError> {
64 #[allow(deprecated)]
65 let key_ref = ctx
66 .dangerous_get_symmetric_key(key_to_seal)
67 .map_err(|_| PasswordProtectedKeyEnvelopeError::KeyMissingError)?;
68 Self::seal_ref(key_ref, password)
69 }
70
71 fn seal_ref(
74 key_to_seal: &SymmetricCryptoKey,
75 password: &str,
76 ) -> Result<Self, PasswordProtectedKeyEnvelopeError> {
77 Self::seal_ref_with_settings(
78 key_to_seal,
79 password,
80 &Argon2RawSettings::local_kdf_settings(),
81 )
82 }
83
84 fn seal_ref_with_settings(
88 key_to_seal: &SymmetricCryptoKey,
89 password: &str,
90 kdf_settings: &Argon2RawSettings,
91 ) -> Result<Self, PasswordProtectedKeyEnvelopeError> {
92 let envelope_key = derive_key(kdf_settings, password)
100 .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?;
101
102 let (content_format, key_to_seal_bytes) = match key_to_seal.to_encoded_raw() {
103 EncodedSymmetricKey::BitwardenLegacyKey(key_bytes) => {
104 (ContentFormat::BitwardenLegacyKey, key_bytes.to_vec())
105 }
106 EncodedSymmetricKey::CoseKey(key_bytes) => (ContentFormat::CoseKey, key_bytes.to_vec()),
107 };
108
109 let mut nonce = [0u8; crate::xchacha20::NONCE_SIZE];
110
111 let mut cose_encrypt = coset::CoseEncryptBuilder::new()
115 .add_recipient({
116 let mut recipient = coset::CoseRecipientBuilder::new()
117 .unprotected(kdf_settings.into())
118 .build();
119 recipient.protected.header.alg = Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13));
120 recipient
121 })
122 .protected(HeaderBuilder::from(content_format).build())
123 .create_ciphertext(&key_to_seal_bytes, &[], |data, aad| {
124 let ciphertext = xchacha20::encrypt_xchacha20_poly1305(&envelope_key, data, aad);
125 nonce.copy_from_slice(&ciphertext.nonce());
126 ciphertext.encrypted_bytes().to_vec()
127 })
128 .build();
129 cose_encrypt.unprotected.iv = nonce.into();
130
131 Ok(PasswordProtectedKeyEnvelope {
132 _phantom: PhantomData,
133 cose_encrypt,
134 })
135 }
136
137 pub fn unseal(
140 &self,
141 target_keyslot: Ids::Symmetric,
142 password: &str,
143 ctx: &mut KeyStoreContext<Ids>,
144 ) -> Result<Ids::Symmetric, PasswordProtectedKeyEnvelopeError> {
145 let key = self.unseal_ref(password)?;
146 #[allow(deprecated)]
147 ctx.set_symmetric_key(target_keyslot, key)
148 .map_err(|_| PasswordProtectedKeyEnvelopeError::KeyStoreError)?;
149 Ok(target_keyslot)
150 }
151
152 fn unseal_ref(
153 &self,
154 password: &str,
155 ) -> Result<SymmetricCryptoKey, PasswordProtectedKeyEnvelopeError> {
156 let recipient = self
159 .cose_encrypt
160 .recipients
161 .first()
162 .filter(|_| self.cose_encrypt.recipients.len() == 1)
163 .ok_or_else(|| {
164 PasswordProtectedKeyEnvelopeError::ParsingError(
165 "Invalid number of recipients".to_string(),
166 )
167 })?;
168
169 if recipient.protected.header.alg != Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13)) {
170 return Err(PasswordProtectedKeyEnvelopeError::ParsingError(
171 "Unknown or unsupported KDF algorithm".to_string(),
172 ));
173 }
174
175 let kdf_settings: Argon2RawSettings =
176 (&recipient.unprotected).try_into().map_err(|_| {
177 PasswordProtectedKeyEnvelopeError::ParsingError(
178 "Invalid or missing KDF parameters".to_string(),
179 )
180 })?;
181 let envelope_key = derive_key(&kdf_settings, password)
182 .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?;
183 let nonce: [u8; crate::xchacha20::NONCE_SIZE] = self
184 .cose_encrypt
185 .unprotected
186 .iv
187 .clone()
188 .try_into()
189 .map_err(|_| {
190 PasswordProtectedKeyEnvelopeError::ParsingError("Invalid IV".to_string())
191 })?;
192
193 let key_bytes = self
194 .cose_encrypt
195 .decrypt(&[], |data, aad| {
196 xchacha20::decrypt_xchacha20_poly1305(&nonce, &envelope_key, data, aad)
197 })
198 .map_err(|_| PasswordProtectedKeyEnvelopeError::WrongPassword)?;
201
202 SymmetricCryptoKey::try_from(
203 match ContentFormat::try_from(&self.cose_encrypt.protected.header).map_err(|_| {
204 PasswordProtectedKeyEnvelopeError::ParsingError(
205 "Invalid content format".to_string(),
206 )
207 })? {
208 ContentFormat::BitwardenLegacyKey => EncodedSymmetricKey::BitwardenLegacyKey(
209 BitwardenLegacyKeyBytes::from(key_bytes),
210 ),
211 ContentFormat::CoseKey => {
212 EncodedSymmetricKey::CoseKey(CoseKeyBytes::from(key_bytes))
213 }
214 _ => {
215 return Err(PasswordProtectedKeyEnvelopeError::ParsingError(
216 "Unknown or unsupported content format".to_string(),
217 ));
218 }
219 },
220 )
221 .map_err(|_| {
222 PasswordProtectedKeyEnvelopeError::ParsingError("Failed to decode key".to_string())
223 })
224 }
225
226 pub fn reseal(
228 &self,
229 password: &str,
230 new_password: &str,
231 ) -> Result<Self, PasswordProtectedKeyEnvelopeError> {
232 let unsealed = self.unseal_ref(password)?;
233 Self::seal_ref(&unsealed, new_password)
234 }
235}
236
237impl<Ids: KeyIds> From<&PasswordProtectedKeyEnvelope<Ids>> for Vec<u8> {
238 fn from(val: &PasswordProtectedKeyEnvelope<Ids>) -> Self {
239 val.cose_encrypt
240 .clone()
241 .to_vec()
242 .expect("Serialization to cose should not fail")
243 }
244}
245
246impl<Ids: KeyIds> TryFrom<&Vec<u8>> for PasswordProtectedKeyEnvelope<Ids> {
247 type Error = CoseError;
248
249 fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
250 let cose_encrypt = coset::CoseEncrypt::from_slice(value)?;
251 Ok(PasswordProtectedKeyEnvelope {
252 _phantom: PhantomData,
253 cose_encrypt,
254 })
255 }
256}
257
258impl<Ids: KeyIds> std::fmt::Debug for PasswordProtectedKeyEnvelope<Ids> {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 f.debug_struct("PasswordProtectedKeyEnvelope")
261 .field("cose_encrypt", &self.cose_encrypt)
262 .finish()
263 }
264}
265
266impl<Ids: KeyIds> FromStr for PasswordProtectedKeyEnvelope<Ids> {
267 type Err = PasswordProtectedKeyEnvelopeError;
268
269 fn from_str(s: &str) -> Result<Self, Self::Err> {
270 let data = B64::try_from(s).map_err(|_| {
271 PasswordProtectedKeyEnvelopeError::ParsingError(
272 "Invalid PasswordProtectedKeyEnvelope Base64 encoding".to_string(),
273 )
274 })?;
275 Self::try_from(&data.as_bytes().to_vec()).map_err(|_| {
276 PasswordProtectedKeyEnvelopeError::ParsingError(
277 "Failed to parse PasswordProtectedKeyEnvelope".to_string(),
278 )
279 })
280 }
281}
282
283impl<Ids: KeyIds> From<PasswordProtectedKeyEnvelope<Ids>> for String {
284 fn from(val: PasswordProtectedKeyEnvelope<Ids>) -> Self {
285 let serialized: Vec<u8> = (&val).into();
286 B64::from(serialized).to_string()
287 }
288}
289
290impl<'de, Ids: KeyIds> Deserialize<'de> for PasswordProtectedKeyEnvelope<Ids> {
291 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292 where
293 D: serde::Deserializer<'de>,
294 {
295 deserializer.deserialize_str(FromStrVisitor::new())
296 }
297}
298
299impl<Ids: KeyIds> Serialize for PasswordProtectedKeyEnvelope<Ids> {
300 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301 where
302 S: serde::Serializer,
303 {
304 let serialized: Vec<u8> = self.into();
305 serializer.serialize_str(&B64::from(serialized).to_string())
306 }
307}
308
309struct Argon2RawSettings {
314 iterations: u32,
315 memory: u32,
317 parallelism: u32,
318 salt: [u8; ENVELOPE_ARGON2_SALT_SIZE],
319}
320
321impl Argon2RawSettings {
322 fn local_kdf_settings() -> Self {
325 if cfg!(target_os = "ios") {
328 Self {
330 iterations: 6,
331 memory: 32 * 1024, parallelism: 4,
333 salt: make_salt(),
334 }
335 } else {
336 Self {
340 iterations: 3,
341 memory: 64 * 1024, parallelism: 4,
343 salt: make_salt(),
344 }
345 }
346 }
347}
348
349impl From<&Argon2RawSettings> for Header {
350 fn from(settings: &Argon2RawSettings) -> Header {
351 let builder = HeaderBuilder::new()
352 .value(ARGON2_ITERATIONS, Integer::from(settings.iterations).into())
353 .value(ARGON2_MEMORY, Integer::from(settings.memory).into())
354 .value(
355 ARGON2_PARALLELISM,
356 Integer::from(settings.parallelism).into(),
357 )
358 .value(ARGON2_SALT, Value::from(settings.salt.to_vec()));
359
360 let mut header = builder.build();
361 header.alg = Some(coset::Algorithm::PrivateUse(ALG_ARGON2ID13));
362 header
363 }
364}
365
366impl TryInto<Params> for &Argon2RawSettings {
367 type Error = PasswordProtectedKeyEnvelopeError;
368
369 fn try_into(self) -> Result<Params, PasswordProtectedKeyEnvelopeError> {
370 Params::new(
371 self.memory,
372 self.iterations,
373 self.parallelism,
374 Some(ENVELOPE_ARGON2_OUTPUT_KEY_SIZE),
375 )
376 .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)
377 }
378}
379
380impl TryInto<Argon2RawSettings> for &Header {
381 type Error = PasswordProtectedKeyEnvelopeError;
382
383 fn try_into(self) -> Result<Argon2RawSettings, PasswordProtectedKeyEnvelopeError> {
384 Ok(Argon2RawSettings {
385 iterations: extract_integer(self, ARGON2_ITERATIONS, "iterations")?.try_into()?,
386 memory: extract_integer(self, ARGON2_MEMORY, "memory")?.try_into()?,
387 parallelism: extract_integer(self, ARGON2_PARALLELISM, "parallelism")?.try_into()?,
388 salt: extract_bytes(self, ARGON2_SALT, "salt")?
389 .try_into()
390 .map_err(|_| {
391 PasswordProtectedKeyEnvelopeError::ParsingError(
392 "Invalid Argon2 salt".to_string(),
393 )
394 })?,
395 })
396 }
397}
398
399fn make_salt() -> [u8; ENVELOPE_ARGON2_SALT_SIZE] {
400 let mut salt = [0u8; ENVELOPE_ARGON2_SALT_SIZE];
401 rand::thread_rng().fill_bytes(&mut salt);
402 salt
403}
404
405fn derive_key(
406 argon2_settings: &Argon2RawSettings,
407 password: &str,
408) -> Result<[u8; ENVELOPE_ARGON2_OUTPUT_KEY_SIZE], PasswordProtectedKeyEnvelopeError> {
409 use argon2::*;
410
411 let mut hash = [0u8; ENVELOPE_ARGON2_OUTPUT_KEY_SIZE];
412 Argon2::new(
413 Algorithm::Argon2id,
414 Version::V0x13,
415 argon2_settings.try_into()?,
416 )
417 .hash_password_into(password.as_bytes(), &argon2_settings.salt, &mut hash)
418 .map_err(|_| PasswordProtectedKeyEnvelopeError::KdfError)?;
419
420 Ok(hash)
421}
422
423#[derive(Debug, Error)]
425pub enum PasswordProtectedKeyEnvelopeError {
426 #[error("Wrong password")]
428 WrongPassword,
429 #[error("Parsing error {0}")]
431 ParsingError(String),
432 #[error("Kdf error")]
435 KdfError,
436 #[error("Key missing error")]
438 KeyMissingError,
439 #[error("Could not write to key store")]
441 KeyStoreError,
442}
443
444impl From<CoseExtractError> for PasswordProtectedKeyEnvelopeError {
445 fn from(err: CoseExtractError) -> Self {
446 let CoseExtractError::MissingValue(label) = err;
447 PasswordProtectedKeyEnvelopeError::ParsingError(format!("Missing value for {}", label))
448 }
449}
450
451impl From<TryFromIntError> for PasswordProtectedKeyEnvelopeError {
452 fn from(err: TryFromIntError) -> Self {
453 PasswordProtectedKeyEnvelopeError::ParsingError(format!("Invalid integer: {}", err))
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use crate::{
461 traits::tests::{TestIds, TestSymmKey},
462 KeyStore,
463 };
464
465 const TEST_UNSEALED_COSEKEY_ENCODED: &[u8] = &[
466 165, 1, 4, 2, 80, 63, 208, 189, 183, 204, 37, 72, 170, 179, 236, 190, 208, 22, 65, 227,
467 183, 3, 58, 0, 1, 17, 111, 4, 132, 3, 4, 5, 6, 32, 88, 32, 88, 25, 68, 85, 205, 28, 133,
468 28, 90, 147, 160, 145, 48, 3, 178, 184, 30, 11, 122, 132, 64, 59, 51, 233, 191, 117, 159,
469 117, 23, 168, 248, 36, 1,
470 ];
471 const TESTVECTOR_COSEKEY_ENVELOPE: &[u8] = &[
472 132, 68, 161, 3, 24, 101, 161, 5, 88, 24, 1, 31, 58, 230, 10, 92, 195, 233, 212, 7, 166,
473 252, 67, 115, 221, 58, 3, 191, 218, 188, 181, 192, 28, 11, 88, 84, 141, 183, 137, 167, 166,
474 161, 33, 82, 30, 255, 23, 10, 179, 149, 88, 24, 39, 60, 74, 232, 133, 44, 90, 98, 117, 31,
475 41, 69, 251, 76, 250, 141, 229, 83, 191, 6, 237, 107, 127, 93, 238, 110, 49, 125, 201, 37,
476 162, 120, 157, 32, 116, 195, 208, 143, 83, 254, 223, 93, 97, 158, 0, 24, 95, 197, 249, 35,
477 240, 3, 20, 71, 164, 97, 180, 29, 203, 69, 31, 151, 249, 244, 197, 91, 101, 174, 129, 131,
478 71, 161, 1, 58, 0, 1, 21, 87, 165, 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, 3, 58, 0, 1, 21,
479 90, 26, 0, 1, 0, 0, 58, 0, 1, 21, 91, 4, 58, 0, 1, 21, 88, 80, 165, 253, 56, 243, 255, 54,
480 246, 252, 231, 230, 33, 252, 49, 175, 1, 111, 246,
481 ];
482 const TEST_UNSEALED_LEGACYKEY_ENCODED: &[u8] = &[
483 135, 114, 97, 155, 115, 209, 215, 224, 175, 159, 231, 208, 15, 244, 40, 171, 239, 137, 57,
484 98, 207, 167, 231, 138, 145, 254, 28, 136, 236, 60, 23, 163, 4, 246, 219, 117, 104, 246,
485 86, 10, 152, 52, 90, 85, 58, 6, 70, 39, 111, 128, 93, 145, 143, 180, 77, 129, 178, 242, 82,
486 72, 57, 61, 192, 64,
487 ];
488 const TESTVECTOR_LEGACYKEY_ENVELOPE: &[u8] = &[
489 132, 88, 38, 161, 3, 120, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120,
490 46, 98, 105, 116, 119, 97, 114, 100, 101, 110, 46, 108, 101, 103, 97, 99, 121, 45, 107,
491 101, 121, 161, 5, 88, 24, 218, 72, 22, 79, 149, 30, 12, 36, 180, 212, 44, 21, 167, 208,
492 214, 221, 7, 91, 178, 12, 104, 17, 45, 219, 88, 80, 114, 38, 14, 165, 85, 229, 103, 108,
493 17, 175, 41, 43, 203, 175, 119, 125, 227, 127, 163, 214, 213, 138, 12, 216, 163, 204, 38,
494 222, 47, 11, 44, 231, 239, 170, 63, 8, 249, 56, 102, 18, 134, 34, 232, 193, 44, 19, 228,
495 17, 187, 199, 238, 187, 2, 13, 30, 112, 103, 110, 5, 31, 238, 58, 4, 24, 19, 239, 135, 57,
496 206, 190, 144, 83, 128, 204, 59, 155, 21, 80, 180, 34, 129, 131, 71, 161, 1, 58, 0, 1, 21,
497 87, 165, 1, 58, 0, 1, 21, 87, 58, 0, 1, 21, 89, 3, 58, 0, 1, 21, 90, 26, 0, 1, 0, 0, 58, 0,
498 1, 21, 91, 4, 58, 0, 1, 21, 88, 80, 212, 91, 185, 112, 92, 177, 108, 33, 182, 202, 26, 141,
499 11, 133, 95, 235, 246,
500 ];
501
502 const TESTVECTOR_PASSWORD: &str = "test_password";
503
504 #[test]
505 fn test_testvector_cosekey() {
506 let key_store = KeyStore::<TestIds>::default();
507 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
508 let envelope =
509 PasswordProtectedKeyEnvelope::try_from(&TESTVECTOR_COSEKEY_ENVELOPE.to_vec())
510 .expect("Key envelope should be valid");
511 envelope
512 .unseal(TestSymmKey::A(0), TESTVECTOR_PASSWORD, &mut ctx)
513 .expect("Unsealing should succeed");
514 #[allow(deprecated)]
515 let unsealed_key = ctx
516 .dangerous_get_symmetric_key(TestSymmKey::A(0))
517 .expect("Key should exist in the key store");
518 assert_eq!(
519 unsealed_key.to_encoded().to_vec(),
520 TEST_UNSEALED_COSEKEY_ENCODED
521 );
522 }
523
524 #[test]
525 fn test_testvector_legacykey() {
526 let key_store = KeyStore::<TestIds>::default();
527 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
528 let envelope =
529 PasswordProtectedKeyEnvelope::try_from(&TESTVECTOR_LEGACYKEY_ENVELOPE.to_vec())
530 .expect("Key envelope should be valid");
531 envelope
532 .unseal(TestSymmKey::A(0), TESTVECTOR_PASSWORD, &mut ctx)
533 .expect("Unsealing should succeed");
534 #[allow(deprecated)]
535 let unsealed_key = ctx
536 .dangerous_get_symmetric_key(TestSymmKey::A(0))
537 .expect("Key should exist in the key store");
538 assert_eq!(
539 unsealed_key.to_encoded().to_vec(),
540 TEST_UNSEALED_LEGACYKEY_ENCODED
541 );
542 }
543
544 #[test]
545 fn test_make_envelope() {
546 let key_store = KeyStore::<TestIds>::default();
547 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
548 let test_key = ctx.make_cose_symmetric_key(TestSymmKey::A(0)).unwrap();
549
550 let password = "test_password";
551
552 let envelope = PasswordProtectedKeyEnvelope::seal(test_key, password, &ctx).unwrap();
554 let serialized: Vec<u8> = (&envelope).into();
555
556 let deserialized: PasswordProtectedKeyEnvelope<TestIds> =
558 PasswordProtectedKeyEnvelope::try_from(&serialized).unwrap();
559 deserialized
560 .unseal(TestSymmKey::A(1), password, &mut ctx)
561 .unwrap();
562
563 #[allow(deprecated)]
565 let unsealed_key = ctx
566 .dangerous_get_symmetric_key(TestSymmKey::A(1))
567 .expect("Key should exist in the key store");
568
569 #[allow(deprecated)]
570 let key_before_sealing = ctx
571 .dangerous_get_symmetric_key(test_key)
572 .expect("Key should exist in the key store");
573
574 assert_eq!(unsealed_key, key_before_sealing);
575 }
576
577 #[test]
578 fn test_make_envelope_legacy_key() {
579 let key_store = KeyStore::<TestIds>::default();
580 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
581 let test_key = ctx.generate_symmetric_key(TestSymmKey::A(0)).unwrap();
582
583 let password = "test_password";
584
585 let envelope = PasswordProtectedKeyEnvelope::seal(test_key, password, &ctx).unwrap();
587 let serialized: Vec<u8> = (&envelope).into();
588
589 let deserialized: PasswordProtectedKeyEnvelope<TestIds> =
591 PasswordProtectedKeyEnvelope::try_from(&serialized).unwrap();
592 deserialized
593 .unseal(TestSymmKey::A(1), password, &mut ctx)
594 .unwrap();
595
596 #[allow(deprecated)]
598 let unsealed_key = ctx
599 .dangerous_get_symmetric_key(TestSymmKey::A(1))
600 .expect("Key should exist in the key store");
601
602 #[allow(deprecated)]
603 let key_before_sealing = ctx
604 .dangerous_get_symmetric_key(test_key)
605 .expect("Key should exist in the key store");
606
607 assert_eq!(unsealed_key, key_before_sealing);
608 }
609
610 #[test]
611 fn test_reseal_envelope() {
612 let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
613 let password = "test_password";
614 let new_password = "new_test_password";
615
616 let envelope: PasswordProtectedKeyEnvelope<TestIds> =
618 PasswordProtectedKeyEnvelope::seal_ref(&key, password).expect("Sealing should work");
619
620 let envelope = envelope
622 .reseal(password, new_password)
623 .expect("Resealing should work");
624 let unsealed = envelope
625 .unseal_ref(new_password)
626 .expect("Unsealing should work");
627
628 assert_eq!(unsealed, key);
630 }
631
632 #[test]
633 fn test_wrong_password() {
634 let key_store = KeyStore::<TestIds>::default();
635 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
636 let test_key = ctx.make_cose_symmetric_key(TestSymmKey::A(0)).unwrap();
637
638 let password = "test_password";
639 let wrong_password = "wrong_password";
640
641 let envelope = PasswordProtectedKeyEnvelope::seal(test_key, password, &ctx).unwrap();
643
644 let deserialized: PasswordProtectedKeyEnvelope<TestIds> =
646 PasswordProtectedKeyEnvelope::try_from(&(&envelope).into()).unwrap();
647 assert!(matches!(
648 deserialized.unseal(TestSymmKey::A(1), wrong_password, &mut ctx),
649 Err(PasswordProtectedKeyEnvelopeError::WrongPassword)
650 ));
651 }
652}