1use std::{num::TryFromIntError, str::FromStr};
26
27use bitwarden_encoding::{B64, FromStrVisitor};
28use bitwarden_sensitive_value::ExposeSensitive;
29use ciborium::Value;
30use coset::{CborSerializable, CoseError, Header, HeaderBuilder, iana};
31use rand::Rng;
32use serde::{Deserialize, Serialize};
33use thiserror::Error;
34#[cfg(feature = "wasm")]
35use wasm_bindgen::convert::{FromWasmAbi, IntoWasmAbi, OptionFromWasmAbi};
36
37use crate::{
38 ContentFormat, EncodedSymmetricKey, KeySlotIds, KeyStoreContext, SymmetricCryptoKey,
39 cose::{
40 ContentNamespace, CoseExtractError, SafeObjectNamespace, extract_bytes,
41 symmetric::{
42 CoseAlgorithmPolicy, CoseContentEncryptionAlgorithm, decrypt_cose, encrypt_cose,
43 },
44 },
45 keys::KeyId,
46 safe::{
47 DecodeSealedKeyError, HighEntropySecret, decode_sealed_symmetric_key, extract_key_id,
48 extract_single_recipient,
49 helpers::{debug_fmt, set_safe_namespaces, validate_safe_namespaces},
50 set_contained_key_id,
51 },
52};
53
54const HKDF_ALGORITHM: coset::iana::Algorithm = iana::Algorithm::Direct_HKDF_SHA_256;
57const HKDF_SALT_LABEL: i64 = iana::HeaderAlgorithmParameter::Salt as i64;
59const ENVELOPE_HKDF_SALT_SIZE: usize = 32;
62const ENVELOPE_HKDF_OUTPUT_KEY_SIZE: usize = 32;
64
65#[derive(Clone)]
75pub struct SecretProtectedKeyEnvelope {
76 cose_encrypt: coset::CoseEncrypt,
77}
78
79impl SecretProtectedKeyEnvelope {
80 pub fn seal<Ids: KeySlotIds>(
88 key_to_seal: Ids::Symmetric,
89 secret: &HighEntropySecret,
90 namespace: SecretProtectedKeyEnvelopeNamespace,
91 ctx: &KeyStoreContext<Ids>,
92 ) -> Result<Self, SecretProtectedKeyEnvelopeError> {
93 let key_ref = ctx
94 .get_symmetric_key(key_to_seal)
95 .map_err(|_| SecretProtectedKeyEnvelopeError::KeyMissing)?;
96 Self::seal_ref(key_ref, secret, namespace)
97 }
98
99 fn seal_ref(
102 key_to_seal: &SymmetricCryptoKey,
103 secret: &HighEntropySecret,
104 namespace: SecretProtectedKeyEnvelopeNamespace,
105 ) -> Result<Self, SecretProtectedKeyEnvelopeError> {
106 Self::seal_ref_with_settings(key_to_seal, secret, &HkdfSettings::new(), namespace)
107 }
108
109 fn seal_ref_with_settings(
112 key_to_seal: &SymmetricCryptoKey,
113 secret: &HighEntropySecret,
114 hkdf_settings: &HkdfSettings,
115 namespace: SecretProtectedKeyEnvelopeNamespace,
116 ) -> Result<Self, SecretProtectedKeyEnvelopeError> {
117 let cek = derive_cek(
125 hkdf_settings,
126 secret.as_bytes().expose_owned(),
127 iana::Algorithm::A256GCM,
128 )?;
129
130 let (content_format, key_to_seal_bytes) = match key_to_seal.to_encoded_raw() {
131 EncodedSymmetricKey::BitwardenLegacyKey(key_bytes) => {
132 (ContentFormat::BitwardenLegacyKey, key_bytes.to_vec())
133 }
134 EncodedSymmetricKey::CoseKey(key_bytes) => (ContentFormat::CoseKey, key_bytes.to_vec()),
135 };
136
137 let protected_header = {
138 let mut header = HeaderBuilder::from(content_format).build();
139 set_contained_key_id(&mut header, key_to_seal.key_id());
140 set_safe_namespaces(
141 &mut header,
142 SafeObjectNamespace::SecretProtectedKeyEnvelope,
143 namespace,
144 );
145 header
146 };
147
148 let builder = coset::CoseEncryptBuilder::new().add_recipient(
152 coset::CoseRecipientBuilder::new()
153 .unprotected(hkdf_settings.into())
154 .build(),
155 );
156 let cose_encrypt = encrypt_cose(
159 CoseContentEncryptionAlgorithm::Aes256Gcm,
160 builder,
161 protected_header,
162 &key_to_seal_bytes,
163 &cek,
164 )
165 .map_err(|_| SecretProtectedKeyEnvelopeError::Kdf)?;
166
167 Ok(SecretProtectedKeyEnvelope { cose_encrypt })
168 }
169
170 pub fn unseal<Ids: KeySlotIds>(
173 &self,
174 secret: &HighEntropySecret,
175 namespace: SecretProtectedKeyEnvelopeNamespace,
176 ctx: &mut KeyStoreContext<Ids>,
177 ) -> Result<Ids::Symmetric, SecretProtectedKeyEnvelopeError> {
178 let key = self.unseal_ref(secret, namespace)?;
179 Ok(ctx.add_local_symmetric_key(key))
180 }
181
182 fn unseal_ref(
183 &self,
184 secret: &HighEntropySecret,
185 content_namespace: SecretProtectedKeyEnvelopeNamespace,
186 ) -> Result<SymmetricCryptoKey, SecretProtectedKeyEnvelopeError> {
187 let recipient = extract_single_recipient(&self.cose_encrypt).map_err(|_| {
190 SecretProtectedKeyEnvelopeError::Parsing("Invalid number of recipients".to_string())
191 })?;
192
193 if recipient.unprotected.alg
194 != Some(coset::RegisteredLabelWithPrivate::Assigned(HKDF_ALGORITHM))
195 {
196 return Err(SecretProtectedKeyEnvelopeError::Parsing(
197 "Unknown or unsupported KDF algorithm".to_string(),
198 ));
199 }
200
201 validate_safe_namespaces(
202 &self.cose_encrypt.protected.header,
203 SafeObjectNamespace::SecretProtectedKeyEnvelope,
204 content_namespace,
205 )
206 .map_err(|_| SecretProtectedKeyEnvelopeError::InvalidNamespace)?;
207
208 let kdf_settings: HkdfSettings = (&recipient.unprotected).try_into().map_err(|_| {
209 SecretProtectedKeyEnvelopeError::Parsing(
210 "Invalid or missing KDF parameters".to_string(),
211 )
212 })?;
213 let content_alg = content_encryption_algorithm(&self.cose_encrypt.protected.header)?;
217 let cek = derive_cek(&kdf_settings, secret.as_bytes().expose_owned(), content_alg)?;
220
221 let key_bytes = decrypt_cose(
226 &self.cose_encrypt,
227 CoseAlgorithmPolicy::RequireProtectedHeaderAlgorithm,
228 &cek,
229 )
230 .map_err(|_| SecretProtectedKeyEnvelopeError::WrongSecret)?;
231
232 decode_sealed_symmetric_key(&self.cose_encrypt.protected.header, key_bytes).map_err(|e| {
233 match e {
234 DecodeSealedKeyError::InvalidContentFormat => {
235 SecretProtectedKeyEnvelopeError::Parsing("Invalid content format".to_string())
236 }
237 DecodeSealedKeyError::UnsupportedContentFormat => {
238 SecretProtectedKeyEnvelopeError::Parsing(
239 "Unknown or unsupported content format".to_string(),
240 )
241 }
242 DecodeSealedKeyError::InvalidKey => {
243 SecretProtectedKeyEnvelopeError::Parsing("Failed to decode key".to_string())
244 }
245 }
246 })
247 }
248
249 pub fn reseal(
251 &self,
252 secret: &HighEntropySecret,
253 new_secret: &HighEntropySecret,
254 namespace: SecretProtectedKeyEnvelopeNamespace,
255 ) -> Result<Self, SecretProtectedKeyEnvelopeError> {
256 let unsealed = self.unseal_ref(secret, namespace)?;
257 Self::seal_ref(&unsealed, new_secret, namespace)
258 }
259
260 pub fn contained_key_id(&self) -> Result<Option<KeyId>, SecretProtectedKeyEnvelopeError> {
263 extract_key_id(&self.cose_encrypt.protected.header)
264 .map_err(|_| SecretProtectedKeyEnvelopeError::Parsing("Invalid key id".to_string()))
265 }
266}
267
268impl From<&SecretProtectedKeyEnvelope> for Vec<u8> {
269 fn from(val: &SecretProtectedKeyEnvelope) -> Self {
270 val.cose_encrypt
271 .clone()
272 .to_vec()
273 .expect("Serialization to cose should not fail")
274 }
275}
276
277impl TryFrom<&Vec<u8>> for SecretProtectedKeyEnvelope {
278 type Error = CoseError;
279
280 fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
281 let cose_encrypt = coset::CoseEncrypt::from_slice(value)?;
282 Ok(SecretProtectedKeyEnvelope { cose_encrypt })
283 }
284}
285
286impl std::fmt::Debug for SecretProtectedKeyEnvelope {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 let mut s = f.debug_struct("SecretProtectedKeyEnvelope");
289
290 debug_fmt::<SecretProtectedKeyEnvelopeNamespace>(
291 &mut s,
292 &self.cose_encrypt.protected.header,
293 );
294
295 if let Ok(Some(key_id)) = self.contained_key_id() {
296 s.field("contained_key_id", &key_id);
297 }
298
299 s.finish()
300 }
301}
302
303impl FromStr for SecretProtectedKeyEnvelope {
304 type Err = SecretProtectedKeyEnvelopeError;
305
306 fn from_str(s: &str) -> Result<Self, Self::Err> {
307 let data = B64::try_from(s).map_err(|_| {
308 SecretProtectedKeyEnvelopeError::Parsing(
309 "Invalid SecretProtectedKeyEnvelope Base64 encoding".to_string(),
310 )
311 })?;
312 Self::try_from(&data.into_bytes()).map_err(|_| {
313 SecretProtectedKeyEnvelopeError::Parsing(
314 "Failed to parse SecretProtectedKeyEnvelope".to_string(),
315 )
316 })
317 }
318}
319
320impl From<SecretProtectedKeyEnvelope> for String {
321 fn from(val: SecretProtectedKeyEnvelope) -> Self {
322 let serialized: Vec<u8> = (&val).into();
323 B64::from(serialized).to_string()
324 }
325}
326
327impl<'de> Deserialize<'de> for SecretProtectedKeyEnvelope {
328 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
329 where
330 D: serde::Deserializer<'de>,
331 {
332 deserializer.deserialize_str(FromStrVisitor::new())
333 }
334}
335
336impl Serialize for SecretProtectedKeyEnvelope {
337 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
338 where
339 S: serde::Serializer,
340 {
341 let serialized: Vec<u8> = self.into();
342 serializer.serialize_str(&B64::from(serialized).to_string())
343 }
344}
345
346struct HkdfSettings {
349 alg: iana::Algorithm,
350 salt: [u8; ENVELOPE_HKDF_SALT_SIZE],
351}
352
353impl HkdfSettings {
354 fn new() -> Self {
356 Self {
357 alg: HKDF_ALGORITHM,
358 salt: make_salt(),
359 }
360 }
361}
362
363impl From<&HkdfSettings> for Header {
364 fn from(settings: &HkdfSettings) -> Header {
365 HeaderBuilder::new()
366 .value(HKDF_SALT_LABEL, Value::from(settings.salt.to_vec()))
367 .algorithm(settings.alg)
368 .build()
369 }
370}
371
372impl TryInto<HkdfSettings> for &Header {
373 type Error = SecretProtectedKeyEnvelopeError;
374
375 fn try_into(self) -> Result<HkdfSettings, SecretProtectedKeyEnvelopeError> {
376 Ok(HkdfSettings {
377 alg: match self.alg {
378 Some(coset::RegisteredLabelWithPrivate::Assigned(alg)) => alg,
379 _ => {
380 return Err(SecretProtectedKeyEnvelopeError::Parsing(
381 "Missing KDF algorithm".to_string(),
382 ));
383 }
384 },
385 salt: extract_bytes(self, HKDF_SALT_LABEL, "salt")?
386 .try_into()
387 .map_err(|_| {
388 SecretProtectedKeyEnvelopeError::Parsing("Invalid HKDF salt".to_string())
389 })?,
390 })
391 }
392}
393
394fn make_salt() -> [u8; ENVELOPE_HKDF_SALT_SIZE] {
395 let mut salt = [0u8; ENVELOPE_HKDF_SALT_SIZE];
396 bitwarden_random::rng().fill_bytes(&mut salt);
397 salt
398}
399
400fn kdf_context_info(alg: iana::Algorithm) -> Result<Vec<u8>, SecretProtectedKeyEnvelopeError> {
417 let empty_party_info = || Value::Array(vec![Value::Null, Value::Null, Value::Null]);
418 let context = Value::Array(vec![
419 Value::Integer((alg as i64).into()),
422 empty_party_info(),
423 empty_party_info(),
424 Value::Array(vec![
425 Value::Integer((ENVELOPE_HKDF_OUTPUT_KEY_SIZE as u64 * 8).into()),
427 Value::Bytes(vec![]),
429 ]),
430 ]);
431
432 let mut info = Vec::new();
433 ciborium::into_writer(&context, &mut info).map_err(|_| SecretProtectedKeyEnvelopeError::Kdf)?;
434 Ok(info)
435}
436
437fn content_encryption_algorithm(
441 header: &Header,
442) -> Result<iana::Algorithm, SecretProtectedKeyEnvelopeError> {
443 match header.alg {
444 Some(coset::RegisteredLabelWithPrivate::Assigned(alg)) => Ok(alg),
445 _ => Err(SecretProtectedKeyEnvelopeError::Parsing(
446 "Missing or unsupported content encryption algorithm".to_string(),
447 )),
448 }
449}
450
451fn derive_cek(
452 hkdf_settings: &HkdfSettings,
453 secret: &[u8],
454 alg: iana::Algorithm,
455) -> Result<[u8; ENVELOPE_HKDF_OUTPUT_KEY_SIZE], SecretProtectedKeyEnvelopeError> {
456 let info = kdf_context_info(alg)?;
461 let hkdf = hkdf::Hkdf::<sha2::Sha256>::new(Some(&hkdf_settings.salt), secret);
462 let mut key = [0u8; ENVELOPE_HKDF_OUTPUT_KEY_SIZE];
463 hkdf.expand(&info, &mut key)
464 .map_err(|_| SecretProtectedKeyEnvelopeError::Kdf)?;
465 Ok(key)
466}
467
468#[derive(Debug, Error)]
470pub enum SecretProtectedKeyEnvelopeError {
471 #[error("Wrong secret")]
473 WrongSecret,
474 #[error("Parsing error {0}")]
476 Parsing(String),
477 #[error("Kdf error")]
479 Kdf,
480 #[error("Key missing error")]
482 KeyMissing,
483 #[error("Could not write to key store")]
485 KeyStore,
486 #[error("Invalid namespace")]
488 InvalidNamespace,
489}
490
491impl From<CoseExtractError> for SecretProtectedKeyEnvelopeError {
492 fn from(err: CoseExtractError) -> Self {
493 let CoseExtractError::MissingValue(label) = err;
494 SecretProtectedKeyEnvelopeError::Parsing(format!("Missing value for {}", label))
495 }
496}
497
498impl From<TryFromIntError> for SecretProtectedKeyEnvelopeError {
499 fn from(err: TryFromIntError) -> Self {
500 SecretProtectedKeyEnvelopeError::Parsing(format!("Invalid integer: {}", err))
501 }
502}
503
504#[cfg(feature = "wasm")]
505#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
506const TS_CUSTOM_TYPES: &'static str = r#"
507export type SecretProtectedKeyEnvelope = Tagged<string, "SecretProtectedKeyEnvelope">;
508"#;
509
510#[cfg(feature = "wasm")]
511impl wasm_bindgen::describe::WasmDescribe for SecretProtectedKeyEnvelope {
512 fn describe() {
513 <String as wasm_bindgen::describe::WasmDescribe>::describe();
514 }
515}
516
517#[cfg(feature = "wasm")]
518impl FromWasmAbi for SecretProtectedKeyEnvelope {
519 type Abi = <String as FromWasmAbi>::Abi;
520
521 unsafe fn from_abi(abi: Self::Abi) -> Self {
522 use wasm_bindgen::UnwrapThrowExt;
523 let string = unsafe { String::from_abi(abi) };
524 SecretProtectedKeyEnvelope::from_str(&string).unwrap_throw()
525 }
526}
527
528#[cfg(feature = "wasm")]
529impl OptionFromWasmAbi for SecretProtectedKeyEnvelope {
530 fn is_none(abi: &Self::Abi) -> bool {
531 <String as OptionFromWasmAbi>::is_none(abi)
532 }
533}
534
535#[cfg(feature = "wasm")]
536impl IntoWasmAbi for SecretProtectedKeyEnvelope {
537 type Abi = <String as IntoWasmAbi>::Abi;
538
539 fn into_abi(self) -> Self::Abi {
540 let string: String = self.into();
541 string.into_abi()
542 }
543}
544
545#[cfg(feature = "wasm")]
546impl TryFrom<wasm_bindgen::JsValue> for SecretProtectedKeyEnvelope {
547 type Error = SecretProtectedKeyEnvelopeError;
548
549 fn try_from(value: wasm_bindgen::JsValue) -> Result<Self, Self::Error> {
550 let string = value.as_string().ok_or_else(|| {
551 SecretProtectedKeyEnvelopeError::Parsing(
552 "SecretProtectedKeyEnvelope JsValue is not a string".to_string(),
553 )
554 })?;
555 SecretProtectedKeyEnvelope::from_str(&string)
556 }
557}
558
559#[derive(Debug, Clone, Copy, PartialEq, Eq)]
561pub enum SecretProtectedKeyEnvelopeNamespace {
562 OrganizationInvite = 1,
565 DesktopBiometricUnlock = 2,
568 RegistrationOpenOrgInvite = 3,
571 #[cfg(test)]
573 ExampleNamespace = -1,
574 #[cfg(test)]
576 ExampleNamespace2 = -2,
577}
578
579impl SecretProtectedKeyEnvelopeNamespace {
580 fn as_i64(&self) -> i64 {
582 *self as i64
583 }
584}
585
586impl TryFrom<i128> for SecretProtectedKeyEnvelopeNamespace {
587 type Error = SecretProtectedKeyEnvelopeError;
588
589 fn try_from(value: i128) -> Result<Self, Self::Error> {
590 match value {
591 1 => Ok(SecretProtectedKeyEnvelopeNamespace::OrganizationInvite),
592 2 => Ok(SecretProtectedKeyEnvelopeNamespace::DesktopBiometricUnlock),
593 3 => Ok(SecretProtectedKeyEnvelopeNamespace::RegistrationOpenOrgInvite),
594 #[cfg(test)]
595 -1 => Ok(SecretProtectedKeyEnvelopeNamespace::ExampleNamespace),
596 #[cfg(test)]
597 -2 => Ok(SecretProtectedKeyEnvelopeNamespace::ExampleNamespace2),
598 _ => Err(SecretProtectedKeyEnvelopeError::InvalidNamespace),
599 }
600 }
601}
602
603impl TryFrom<i64> for SecretProtectedKeyEnvelopeNamespace {
604 type Error = SecretProtectedKeyEnvelopeError;
605
606 fn try_from(value: i64) -> Result<Self, Self::Error> {
607 Self::try_from(i128::from(value))
608 }
609}
610
611impl From<SecretProtectedKeyEnvelopeNamespace> for i128 {
612 fn from(val: SecretProtectedKeyEnvelopeNamespace) -> Self {
613 val.as_i64().into()
614 }
615}
616
617impl ContentNamespace for SecretProtectedKeyEnvelopeNamespace {}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622 use crate::{KeyStore, SymmetricKeyAlgorithm, traits::tests::TestIds};
623
624 const TESTVECTOR_SECRET_BYTES: &[u8] = &[
627 174, 83, 45, 9, 235, 3, 186, 62, 199, 125, 198, 108, 129, 205, 24, 21, 174, 148, 88, 80,
628 10, 238, 169, 66, 75, 202, 41, 201, 186, 244, 169, 67,
629 ];
630
631 fn testvector_secret() -> HighEntropySecret {
632 HighEntropySecret::from_internal(TESTVECTOR_SECRET_BYTES)
633 }
634
635 const TEST_UNSEALED_COSEKEY_ENCODED: &[u8] = &[
639 165, 1, 4, 2, 80, 214, 124, 137, 200, 1, 180, 227, 27, 77, 48, 119, 198, 210, 9, 149, 144,
640 3, 58, 0, 1, 17, 111, 4, 132, 3, 4, 5, 6, 32, 88, 32, 111, 105, 200, 46, 142, 185, 114,
641 127, 136, 152, 153, 40, 8, 62, 120, 184, 252, 175, 210, 2, 245, 237, 175, 195, 73, 211,
642 136, 23, 217, 203, 35, 10, 1,
643 ];
644 const TESTVECTOR_COSEKEY_ENVELOPE: &[u8] = &[
645 132, 88, 40, 165, 1, 3, 3, 24, 101, 58, 0, 1, 21, 92, 80, 214, 124, 137, 200, 1, 180, 227,
646 27, 77, 48, 119, 198, 210, 9, 149, 144, 58, 0, 1, 56, 129, 6, 58, 0, 1, 56, 128, 32, 161,
647 5, 76, 155, 157, 246, 33, 115, 165, 158, 222, 125, 222, 199, 188, 88, 84, 132, 235, 37,
648 236, 53, 75, 63, 253, 184, 134, 147, 83, 103, 87, 56, 81, 69, 202, 114, 23, 82, 25, 163,
649 68, 36, 13, 104, 187, 54, 143, 167, 113, 63, 62, 88, 146, 50, 214, 209, 170, 6, 235, 122,
650 44, 129, 149, 67, 213, 112, 112, 55, 51, 183, 165, 61, 168, 174, 215, 147, 110, 133, 164,
651 198, 29, 177, 84, 20, 203, 8, 0, 211, 218, 226, 62, 121, 51, 129, 230, 248, 66, 170, 83,
652 106, 109, 129, 131, 64, 162, 1, 41, 51, 88, 32, 123, 254, 226, 185, 81, 106, 88, 73, 109,
653 191, 241, 1, 143, 230, 179, 47, 36, 100, 235, 131, 4, 180, 12, 96, 125, 91, 184, 5, 175,
654 125, 188, 16, 246,
655 ];
656 const TEST_UNSEALED_LEGACYKEY_ENCODED: &[u8] = &[
657 23, 37, 64, 225, 53, 59, 143, 179, 18, 121, 128, 120, 86, 134, 93, 166, 214, 151, 210, 46,
658 240, 216, 69, 249, 247, 222, 110, 100, 185, 38, 173, 84, 202, 107, 132, 251, 144, 245, 105,
659 244, 220, 93, 212, 227, 98, 208, 173, 122, 245, 78, 244, 106, 174, 124, 109, 91, 53, 119,
660 96, 182, 45, 174, 206, 131,
661 ];
662 const TESTVECTOR_LEGACYKEY_ENVELOPE: &[u8] = &[
663 132, 88, 52, 164, 1, 3, 3, 120, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47,
664 120, 46, 98, 105, 116, 119, 97, 114, 100, 101, 110, 46, 108, 101, 103, 97, 99, 121, 45,
665 107, 101, 121, 58, 0, 1, 56, 129, 6, 58, 0, 1, 56, 128, 32, 161, 5, 76, 20, 11, 52, 107,
666 155, 203, 125, 143, 165, 38, 59, 135, 88, 80, 84, 46, 227, 50, 142, 191, 103, 207, 31, 192,
667 201, 215, 163, 102, 18, 93, 181, 247, 229, 12, 166, 221, 143, 98, 86, 74, 138, 12, 165, 1,
668 206, 101, 240, 222, 51, 239, 216, 4, 85, 61, 212, 62, 44, 29, 1, 184, 4, 191, 189, 248,
669 174, 159, 11, 133, 205, 19, 22, 28, 148, 138, 238, 136, 253, 173, 250, 69, 186, 232, 91,
670 222, 238, 9, 175, 178, 214, 27, 120, 254, 212, 110, 129, 131, 64, 162, 1, 41, 51, 88, 32,
671 222, 10, 249, 242, 57, 196, 223, 240, 234, 177, 19, 72, 201, 32, 1, 129, 46, 6, 76, 38,
672 149, 151, 217, 94, 84, 67, 50, 107, 103, 74, 88, 72, 246,
673 ];
674
675 #[test]
676 fn test_registration_open_org_invite_namespace_maps_to_expected_discriminant() {
677 assert_eq!(
680 SecretProtectedKeyEnvelopeNamespace::try_from(3i128).unwrap(),
681 SecretProtectedKeyEnvelopeNamespace::RegistrationOpenOrgInvite
682 );
683 assert_eq!(
684 i128::from(SecretProtectedKeyEnvelopeNamespace::RegistrationOpenOrgInvite),
685 3
686 );
687 }
688
689 #[test]
690 fn test_registration_open_org_invite_rejects_cross_namespace_unseal() {
691 let key_store = KeyStore::<TestIds>::default();
696 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
697 let test_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
698 let secret = testvector_secret();
699
700 let envelope = SecretProtectedKeyEnvelope::seal(
701 test_key,
702 &secret,
703 SecretProtectedKeyEnvelopeNamespace::RegistrationOpenOrgInvite,
704 &ctx,
705 )
706 .expect("seal");
707
708 assert!(matches!(
709 envelope.unseal(
710 &secret,
711 SecretProtectedKeyEnvelopeNamespace::DesktopBiometricUnlock,
712 &mut ctx,
713 ),
714 Err(SecretProtectedKeyEnvelopeError::InvalidNamespace)
715 ));
716
717 let _ = envelope
719 .unseal(
720 &secret,
721 SecretProtectedKeyEnvelopeNamespace::RegistrationOpenOrgInvite,
722 &mut ctx,
723 )
724 .expect("unseal under correct namespace succeeds");
725 }
726
727 #[test]
728 #[ignore = "Manual test to verify debug format"]
729 fn test_debug() {
730 let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
731 let envelope = SecretProtectedKeyEnvelope::seal_ref(
732 &key,
733 &testvector_secret(),
734 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
735 )
736 .unwrap();
737 println!("{:?}", envelope);
738 }
739
740 #[test]
741 fn test_testvector_cosekey() {
742 let key_store = KeyStore::<TestIds>::default();
743 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
744 let envelope = SecretProtectedKeyEnvelope::try_from(&TESTVECTOR_COSEKEY_ENVELOPE.to_vec())
745 .expect("Key envelope should be valid");
746 let key = envelope
747 .unseal(
748 &testvector_secret(),
749 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
750 &mut ctx,
751 )
752 .expect("Unsealing should succeed");
753 let unsealed_key = ctx
754 .get_symmetric_key(key)
755 .expect("Key should exist in the key store");
756 assert_eq!(
757 unsealed_key.to_encoded().to_vec(),
758 TEST_UNSEALED_COSEKEY_ENCODED
759 );
760 }
761
762 #[test]
763 fn test_testvector_legacykey() {
764 let key_store = KeyStore::<TestIds>::default();
765 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
766 let envelope =
767 SecretProtectedKeyEnvelope::try_from(&TESTVECTOR_LEGACYKEY_ENVELOPE.to_vec())
768 .expect("Key envelope should be valid");
769 let key = envelope
770 .unseal(
771 &testvector_secret(),
772 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
773 &mut ctx,
774 )
775 .expect("Unsealing should succeed");
776 let unsealed_key = ctx
777 .get_symmetric_key(key)
778 .expect("Key should exist in the key store");
779 assert_eq!(
780 unsealed_key.to_encoded().to_vec(),
781 TEST_UNSEALED_LEGACYKEY_ENCODED
782 );
783 }
784
785 #[test]
786 fn test_make_envelope() {
787 let key_store = KeyStore::<TestIds>::default();
788 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
789 let test_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
790
791 let secret = testvector_secret();
792
793 let envelope = SecretProtectedKeyEnvelope::seal(
795 test_key,
796 &secret,
797 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
798 &ctx,
799 )
800 .unwrap();
801 let serialized: Vec<u8> = (&envelope).into();
802
803 let deserialized: SecretProtectedKeyEnvelope =
805 SecretProtectedKeyEnvelope::try_from(&serialized).unwrap();
806 let key = deserialized
807 .unseal(
808 &secret,
809 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
810 &mut ctx,
811 )
812 .unwrap();
813
814 let unsealed_key = ctx
816 .get_symmetric_key(key)
817 .expect("Key should exist in the key store");
818
819 let key_before_sealing = ctx
820 .get_symmetric_key(test_key)
821 .expect("Key should exist in the key store");
822
823 assert_eq!(unsealed_key, key_before_sealing);
824 }
825
826 #[test]
827 fn test_make_envelope_legacy_key() {
828 let key_store = KeyStore::<TestIds>::default();
829 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
830 let test_key = ctx.generate_symmetric_key();
831
832 let secret = testvector_secret();
833
834 let envelope = SecretProtectedKeyEnvelope::seal(
836 test_key,
837 &secret,
838 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
839 &ctx,
840 )
841 .unwrap();
842 let serialized: Vec<u8> = (&envelope).into();
843
844 let deserialized: SecretProtectedKeyEnvelope =
846 SecretProtectedKeyEnvelope::try_from(&serialized).unwrap();
847 let key = deserialized
848 .unseal(
849 &secret,
850 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
851 &mut ctx,
852 )
853 .unwrap();
854
855 let unsealed_key = ctx
857 .get_symmetric_key(key)
858 .expect("Key should exist in the key store");
859
860 let key_before_sealing = ctx
861 .get_symmetric_key(test_key)
862 .expect("Key should exist in the key store");
863
864 assert_eq!(unsealed_key, key_before_sealing);
865 }
866
867 #[test]
868 fn test_reseal_envelope() {
869 let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
870 let secret = testvector_secret();
871 let new_secret = HighEntropySecret::make(32).unwrap();
872
873 let envelope: SecretProtectedKeyEnvelope = SecretProtectedKeyEnvelope::seal_ref(
875 &key,
876 &secret,
877 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
878 )
879 .expect("Sealing should work");
880
881 let envelope = envelope
883 .reseal(
884 &secret,
885 &new_secret,
886 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
887 )
888 .expect("Resealing should work");
889 let unsealed = envelope
890 .unseal_ref(
891 &new_secret,
892 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
893 )
894 .expect("Unsealing should work");
895
896 assert_eq!(unsealed, key);
898 }
899
900 #[test]
901 fn test_wrong_secret() {
902 let key_store = KeyStore::<TestIds>::default();
903 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
904 let test_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
905
906 let secret = testvector_secret();
907 let wrong_secret = HighEntropySecret::make(32).unwrap();
908
909 let envelope = SecretProtectedKeyEnvelope::seal(
911 test_key,
912 &secret,
913 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
914 &ctx,
915 )
916 .unwrap();
917
918 let deserialized: SecretProtectedKeyEnvelope =
920 SecretProtectedKeyEnvelope::try_from(&(&envelope).into()).unwrap();
921 assert!(matches!(
922 deserialized.unseal(
923 &wrong_secret,
924 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
925 &mut ctx
926 ),
927 Err(SecretProtectedKeyEnvelopeError::WrongSecret)
928 ));
929 }
930
931 #[test]
932 fn test_wrong_safe_namespace() {
933 let key_store = KeyStore::<TestIds>::default();
934 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
935 let test_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
936 let secret = testvector_secret();
937
938 let mut envelope = SecretProtectedKeyEnvelope::seal(
939 test_key,
940 &secret,
941 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
942 &ctx,
943 )
944 .expect("Seal works");
945
946 if let Some((_, value)) = envelope
947 .cose_encrypt
948 .protected
949 .header
950 .rest
951 .iter_mut()
952 .find(|(label, _)| {
953 matches!(label, coset::Label::Int(label_value) if *label_value == crate::cose::SAFE_OBJECT_NAMESPACE)
954 })
955 {
956 *value = Value::Integer((SafeObjectNamespace::DataEnvelope as i64).into());
957 }
958
959 let deserialized: SecretProtectedKeyEnvelope =
960 SecretProtectedKeyEnvelope::try_from(&(&envelope).into())
961 .expect("Envelope should be valid");
962
963 assert!(matches!(
964 deserialized.unseal(
965 &secret,
966 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
967 &mut ctx,
968 ),
969 Err(SecretProtectedKeyEnvelopeError::InvalidNamespace)
970 ));
971 }
972
973 #[test]
974 fn test_key_id() {
975 let key_store = KeyStore::<TestIds>::default();
976 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
977 let test_key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::XChaCha20Poly1305);
978 let key_id = ctx.get_symmetric_key(test_key).unwrap().key_id().unwrap();
979
980 let secret = testvector_secret();
981
982 let envelope = SecretProtectedKeyEnvelope::seal(
984 test_key,
985 &secret,
986 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
987 &ctx,
988 )
989 .unwrap();
990 let contained_key_id = envelope.contained_key_id().unwrap();
991 assert_eq!(Some(key_id), contained_key_id);
992 }
993
994 #[test]
995 fn test_no_key_id() {
996 let key_store = KeyStore::<TestIds>::default();
997 let mut ctx: KeyStoreContext<'_, TestIds> = key_store.context_mut();
998 let test_key = ctx.generate_symmetric_key();
999
1000 let secret = testvector_secret();
1001
1002 let envelope = SecretProtectedKeyEnvelope::seal(
1004 test_key,
1005 &secret,
1006 SecretProtectedKeyEnvelopeNamespace::ExampleNamespace,
1007 &ctx,
1008 )
1009 .unwrap();
1010 let contained_key_id = envelope.contained_key_id().unwrap();
1011 assert_eq!(None, contained_key_id);
1012 }
1013}