1use crate::der;
15use crate::types::{Oid, RecipientInfoKind, WrapDescription};
16
17const ENVELOPED_DATA: &str = "1.2.840.113549.1.7.3";
19const AUTH_ENVELOPED_DATA: &str = "1.2.840.113549.1.9.16.1.23";
21const ORI_KEM: &str = "1.2.840.113549.1.9.16.13.3";
23
24#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
26#[error("CMS artifact is not readable as a wrap: {0}")]
27pub struct CmsReadError(String);
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct CmsFacts {
32 pub description: WrapDescription,
34 pub recipient_key_identifier: Option<Vec<u8>>,
38}
39
40pub fn describe(der: &[u8]) -> Result<CmsFacts, CmsReadError> {
48 let (content_info, rest) = read_tlv(der)?;
49 if !rest.is_empty() {
50 return Err(err("trailing bytes after ContentInfo"));
51 }
52 let body = content_info.expect_tag(TAG_SEQUENCE, "ContentInfo")?;
53
54 let (content_type, after_type) = read_tlv(body)?;
55 let content_type = oid_of(&content_type, "ContentInfo.contentType")?;
56 match content_type.as_str() {
57 ENVELOPED_DATA | AUTH_ENVELOPED_DATA => {}
58 other => {
59 return Err(err(format!(
60 "content type {other} is not an enveloped structure"
61 )));
62 }
63 }
64
65 let (explicit_content, _) = read_tlv(after_type)?;
66 let content = explicit_content.expect_tag(TAG_CONTEXT_0_CONSTRUCTED, "ContentInfo.content")?;
67 let (enveloped, _) = read_tlv(content)?;
68 let enveloped = enveloped.expect_tag(TAG_SEQUENCE, "EnvelopedData")?;
69
70 let (_version, after_version) = read_tlv(enveloped)?;
73 let (next, after_next) = read_tlv(after_version)?;
74 let (recipients, after_recipients) = if next.tag == TAG_CONTEXT_0_CONSTRUCTED {
75 read_tlv(after_next)?
76 } else {
77 (next, after_next)
78 };
79 let recipients = recipients.expect_tag(TAG_SET, "recipientInfos")?;
80
81 let (first_recipient, _) = read_tlv(recipients)?;
82 let recipient = read_recipient(&first_recipient)?;
83
84 let (encrypted_content_info, _) = read_tlv(after_recipients)?;
85 let content_encryption = content_encryption_oid(&encrypted_content_info)?;
86
87 Ok(CmsFacts {
88 description: recipient
89 .description
90 .with_content_encryption(content_encryption),
91 recipient_key_identifier: recipient.key_identifier,
92 })
93}
94
95struct Recipient {
97 description: WrapDescription,
98 key_identifier: Option<Vec<u8>>,
99}
100
101fn read_recipient(info: &Tlv<'_>) -> Result<Recipient, CmsReadError> {
102 match info.tag {
103 TAG_SEQUENCE => read_ktri(info.value),
104 TAG_CONTEXT_1_CONSTRUCTED => read_kari(info.value),
105 TAG_CONTEXT_2_CONSTRUCTED => {
106 let kekri = read_kekri(info.value)?;
107 Ok(Recipient {
108 description: WrapDescription::new(RecipientInfoKind::Kekri, kekri.algorithm),
109 key_identifier: Some(kekri.key_identifier.to_vec()),
110 })
111 }
112 TAG_CONTEXT_3_CONSTRUCTED => Err(err("PasswordRecipientInfo is not a key wrap")),
113 TAG_CONTEXT_4_CONSTRUCTED => read_ori(info.value),
114 other => Err(err(format!("unknown RecipientInfo tag 0x{other:02x}"))),
115 }
116}
117
118fn read_ktri(body: &[u8]) -> Result<Recipient, CmsReadError> {
120 let (_version, after_version) = read_tlv(body)?;
121 let (rid, after_rid) = read_tlv(after_version)?;
122 let (algorithm, _) = read_tlv(after_rid)?;
123
124 let key_identifier = (rid.tag == TAG_CONTEXT_0_PRIMITIVE).then(|| rid.value.to_vec());
126
127 Ok(Recipient {
128 description: WrapDescription::new(
129 RecipientInfoKind::Ktri,
130 algorithm_oid(&algorithm, "keyEncryptionAlgorithm")?,
131 ),
132 key_identifier,
133 })
134}
135
136struct Kekri<'a> {
138 key_identifier: &'a [u8],
139 algorithm: Oid,
140 encrypted_key: &'a [u8],
141}
142
143fn read_kekri(body: &[u8]) -> Result<Kekri<'_>, CmsReadError> {
156 let (_version, after_version) = read_tlv(body)?;
157 let (kekid, after_kekid) = read_tlv(after_version)?;
158 let (algorithm, after_algorithm) = read_tlv(after_kekid)?;
159 let (encrypted_key, _) = read_tlv(after_algorithm)?;
160
161 let (key_identifier, _) = read_tlv(kekid.expect_tag(TAG_SEQUENCE, "KEKIdentifier")?)?;
162 Ok(Kekri {
163 key_identifier: key_identifier.expect_tag(TAG_OCTET_STRING, "kekid.keyIdentifier")?,
164 algorithm: algorithm_oid(&algorithm, "keyEncryptionAlgorithm")?,
165 encrypted_key: encrypted_key.expect_tag(TAG_OCTET_STRING, "encryptedKey")?,
166 })
167}
168
169fn read_kari(body: &[u8]) -> Result<Recipient, CmsReadError> {
176 let (_version, after_version) = read_tlv(body)?;
177 let (originator, after_originator) = read_tlv(after_version)?;
178 if originator.tag != TAG_CONTEXT_0_CONSTRUCTED {
179 return Err(err("KeyAgreeRecipientInfo without an originator"));
180 }
181 let (next, after_next) = read_tlv(after_originator)?;
182 let (algorithm, after_algorithm) = if next.tag == TAG_CONTEXT_1_CONSTRUCTED {
183 read_tlv(after_next)?
184 } else {
185 (next, after_next)
186 };
187
188 let (scheme, parameters) = algorithm_parts(&algorithm, "keyEncryptionAlgorithm")?;
189 let mut description = WrapDescription::new(RecipientInfoKind::Kari, scheme);
190 if let Some(parameters) = parameters {
191 description = description.with_kek_wrap(algorithm_oid(¶meters, "keyWrapAlgorithm")?);
192 }
193
194 let (recipient_keys, _) = read_tlv(after_algorithm)?;
195 Ok(Recipient {
196 description,
197 key_identifier: agreed_key_identifier(recipient_keys.value)?,
198 })
199}
200
201fn agreed_key_identifier(body: &[u8]) -> Result<Option<Vec<u8>>, CmsReadError> {
204 let (first, _) = read_tlv(body)?;
205 let (rid, _) = read_tlv(first.expect_tag(TAG_SEQUENCE, "RecipientEncryptedKey")?)?;
206 if rid.tag != TAG_CONTEXT_0_CONSTRUCTED {
207 return Ok(None);
208 }
209 let (key_id, _) = read_tlv(rid.value)?;
210 Ok(Some(
211 key_id
212 .expect_tag(TAG_OCTET_STRING, "subjectKeyIdentifier")?
213 .to_vec(),
214 ))
215}
216
217fn read_ori(body: &[u8]) -> Result<Recipient, CmsReadError> {
219 let (oid, after_oid) = read_tlv(body)?;
220 let oid = oid_of(&oid, "OtherRecipientInfo.oriType")?;
221 if oid.as_str() != ORI_KEM {
222 return Ok(Recipient {
225 description: WrapDescription::new(RecipientInfoKind::Ori, oid),
226 key_identifier: None,
227 });
228 }
229 let (kemri, _) = read_tlv(after_oid)?;
230 read_kemri(kemri.expect_tag(TAG_SEQUENCE, "KEMRecipientInfo")?)
231}
232
233fn read_kemri(body: &[u8]) -> Result<Recipient, CmsReadError> {
239 let (_version, after_version) = read_tlv(body)?;
240 let (rid, after_rid) = read_tlv(after_version)?;
241 let (kem, after_kem) = read_tlv(after_rid)?;
242 let (_kemct, after_kemct) = read_tlv(after_kem)?;
243 let (kdf, after_kdf) = read_tlv(after_kemct)?;
244 let (_kek_length, after_kek_length) = read_tlv(after_kdf)?;
245
246 let (next, after_next) = read_tlv(after_kek_length)?;
247 let (wrap, _) = if next.tag == TAG_CONTEXT_0_CONSTRUCTED {
248 read_tlv(after_next)?
249 } else {
250 (next, after_next)
251 };
252
253 let description = WrapDescription::new(
254 RecipientInfoKind::Kemri,
255 algorithm_oid(&kem, "KEMRecipientInfo.kem")?,
256 )
257 .with_kdf(algorithm_oid(&kdf, "KEMRecipientInfo.kdf")?)
258 .with_kek_wrap(algorithm_oid(&wrap, "KEMRecipientInfo.wrap")?);
259
260 Ok(Recipient {
261 description,
262 key_identifier: (rid.tag == TAG_CONTEXT_0_PRIMITIVE).then(|| rid.value.to_vec()),
263 })
264}
265
266fn content_encryption_oid(info: &Tlv<'_>) -> Result<Oid, CmsReadError> {
269 let body = info.expect_tag(TAG_SEQUENCE, "encryptedContentInfo")?;
270 let (_content_type, after_type) = read_tlv(body)?;
271 let (algorithm, _) = read_tlv(after_type)?;
272 algorithm_oid(&algorithm, "contentEncryptionAlgorithm")
273}
274
275fn algorithm_oid(algorithm: &Tlv<'_>, field: &str) -> Result<Oid, CmsReadError> {
277 Ok(algorithm_parts(algorithm, field)?.0)
278}
279
280fn algorithm_parts<'a>(
282 algorithm: &Tlv<'a>,
283 field: &str,
284) -> Result<(Oid, Option<Tlv<'a>>), CmsReadError> {
285 let body = algorithm.expect_tag(TAG_SEQUENCE, field)?;
286 let (oid, rest) = read_tlv(body)?;
287 let oid = oid_of(&oid, field)?;
288 let parameters = if rest.is_empty() {
289 None
290 } else {
291 Some(read_tlv(rest)?.0)
292 };
293 Ok((oid, parameters))
294}
295
296const TAG_INTEGER: u8 = 0x02;
297const TAG_OCTET_STRING: u8 = 0x04;
298const TAG_OID: u8 = 0x06;
299const TAG_SEQUENCE: u8 = 0x30;
300const TAG_SET: u8 = 0x31;
301const TAG_CONTEXT_0_PRIMITIVE: u8 = 0x80;
302const TAG_CONTEXT_0_CONSTRUCTED: u8 = 0xa0;
303const TAG_CONTEXT_1_CONSTRUCTED: u8 = 0xa1;
304const TAG_CONTEXT_2_CONSTRUCTED: u8 = 0xa2;
305const TAG_CONTEXT_3_CONSTRUCTED: u8 = 0xa3;
306const TAG_CONTEXT_4_CONSTRUCTED: u8 = 0xa4;
307
308#[derive(Debug, Clone, Copy, PartialEq, Eq)]
310struct Tlv<'a> {
311 tag: u8,
312 value: &'a [u8],
313}
314
315impl<'a> Tlv<'a> {
316 fn expect_tag(&self, tag: u8, field: &str) -> Result<&'a [u8], CmsReadError> {
318 if self.tag == tag {
319 Ok(self.value)
320 } else {
321 Err(err(format!(
322 "{field}: expected tag 0x{tag:02x}, found 0x{:02x}",
323 self.tag
324 )))
325 }
326 }
327}
328
329fn read_tlv(input: &[u8]) -> Result<(Tlv<'_>, &[u8]), CmsReadError> {
334 let (&tag, after_tag) = input.split_first().ok_or_else(|| err("truncated tag"))?;
335 let (&first_length, after_first) = after_tag
336 .split_first()
337 .ok_or_else(|| err("truncated length"))?;
338
339 let (length, after_length) = match first_length {
340 short if short < 0x80 => (usize::from(short), after_first),
341 0x80 => return Err(err("indefinite length is BER, not DER")),
342 long => {
343 let count = usize::from(long & 0x7f);
344 if count > std::mem::size_of::<usize>() {
345 return Err(err("length does not fit in a usize"));
346 }
347 let (bytes, rest) = after_first
348 .split_at_checked(count)
349 .ok_or_else(|| err("truncated long-form length"))?;
350 let length = bytes
351 .iter()
352 .fold(0usize, |acc, &b| (acc << 8) | usize::from(b));
353 (length, rest)
354 }
355 };
356
357 let (value, rest) = after_length
358 .split_at_checked(length)
359 .ok_or_else(|| err("value runs past the end of the input"))?;
360 Ok((Tlv { tag, value }, rest))
361}
362
363fn oid_of(element: &Tlv<'_>, field: &str) -> Result<Oid, CmsReadError> {
370 let bytes = element.expect_tag(TAG_OID, field)?;
371
372 let mut subidentifiers = Vec::new();
373 let mut value: u64 = 0;
374 let mut pending = false;
375 for &byte in bytes {
376 value = value
377 .checked_mul(128)
378 .and_then(|shifted| shifted.checked_add(u64::from(byte & 0x7f)))
379 .ok_or_else(|| err(format!("{field}: OID arc overflows")))?;
380 pending = byte & 0x80 != 0;
381 if !pending {
382 subidentifiers.push(value);
383 value = 0;
384 }
385 }
386 if pending {
387 return Err(err(format!("{field}: OID ends mid-arc")));
388 }
389
390 let (&packed, rest) = subidentifiers
391 .split_first()
392 .ok_or_else(|| err(format!("{field}: empty OID")))?;
393 let (first, second) = match packed.checked_sub(80) {
396 Some(above) => (2, above),
397 None => (packed / 40, packed % 40),
398 };
399
400 let mut arcs = vec![first.to_string(), second.to_string()];
401 arcs.extend(rest.iter().map(u64::to_string));
402 Oid::new(&arcs.join(".")).map_err(|e| err(format!("{field}: {e}")))
403}
404
405fn err(message: impl Into<String>) -> CmsReadError {
406 CmsReadError(message.into())
407}
408
409const DATA: &str = "1.2.840.113549.1.7.1";
411
412const GCM_ICV_LENGTHS: std::ops::RangeInclusive<usize> = 12..=16;
415
416pub const GCM_ICV_LEN: usize = 16;
421
422#[derive(Debug, Clone, PartialEq, Eq)]
456pub struct KekEnvelope {
457 pub key_identifier: Vec<u8>,
459 pub wrapped_cek: Vec<u8>,
461 pub nonce: Vec<u8>,
463 pub ciphertext: Vec<u8>,
465 pub tag: Vec<u8>,
468}
469
470pub fn write_kek_enveloped(envelope: &KekEnvelope) -> Result<Vec<u8>, CmsReadError> {
482 if !GCM_ICV_LENGTHS.contains(&envelope.tag.len()) {
483 return Err(err(format!(
484 "a {}-byte authentication tag is not one RFC 5084 admits",
485 envelope.tag.len()
486 )));
487 }
488 let oid = |dotted: &str| {
489 crate::der::object_identifier(dotted)
490 .ok_or_else(|| err(format!("{dotted} is not an object identifier")))
491 };
492
493 let kekri = der::tlv(
496 TAG_CONTEXT_2_CONSTRUCTED,
497 &[
498 der::small_integer(4),
499 der::sequence(&[der::octet_string(&envelope.key_identifier)]),
500 der::sequence(&[oid(crate::types::oid::AES_256_WRAP)?]),
501 der::octet_string(&envelope.wrapped_cek),
502 ]
503 .concat(),
504 );
505
506 let content = der::sequence(&[
509 oid(DATA)?,
510 der::sequence(&[
511 oid(crate::types::oid::AES_256_GCM)?,
512 der::sequence(&[
513 der::octet_string(&envelope.nonce),
514 der::small_integer(u8::try_from(envelope.tag.len()).unwrap_or(0)),
515 ]),
516 ]),
517 der::tlv(TAG_CONTEXT_0_PRIMITIVE, &envelope.ciphertext),
518 ]);
519
520 let auth_enveloped = der::sequence(&[
524 der::small_integer(0),
525 der::set_of(&[kekri]),
526 content,
527 der::octet_string(&envelope.tag),
528 ]);
529
530 Ok(der::sequence(&[
531 oid(AUTH_ENVELOPED_DATA)?,
532 der::tlv(TAG_CONTEXT_0_CONSTRUCTED, &auth_enveloped),
533 ]))
534}
535
536pub fn read_kek_enveloped(der_bytes: &[u8]) -> Result<KekEnvelope, CmsReadError> {
554 let (content_info, rest) = read_tlv(der_bytes)?;
555 if !rest.is_empty() {
556 return Err(err("trailing bytes after ContentInfo"));
557 }
558 let body = content_info.expect_tag(TAG_SEQUENCE, "ContentInfo")?;
559 let (content_type, after_type) = read_tlv(body)?;
560 if oid_of(&content_type, "ContentInfo.contentType")?.as_str() != AUTH_ENVELOPED_DATA {
561 return Err(err("not an AuthEnvelopedData"));
562 }
563
564 let (explicit, _) = read_tlv(after_type)?;
565 let (auth_enveloped, _) =
566 read_tlv(explicit.expect_tag(TAG_CONTEXT_0_CONSTRUCTED, "ContentInfo.content")?)?;
567 let auth_enveloped = auth_enveloped.expect_tag(TAG_SEQUENCE, "AuthEnvelopedData")?;
568
569 let (_version, after_version) = read_tlv(auth_enveloped)?;
573 let (next, after_next) = read_tlv(after_version)?;
574 let (recipients, after_recipients) = if next.tag == TAG_CONTEXT_0_CONSTRUCTED {
575 read_tlv(after_next)?
576 } else {
577 (next, after_next)
578 };
579 let recipients = recipients.expect_tag(TAG_SET, "recipientInfos")?;
580
581 let (recipient, more) = read_tlv(recipients)?;
582 if !more.is_empty() {
583 return Err(err("more than one recipient"));
586 }
587 let kekri = read_kekri(recipient.expect_tag(TAG_CONTEXT_2_CONSTRUCTED, "KEKRecipientInfo")?)?;
588 if kekri.algorithm.as_str() != crate::types::oid::AES_256_WRAP {
589 return Err(err(format!(
593 "key encryption algorithm {} is not id-aes256-wrap",
594 kekri.algorithm
595 )));
596 }
597
598 let (content_info, after_content) = read_tlv(after_recipients)?;
599 let (nonce, icv_len, ciphertext) =
600 read_gcm_content(content_info.expect_tag(TAG_SEQUENCE, "authEncryptedContentInfo")?)?;
601
602 let (mac, _) = read_tlv(after_content)?;
603 if mac.tag == TAG_CONTEXT_1_CONSTRUCTED {
604 return Err(err(
610 "this AuthEnvelopedData carries authAttrs, which are part of what its \
611 tag authenticates and which this reader does not cover",
612 ));
613 }
614 let tag = mac.expect_tag(TAG_OCTET_STRING, "mac")?;
615 if tag.len() != icv_len {
616 return Err(err(format!(
620 "GCMParameters names a {icv_len}-byte tag and the mac is {} bytes",
621 tag.len()
622 )));
623 }
624
625 Ok(KekEnvelope {
626 key_identifier: kekri.key_identifier.to_vec(),
627 wrapped_cek: kekri.encrypted_key.to_vec(),
628 nonce,
629 ciphertext,
630 tag: tag.to_vec(),
631 })
632}
633
634fn read_gcm_content(body: &[u8]) -> Result<(Vec<u8>, usize, Vec<u8>), CmsReadError> {
637 let (_content_type, after_type) = read_tlv(body)?;
638 let (algorithm, after_algorithm) = read_tlv(after_type)?;
639
640 let (algorithm_body, parameters) = algorithm_parts(&algorithm, "contentEncryptionAlgorithm")?;
641 if algorithm_body.as_str() != crate::types::oid::AES_256_GCM {
642 return Err(err(format!(
643 "content encryption algorithm {algorithm_body} is not id-aes256-GCM"
644 )));
645 }
646 let parameters = parameters.ok_or_else(|| err("AES-GCM content carries no GCMParameters"))?;
647 let (nonce, after_nonce) = read_tlv(parameters.expect_tag(TAG_SEQUENCE, "GCMParameters")?)?;
648 let nonce = nonce.expect_tag(TAG_OCTET_STRING, "GCMParameters.aes-nonce")?;
649
650 let icv_len = match read_tlv(after_nonce) {
653 Ok((field, _)) => usize::from(
654 *field
655 .expect_tag(TAG_INTEGER, "GCMParameters.aes-ICVlen")?
656 .first()
657 .ok_or_else(|| err("GCMParameters.aes-ICVlen is empty"))?,
658 ),
659 Err(_) => 12,
660 };
661 if !GCM_ICV_LENGTHS.contains(&icv_len) {
662 return Err(err(format!(
665 "GCMParameters names a {icv_len}-byte tag, which RFC 5084 does not admit"
666 )));
667 }
668
669 let (content, _) = read_tlv(after_algorithm)?;
670 Ok((
671 nonce.to_vec(),
672 icv_len,
673 content
674 .expect_tag(TAG_CONTEXT_0_PRIMITIVE, "encryptedContent")?
675 .to_vec(),
676 ))
677}
678
679#[cfg(test)]
680#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
681mod tests {
682 use super::*;
683 use base64ct::Encoding as _;
684
685 #[test]
686 fn reads_a_dotted_oid_from_its_der_encoding() {
687 let der = [
690 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2e,
691 ];
692 let (element, rest) = read_tlv(&der).unwrap();
693 assert!(rest.is_empty());
694 assert_eq!(
695 oid_of(&element, "test").unwrap().as_str(),
696 "2.16.840.1.101.3.4.1.46"
697 );
698 }
699
700 #[test]
704 fn reads_a_first_arc_that_spans_two_bytes() {
705 let der = [0x06, 0x03, 0x81, 0x34, 0x03];
708 let (element, _) = read_tlv(&der).unwrap();
709 assert_eq!(oid_of(&element, "test").unwrap().as_str(), "2.100.3");
710 }
711
712 #[test]
713 fn reads_a_long_form_length() {
714 let mut der = vec![0x04, 0x81, 0x80];
715 der.extend(std::iter::repeat_n(0xaa, 0x80));
716 let (element, rest) = read_tlv(&der).unwrap();
717 assert_eq!(element.value.len(), 0x80);
718 assert!(rest.is_empty());
719 }
720
721 #[test]
722 fn refuses_indefinite_length_and_truncation() {
723 assert!(read_tlv(&[0x30, 0x80, 0x00, 0x00]).is_err());
724 assert!(read_tlv(&[0x30, 0x05, 0x01]).is_err());
725 assert!(read_tlv(&[0x30]).is_err());
726 assert!(read_tlv(&[]).is_err());
727 }
728
729 const RFC4134_ENVELOPED_DATA: &str = concat!(
736 "MIIBHgYJKoZIhvcNAQcDoIIBDzCCAQsCAQAxgcAwgb0CAQAwJjASMRAwDgYDVQQDEwdDYXJ",
737 "sUlNBAhBGNGvHgABWvBHTbi7NXXHQMA0GCSqGSIb3DQEBAQUABIGAC3EN5nGIiJi2lsGPcP",
738 "2iJ97a4e8kbKQz36zg6Z2i0yx6zYC4mZ7mX7FBs3IWg+f6KgCLx3M1eCbWx8+MDFbbpXadC",
739 "DgO8/nUkUNYeNxJtuzubGgzoyEd8Ch4H/dd9gdzTd+taTEgS0ipdSJuNnkVY4/M652jKKHR",
740 "LFf02hosdR8wQwYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAgtaMXpRwZRNYAgDsiSf8Z9P43",
741 "LrY4OxUk660cu1lXeCSFOSOpOJ7FuVyU=",
742 );
743
744 fn sample_envelope() -> KekEnvelope {
745 KekEnvelope {
746 key_identifier: vec![0x76, 0x3c, 0xbc],
747 wrapped_cek: vec![0x11; 40],
748 nonce: vec![0x22; 12],
749 ciphertext: vec![0x33; 64],
750 tag: vec![0x44; 16],
751 }
752 }
753
754 #[test]
756 fn a_kek_envelope_round_trips() {
757 let envelope = sample_envelope();
758 let der = write_kek_enveloped(&envelope).unwrap();
759 assert_eq!(read_kek_enveloped(&der).unwrap(), envelope);
760 }
761
762 #[test]
765 fn a_kek_envelope_describes_itself() {
766 let der = write_kek_enveloped(&sample_envelope()).unwrap();
767 let facts = describe(&der).unwrap();
768
769 assert_eq!(
770 facts.description.recipient_info,
771 Some(RecipientInfoKind::Kekri)
772 );
773 assert_eq!(
774 facts.description.key_encryption_oid.as_str(),
775 crate::types::oid::AES_256_WRAP
776 );
777 assert_eq!(
778 facts
779 .description
780 .content_encryption_oid
781 .as_ref()
782 .map(Oid::as_str),
783 Some(crate::types::oid::AES_256_GCM)
784 );
785 assert_eq!(
786 facts.recipient_key_identifier,
787 Some(vec![0x76, 0x3c, 0xbc]),
788 "the recipient is named by the key's check value"
789 );
790 }
791
792 #[test]
795 fn a_kek_envelope_carries_bytes_under_the_generic_content_type() {
796 let der = write_kek_enveloped(&sample_envelope()).unwrap();
797 let data_oid = crate::der::object_identifier(DATA).unwrap();
798 assert!(
799 der.windows(data_oid.len()).any(|w| w == data_oid),
800 "the encrypted content is id-data, not a key-specific type"
801 );
802 }
803
804 #[test]
808 fn a_kek_envelope_round_trips_across_the_length_boundary() {
809 for size in [0, 1, 127, 128, 129, 255, 256, 1024, 65_536] {
810 let envelope = KekEnvelope {
811 ciphertext: vec![0x5a; size],
812 ..sample_envelope()
813 };
814 let der = write_kek_enveloped(&envelope).unwrap();
815 assert_eq!(
816 read_kek_enveloped(&der).unwrap(),
817 envelope,
818 "a {size}-byte content did not survive the round trip"
819 );
820 }
821 }
822
823 #[test]
826 fn a_kek_envelope_carries_the_tag_length_it_declares() {
827 for length in [12usize, 13, 14, 15, 16] {
828 let envelope = KekEnvelope {
829 tag: vec![0x44; length],
830 ..sample_envelope()
831 };
832 let der = write_kek_enveloped(&envelope).unwrap();
833 assert_eq!(read_kek_enveloped(&der).unwrap().tag.len(), length);
834 }
835 for length in [0usize, 8, 11, 17] {
836 let envelope = KekEnvelope {
837 tag: vec![0x44; length],
838 ..sample_envelope()
839 };
840 assert!(
841 write_kek_enveloped(&envelope).is_err(),
842 "a {length}-byte tag is not one RFC 5084 admits"
843 );
844 }
845 }
846
847 #[test]
850 fn reading_refuses_what_is_not_this_structure() {
851 let der = write_kek_enveloped(&sample_envelope()).unwrap();
852
853 assert!(read_kek_enveloped(&[]).is_err(), "empty input");
854 assert!(
855 read_kek_enveloped(&der[..der.len() - 1]).is_err(),
856 "truncated input"
857 );
858 let mut trailing = der.clone();
859 trailing.push(0x00);
860 assert!(read_kek_enveloped(&trailing).is_err(), "trailing bytes");
861
862 let other = base64ct::Base64::decode_vec(RFC4134_ENVELOPED_DATA).unwrap();
864 assert!(
865 read_kek_enveloped(&other).is_err(),
866 "an EnvelopedData with a key transport recipient is not this"
867 );
868 }
869
870 #[test]
878 fn reads_the_rfc_4134_enveloped_data_example() {
879 let der = base64ct::Base64::decode_vec(RFC4134_ENVELOPED_DATA).unwrap();
880 assert_eq!(der.len(), 290, "RFC 4134 section 5.1 is 290 bytes");
881
882 let facts = describe(&der).unwrap();
883 assert_eq!(
884 facts.description.recipient_info,
885 Some(RecipientInfoKind::Ktri)
886 );
887 assert_eq!(
888 facts.description.key_encryption_oid.as_str(),
889 crate::types::oid::RSA_ENCRYPTION
890 );
891 assert_eq!(
892 facts
893 .description
894 .content_encryption_oid
895 .as_ref()
896 .map(Oid::as_str),
897 Some("1.2.840.113549.3.7"),
898 "des-EDE3-CBC, as the RFC's dump names it"
899 );
900 assert_eq!(
901 facts.description.kek_wrap_oid, None,
902 "no KEK in key transport"
903 );
904 assert_eq!(
905 facts.recipient_key_identifier, None,
906 "this recipient is named by issuer and serial, which identifies a \
907 certificate rather than a key"
908 );
909
910 assert!(!facts.description.content_is_authenticated());
914 assert!(!crate::types::WrapScheme::CmsAes256Gcm.permits(&facts.description));
915 }
916
917 #[test]
936 fn reads_a_recorded_ml_kem_recipient() {
937 let der = include_bytes!("../tests/fixtures/ml_kem_768_authenveloped.der");
938 let facts = describe(der).unwrap();
939 let description = &facts.description;
940
941 assert_eq!(description.recipient_info, Some(RecipientInfoKind::Kemri));
942 assert_eq!(
944 description.key_encryption_oid.as_str(),
945 "2.16.840.1.101.3.4.4.2"
946 );
947 assert_eq!(
950 description.kdf_oid.as_ref().map(Oid::as_str),
951 Some("1.2.840.113549.1.9.16.3.28")
952 );
953 assert_eq!(
954 description.kek_wrap_oid.as_ref().map(Oid::as_str),
955 Some(crate::types::oid::AES_256_WRAP)
956 );
957 assert!(description.content_is_authenticated());
958 assert!(
959 facts.recipient_key_identifier.is_some(),
960 "the blob names its recipient by key id"
961 );
962 }
963
964 #[test]
973 fn refuses_an_auth_enveloped_data_carrying_auth_attrs() {
974 let der = base64ct::Base64::decode_vec(concat!(
975 "MIHGBgsqhkiG9w0BCRABF6CBtjCBswIBADFEokICAQQwBgQEAQIDBDALBglg",
976 "hkgBZQMEAS0EKHQahB32AHvkD3mve3AQWdpTNQ/d825fM9NJOwFCxtF9DZJ4",
977 "PXsjRgcwOgYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAwDAwMDAwMDAwMD",
978 "AwMCARCADbWcgkV4nh1cv9wwVfGhGjAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN",
979 "AQcBBBCiz7sY6w+LFX3mIoJbWngX",
980 ))
981 .unwrap();
982
983 let error = read_kek_enveloped(&der).unwrap_err();
984 assert!(
985 error.to_string().contains("authAttrs"),
986 "the refusal should name the field: {error}"
987 );
988
989 let facts = describe(&der).expect("the recipient is readable either way");
992 assert_eq!(
993 facts.description.recipient_info,
994 Some(RecipientInfoKind::Kekri)
995 );
996 }
997
998 #[test]
999 fn refuses_bytes_that_are_not_an_enveloped_structure() {
1000 let der = [
1002 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02,
1003 ];
1004 let error = describe(&der).unwrap_err();
1005 assert!(
1006 error.to_string().contains("1.2.840.113549.1.7.2"),
1007 "the error should name what was found: {error}"
1008 );
1009 }
1010}