1use crate::attest::cd_keys::{self, KEY_IDENTIFIER_LEN};
35use crate::cert::der_utils::ecdsa_der_to_raw;
36use crate::cert::x509::AlgorithmIdentifier;
37use crate::crypto::{CanonPkcPublicKeyRef, CanonPkcSignatureRef, Crypto, PublicKey};
38use crate::error::{Error, ErrorCode};
39use crate::tlv::{TLVElement, TLVSequence};
40
41use der::asn1::{AnyRef, ObjectIdentifier, OctetStringRef};
42use der::{
43 Decode, DecodeValue, EncodeValue, FixedTag, Header, Reader, Sequence, Tag, TagNumber, Tagged,
44};
45
46const OID_PKCS7_SIGNED_DATA: ObjectIdentifier =
49 ObjectIdentifier::new_unwrap("1.2.840.113549.1.7.2");
50const OID_PKCS7_DATA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.7.1");
52
53const OID_SHA256: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
56const OID_ECDSA_WITH_SHA256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
58
59const P256_FE_LEN: usize = 32;
61
62const RAW_SIGNATURE_LEN: usize = P256_FE_LEN * 2;
64
65#[allow(unused)]
72struct ContentInfo<'a> {
73 content_type: ObjectIdentifier,
74 signed_data_bytes: &'a [u8],
76}
77
78impl<'a> DecodeValue<'a> for ContentInfo<'a> {
79 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
80 reader.read_nested(header.length, |reader| {
81 let content_type = ObjectIdentifier::decode(reader)?;
83
84 if content_type != OID_PKCS7_SIGNED_DATA {
86 return Err(der::ErrorKind::Failed.into());
87 }
88
89 let context_header = Header::decode(reader)?;
92 if context_header.tag.number() != TagNumber::new(0)
94 || !context_header.tag.is_constructed()
95 {
96 return Err(der::ErrorKind::Failed.into());
97 }
98
99 let signed_data_bytes = reader.read_slice(context_header.length)?;
101
102 Ok(Self {
103 content_type,
104 signed_data_bytes,
105 })
106 })
107 }
108}
109
110impl<'a> FixedTag for ContentInfo<'a> {
111 const TAG: Tag = Tag::Sequence;
112}
113
114impl<'a> EncodeValue for ContentInfo<'a> {
116 fn value_len(&self) -> der::Result<der::Length> {
117 unimplemented!("ContentInfo encoding is not supported")
118 }
119
120 fn encode_value(&self, _writer: &mut impl der::Writer) -> der::Result<()> {
121 unimplemented!("ContentInfo encoding is not supported")
122 }
123}
124
125#[derive(Sequence)]
132struct EncapsulatedContentInfo<'a> {
133 econtent_type: ObjectIdentifier,
134 #[asn1(context_specific = "0", tag_mode = "EXPLICIT")]
135 econtent: OctetStringRef<'a>,
136}
137
138#[allow(unused)]
147struct SignedData<'a> {
148 version: u8,
149 encap_content_info: EncapsulatedContentInfo<'a>,
150 signer_infos: AnyRef<'a>,
153}
154
155impl<'a> DecodeValue<'a> for SignedData<'a> {
156 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
157 reader.read_nested(header.length, |reader| {
158 let version = u8::decode(reader)?;
160 if version != 3 {
161 return Err(der::ErrorKind::Failed.into());
162 }
163
164 let _digest_algorithms = AnyRef::decode(reader)?;
166 if _digest_algorithms.tag() != Tag::Set {
167 return Err(der::ErrorKind::Failed.into());
168 }
169
170 let encap_content_info = EncapsulatedContentInfo::decode(reader)?;
172
173 if encap_content_info.econtent_type != OID_PKCS7_DATA {
175 return Err(der::ErrorKind::Failed.into());
176 }
177
178 let signer_infos = AnyRef::decode(reader)?;
180 if signer_infos.tag() != Tag::Set {
181 return Err(der::ErrorKind::Failed.into());
182 }
183
184 Ok(Self {
185 version,
186 encap_content_info,
187 signer_infos,
188 })
189 })
190 }
191}
192
193impl<'a> FixedTag for SignedData<'a> {
194 const TAG: Tag = Tag::Sequence;
195}
196
197impl<'a> EncodeValue for SignedData<'a> {
199 fn value_len(&self) -> der::Result<der::Length> {
200 unimplemented!("SignedData encoding is not supported")
201 }
202
203 fn encode_value(&self, _writer: &mut impl der::Writer) -> der::Result<()> {
204 unimplemented!("SignedData encoding is not supported")
205 }
206}
207
208#[allow(unused)]
220struct SignerInfo<'a> {
221 version: u8,
222 subject_key_identifier: &'a [u8],
223 digest_algorithm: AlgorithmIdentifier<'a>,
224 signature_algorithm: AlgorithmIdentifier<'a>,
225 signature: OctetStringRef<'a>,
226}
227
228impl<'a> DecodeValue<'a> for SignerInfo<'a> {
229 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
230 reader.read_nested(header.length, |reader| {
231 let version = u8::decode(reader)?;
233 if version != 3 {
234 return Err(der::ErrorKind::Failed.into());
235 }
236
237 let ski_header = Header::decode(reader)?;
239
240 if ski_header.tag.number() != TagNumber::new(0) || ski_header.tag.is_constructed() {
242 return Err(der::ErrorKind::Failed.into());
243 }
244
245 let subject_key_identifier = reader.read_slice(ski_header.length)?;
246
247 if subject_key_identifier.len() != KEY_IDENTIFIER_LEN {
249 return Err(der::ErrorKind::Failed.into());
250 }
251
252 let digest_algorithm = AlgorithmIdentifier::decode(reader)?;
254
255 if digest_algorithm.algorithm != OID_SHA256 {
257 return Err(der::ErrorKind::Failed.into());
258 }
259
260 let signature_algorithm = AlgorithmIdentifier::decode(reader)?;
262
263 if signature_algorithm.algorithm != OID_ECDSA_WITH_SHA256 {
265 return Err(der::ErrorKind::Failed.into());
266 }
267
268 let signature = OctetStringRef::decode(reader)?;
270
271 Ok(Self {
272 version,
273 subject_key_identifier,
274 digest_algorithm,
275 signature_algorithm,
276 signature,
277 })
278 })
279 }
280}
281
282impl<'a> FixedTag for SignerInfo<'a> {
283 const TAG: Tag = Tag::Sequence;
284}
285
286impl<'a> EncodeValue for SignerInfo<'a> {
290 fn value_len(&self) -> der::Result<der::Length> {
291 unimplemented!("SignerInfo encoding is not supported")
292 }
293
294 fn encode_value(&self, _writer: &mut impl der::Writer) -> der::Result<()> {
295 unimplemented!("SignerInfo encoding is not supported")
296 }
297}
298
299pub struct CmsSignedData<'a> {
301 pub signer_key_id: &'a [u8],
303 pub cd_content: &'a [u8],
305 pub signature_raw: [u8; RAW_SIGNATURE_LEN],
307}
308
309impl<'a> CmsSignedData<'a> {
310 pub fn parse(cms_message: &'a [u8]) -> Result<Self, Error> {
342 let content_info = ContentInfo::from_der(cms_message)
344 .map_err(|_| Error::from(ErrorCode::CdInvalidFormat))?;
345
346 let signed_data = SignedData::from_der(content_info.signed_data_bytes)
348 .map_err(|_| Error::from(ErrorCode::CdInvalidFormat))?;
349
350 let cd_content = signed_data.encap_content_info.econtent.as_bytes();
352
353 let signer_info = SignerInfo::from_der(signed_data.signer_infos.value())
355 .map_err(|_| Error::from(ErrorCode::CdInvalidFormat))?;
356
357 let signature_raw = ecdsa_der_to_raw(signer_info.signature.as_bytes())?;
359
360 Ok(Self {
361 signer_key_id: signer_info.subject_key_identifier,
362 cd_content,
363 signature_raw,
364 })
365 }
366}
367
368const CD_TAG_FORMAT_VERSION: u8 = 0;
370const CD_TAG_VENDOR_ID: u8 = 1;
371const CD_TAG_PRODUCT_ID_ARRAY: u8 = 2;
372const CD_TAG_DEVICE_TYPE_ID: u8 = 3;
373const CD_TAG_CERTIFICATE_ID: u8 = 4;
374const CD_TAG_SECURITY_LEVEL: u8 = 5;
375const CD_TAG_SECURITY_INFORMATION: u8 = 6;
376const CD_TAG_VERSION_NUMBER: u8 = 7;
377const CD_TAG_CERTIFICATION_TYPE: u8 = 8;
378const CD_TAG_DAC_ORIGIN_VENDOR_ID: u8 = 9;
379const CD_TAG_DAC_ORIGIN_PRODUCT_ID: u8 = 10;
380const CD_TAG_AUTHORIZED_PAA_LIST: u8 = 11;
381
382pub const MAX_PRODUCT_IDS: usize = 100;
384
385pub const CERTIFICATE_ID_LEN: usize = 19;
387
388pub const MAX_AUTHORIZED_PAA_LIST: usize = 10;
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq)]
393#[cfg_attr(feature = "defmt", derive(defmt::Format))]
394#[repr(u8)]
395pub enum CertificationType {
396 DevelopmentAndTest = 0,
398 Provisional = 1,
400 Official = 2,
402}
403
404impl CertificationType {
405 pub fn from_u8(value: u8) -> Result<Self, Error> {
407 match value {
408 0 => Ok(Self::DevelopmentAndTest),
409 1 => Ok(Self::Provisional),
410 2 => Ok(Self::Official),
411 _ => Err(ErrorCode::CdInvalidFormat.into()),
412 }
413 }
414}
415
416#[derive(Debug, PartialEq, Eq)]
418#[cfg_attr(feature = "defmt", derive(defmt::Format))]
419pub struct CertificationElements {
420 pub format_version: u16,
421 pub vendor_id: u16,
422 pub product_ids: [u16; MAX_PRODUCT_IDS],
423 pub product_ids_count: usize,
424 pub device_type_id: u32,
425 pub certificate_id: [u8; CERTIFICATE_ID_LEN],
426 pub security_level: u8,
427 pub security_information: u16,
428 pub version_number: u16,
429 pub certification_type: CertificationType,
430 pub dac_origin_vendor_id: u16,
432 pub dac_origin_product_id: u16,
434 pub dac_origin_vid_pid_present: bool,
436 pub authorized_paa_list: [[u8; KEY_IDENTIFIER_LEN]; MAX_AUTHORIZED_PAA_LIST],
438 pub authorized_paa_list_count: usize,
440}
441
442impl Default for CertificationElements {
443 fn default() -> Self {
444 Self {
445 format_version: 0,
446 vendor_id: 0,
447 product_ids: [0u16; MAX_PRODUCT_IDS],
448 product_ids_count: 0,
449 device_type_id: 0,
450 certificate_id: [0u8; CERTIFICATE_ID_LEN],
451 security_level: 0,
452 security_information: 0,
453 version_number: 0,
454 certification_type: CertificationType::DevelopmentAndTest,
455 dac_origin_vendor_id: 0,
456 dac_origin_product_id: 0,
457 dac_origin_vid_pid_present: false,
458 authorized_paa_list: [[0u8; KEY_IDENTIFIER_LEN]; MAX_AUTHORIZED_PAA_LIST],
459 authorized_paa_list_count: 0,
460 }
461 }
462}
463
464impl CertificationElements {
465 pub fn decode(cd_content: &[u8]) -> Result<Self, Error> {
475 let elem = TLVElement::new(cd_content);
476 let structure = elem.structure()?;
477
478 let format_version = structure.find_ctx(CD_TAG_FORMAT_VERSION)?.u16()?;
479 if format_version != 1 {
480 return Err(ErrorCode::CdInvalidFormat.into());
481 }
482
483 let (product_ids, product_ids_count) = Self::parse_product_ids(&structure)?;
484 let certificate_id = Self::parse_certificate_id(&structure)?;
485 let (dac_origin_vendor_id, dac_origin_product_id, dac_origin_vid_pid_present) =
486 Self::parse_dac_origin(&structure)?;
487 let (authorized_paa_list, authorized_paa_list_count) =
488 Self::parse_authorized_paa_list(&structure)?;
489
490 Ok(Self {
491 format_version,
492 vendor_id: structure.find_ctx(CD_TAG_VENDOR_ID)?.u16()?,
493 product_ids,
494 product_ids_count,
495 device_type_id: structure.find_ctx(CD_TAG_DEVICE_TYPE_ID)?.u32()?,
496 certificate_id,
497 security_level: structure.find_ctx(CD_TAG_SECURITY_LEVEL)?.u8()?,
498 security_information: structure.find_ctx(CD_TAG_SECURITY_INFORMATION)?.u16()?,
499 version_number: structure.find_ctx(CD_TAG_VERSION_NUMBER)?.u16()?,
500 certification_type: CertificationType::from_u8(
501 structure.find_ctx(CD_TAG_CERTIFICATION_TYPE)?.u8()?,
502 )?,
503 dac_origin_vendor_id,
504 dac_origin_product_id,
505 dac_origin_vid_pid_present,
506 authorized_paa_list,
507 authorized_paa_list_count,
508 })
509 }
510
511 fn parse_product_ids(
514 structure: &TLVSequence,
515 ) -> Result<([u16; MAX_PRODUCT_IDS], usize), Error> {
516 let pid_array = structure.find_ctx(CD_TAG_PRODUCT_ID_ARRAY)?;
517 let pid_seq = pid_array.array()?;
518
519 let mut product_ids = [0u16; MAX_PRODUCT_IDS];
520 let mut count = 0usize;
521
522 for pid_elem in pid_seq.iter() {
523 let pid_elem: TLVElement<'_> = pid_elem?;
524 if count >= MAX_PRODUCT_IDS {
525 return Err(ErrorCode::CdInvalidFormat.into());
526 }
527 product_ids[count] = pid_elem.u16()?;
528 count += 1;
529 }
530
531 if count == 0 {
532 return Err(ErrorCode::CdInvalidFormat.into());
533 }
534
535 Ok((product_ids, count))
536 }
537
538 fn parse_certificate_id(structure: &TLVSequence) -> Result<[u8; CERTIFICATE_ID_LEN], Error> {
541 let cert_id_str = structure.find_ctx(CD_TAG_CERTIFICATE_ID)?.utf8()?;
542 if cert_id_str.len() != CERTIFICATE_ID_LEN {
543 return Err(ErrorCode::CdInvalidFormat.into());
544 }
545
546 let mut certificate_id = [0u8; CERTIFICATE_ID_LEN];
547 certificate_id.copy_from_slice(cert_id_str.as_bytes());
548 Ok(certificate_id)
549 }
550
551 fn parse_dac_origin(structure: &TLVSequence) -> Result<(u16, u16, bool), Error> {
554 let vid_elem = structure.find_ctx(CD_TAG_DAC_ORIGIN_VENDOR_ID)?;
555 let pid_elem = structure.find_ctx(CD_TAG_DAC_ORIGIN_PRODUCT_ID)?;
556
557 if vid_elem.is_empty() != pid_elem.is_empty() {
559 return Err(ErrorCode::CdInvalidFormat.into());
560 }
561
562 if !vid_elem.is_empty() {
563 Ok((vid_elem.u16()?, pid_elem.u16()?, true))
564 } else {
565 Ok((0, 0, false))
566 }
567 }
568
569 fn parse_authorized_paa_list(
572 structure: &TLVSequence,
573 ) -> Result<([[u8; KEY_IDENTIFIER_LEN]; MAX_AUTHORIZED_PAA_LIST], usize), Error> {
574 let paa_elem = structure.find_ctx(CD_TAG_AUTHORIZED_PAA_LIST)?;
575
576 let mut authorized_paa_list = [[0u8; KEY_IDENTIFIER_LEN]; MAX_AUTHORIZED_PAA_LIST];
577 let mut paa_count = 0usize;
578
579 if !paa_elem.is_empty() {
580 let paa_seq = paa_elem.array()?;
581 for paa_entry in paa_seq.iter() {
582 let paa_entry: TLVElement<'_> = paa_entry?;
583 if paa_count >= MAX_AUTHORIZED_PAA_LIST {
584 return Err(ErrorCode::CdInvalidFormat.into());
585 }
586 let paa_bytes = paa_entry.str()?;
587 if paa_bytes.len() != KEY_IDENTIFIER_LEN {
588 return Err(ErrorCode::CdInvalidFormat.into());
589 }
590 authorized_paa_list[paa_count].copy_from_slice(paa_bytes);
591 paa_count += 1;
592 }
593 }
594
595 Ok((authorized_paa_list, paa_count))
596 }
597
598 pub fn verify<C: Crypto>(
614 crypto: C,
615 cms_message: &[u8],
616 allow_test_cd_signing_key: bool,
617 ) -> Result<Self, Error> {
618 let cms = CmsSignedData::parse(cms_message)?;
620
621 let pubkey_bytes = cd_keys::lookup_cd_signing_key(cms.signer_key_id)
623 .ok_or(Error::new(ErrorCode::CdSigningKeyNotFound))?;
624
625 let is_test_key = cd_keys::is_test_cd_key(cms.signer_key_id);
627 if is_test_key && !allow_test_cd_signing_key {
628 return Err(ErrorCode::CdSigningKeyNotFound.into());
629 }
630
631 let pubkey_ref = CanonPkcPublicKeyRef::try_new(pubkey_bytes)?;
633 let pubkey = crypto.pub_key(pubkey_ref)?;
634
635 let sig_ref = CanonPkcSignatureRef::new(&cms.signature_raw);
636
637 let valid = pubkey.verify(cms.cd_content, sig_ref)?;
638 if !valid {
639 return Err(ErrorCode::CdInvalidSignature.into());
640 }
641
642 let cd = CertificationElements::decode(cms.cd_content)?;
644
645 if is_test_key && cd.certification_type == CertificationType::Official {
648 return Err(ErrorCode::CdSigningKeyNotFound.into());
649 }
650
651 Ok(cd)
652 }
653
654 pub fn validate(&self, device_info: &DeviceInfoForAttestation) -> Result<(), Error> {
679 if self.format_version != 1 {
681 return Err(ErrorCode::CdInvalidFormat.into());
682 }
683
684 if self.vendor_id != device_info.vendor_id {
688 return Err(ErrorCode::CdInvalidVendorId.into());
689 }
690
691 if !product_id_in_list(device_info.product_id, self) {
693 return Err(ErrorCode::CdInvalidProductId.into());
694 }
695
696 if self.dac_origin_vid_pid_present {
698 if device_info.dac_vendor_id != self.dac_origin_vendor_id {
700 return Err(ErrorCode::CdInvalidVendorId.into());
701 }
702 if device_info.pai_vendor_id != self.dac_origin_vendor_id {
703 return Err(ErrorCode::CdInvalidVendorId.into());
704 }
705 if device_info.dac_product_id != self.dac_origin_product_id {
706 return Err(ErrorCode::CdInvalidProductId.into());
707 }
708 if device_info.pai_product_id != 0
709 && device_info.pai_product_id != self.dac_origin_product_id
710 {
711 return Err(ErrorCode::CdInvalidProductId.into());
712 }
713 } else {
714 if device_info.dac_vendor_id != self.vendor_id {
716 return Err(ErrorCode::CdInvalidVendorId.into());
717 }
718 if device_info.pai_vendor_id != self.vendor_id {
719 return Err(ErrorCode::CdInvalidVendorId.into());
720 }
721 if !product_id_in_list(device_info.dac_product_id, self) {
722 return Err(ErrorCode::CdInvalidProductId.into());
723 }
724 if device_info.pai_product_id != 0
725 && !product_id_in_list(device_info.pai_product_id, self)
726 {
727 return Err(ErrorCode::CdInvalidProductId.into());
728 }
729 }
730
731 if self.authorized_paa_list_count > 0 {
733 let found = self.authorized_paa_list[..self.authorized_paa_list_count]
734 .contains(&device_info.paa_skid);
735 if !found {
736 return Err(ErrorCode::CdInvalidPaa.into());
737 }
738 }
739
740 Ok(())
741 }
742}
743
744pub struct DeviceInfoForAttestation {
749 pub vendor_id: u16,
751 pub product_id: u16,
753 pub dac_vendor_id: u16,
755 pub dac_product_id: u16,
757 pub pai_vendor_id: u16,
759 pub pai_product_id: u16,
761 pub paa_skid: [u8; KEY_IDENTIFIER_LEN],
763}
764
765fn product_id_in_list(pid: u16, cd: &CertificationElements) -> bool {
767 cd.product_ids[..cd.product_ids_count].contains(&pid)
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773 use crate::crypto::test_only_crypto;
774
775 fn expected_cd_01() -> CertificationElements {
790 let mut cd = CertificationElements {
791 format_version: 1,
792 vendor_id: 0xFFF1,
793 product_ids_count: 1,
794 device_type_id: 0x1234,
795 version_number: 0x2694,
796 ..CertificationElements::default()
797 };
798 cd.product_ids[0] = 0x8000;
799 cd.certificate_id.copy_from_slice(b"ZIG20141ZB330001-24");
800 cd
801 }
802
803 const TEST_CMS_CD_CONTENT_01: &[u8] = &[
804 0x15, 0x24, 0x00, 0x01, 0x25, 0x01, 0xf1, 0xff, 0x36, 0x02, 0x05, 0x00, 0x80, 0x18, 0x25,
805 0x03, 0x34, 0x12, 0x2c, 0x04, 0x13, 0x5a, 0x49, 0x47, 0x32, 0x30, 0x31, 0x34, 0x31, 0x5a,
806 0x42, 0x33, 0x33, 0x30, 0x30, 0x30, 0x31, 0x2d, 0x32, 0x34, 0x24, 0x05, 0x00, 0x24, 0x06,
807 0x00, 0x25, 0x07, 0x94, 0x26, 0x24, 0x08, 0x00, 0x18,
808 ];
809
810 const TEST_CMS_SIGNED_MESSAGE_01: &[u8] = &[
811 0x30, 0x81, 0xe8, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0,
812 0x81, 0xda, 0x30, 0x81, 0xd7, 0x02, 0x01, 0x03, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
813 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x45, 0x06, 0x09, 0x2a, 0x86, 0x48,
814 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x38, 0x04, 0x36, 0x15, 0x24, 0x00, 0x01, 0x25,
815 0x01, 0xf1, 0xff, 0x36, 0x02, 0x05, 0x00, 0x80, 0x18, 0x25, 0x03, 0x34, 0x12, 0x2c, 0x04,
816 0x13, 0x5a, 0x49, 0x47, 0x32, 0x30, 0x31, 0x34, 0x31, 0x5a, 0x42, 0x33, 0x33, 0x30, 0x30,
817 0x30, 0x31, 0x2d, 0x32, 0x34, 0x24, 0x05, 0x00, 0x24, 0x06, 0x00, 0x25, 0x07, 0x94, 0x26,
818 0x24, 0x08, 0x00, 0x18, 0x31, 0x7c, 0x30, 0x7a, 0x02, 0x01, 0x03, 0x80, 0x14, 0x62, 0xfa,
819 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04,
820 0xf3, 0x71, 0x60, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
821 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x04, 0x46,
822 0x30, 0x44, 0x02, 0x20, 0x43, 0xa6, 0x3f, 0x2b, 0x94, 0x3d, 0xf3, 0x3c, 0x38, 0xb3, 0xe0,
823 0x2f, 0xca, 0xa7, 0x5f, 0xe3, 0x53, 0x2a, 0xeb, 0xbf, 0x5e, 0x63, 0xf5, 0xbb, 0xdb, 0xc0,
824 0xb1, 0xf0, 0x1d, 0x3c, 0x4f, 0x60, 0x02, 0x20, 0x4c, 0x1a, 0xbf, 0x5f, 0x18, 0x07, 0xb8,
825 0x18, 0x94, 0xb1, 0x57, 0x6c, 0x47, 0xe4, 0x72, 0x4e, 0x4d, 0x96, 0x6c, 0x61, 0x2e, 0xd3,
826 0xfa, 0x25, 0xc1, 0x18, 0xc3, 0xf2, 0xb3, 0xf9, 0x03, 0x69,
827 ];
828
829 fn expected_cd_02() -> CertificationElements {
843 let mut cd = CertificationElements {
844 format_version: 1,
845 vendor_id: 0xFFF2,
846 product_ids_count: 2,
847 device_type_id: 0x1234,
848 version_number: 0x2694,
849 dac_origin_vendor_id: 0xFFF1,
850 dac_origin_product_id: 0x8000,
851 dac_origin_vid_pid_present: true,
852 ..CertificationElements::default()
853 };
854 cd.product_ids[0] = 0x8001;
855 cd.product_ids[1] = 0x8002;
856 cd.certificate_id.copy_from_slice(b"ZIG20142ZB330002-24");
857 cd
858 }
859
860 const TEST_CMS_CD_CONTENT_02: &[u8] = &[
861 0x15, 0x24, 0x00, 0x01, 0x25, 0x01, 0xf2, 0xff, 0x36, 0x02, 0x05, 0x01, 0x80, 0x05, 0x02,
862 0x80, 0x18, 0x25, 0x03, 0x34, 0x12, 0x2c, 0x04, 0x13, 0x5a, 0x49, 0x47, 0x32, 0x30, 0x31,
863 0x34, 0x32, 0x5a, 0x42, 0x33, 0x33, 0x30, 0x30, 0x30, 0x32, 0x2d, 0x32, 0x34, 0x24, 0x05,
864 0x00, 0x24, 0x06, 0x00, 0x25, 0x07, 0x94, 0x26, 0x24, 0x08, 0x00, 0x25, 0x09, 0xf1, 0xff,
865 0x25, 0x0a, 0x00, 0x80, 0x18,
866 ];
867
868 const TEST_CMS_SIGNED_MESSAGE_02: &[u8] = &[
869 0x30, 0x81, 0xf5, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0,
870 0x81, 0xe7, 0x30, 0x81, 0xe4, 0x02, 0x01, 0x03, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
871 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x50, 0x06, 0x09, 0x2a, 0x86, 0x48,
872 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x43, 0x04, 0x41, 0x15, 0x24, 0x00, 0x01, 0x25,
873 0x01, 0xf2, 0xff, 0x36, 0x02, 0x05, 0x01, 0x80, 0x05, 0x02, 0x80, 0x18, 0x25, 0x03, 0x34,
874 0x12, 0x2c, 0x04, 0x13, 0x5a, 0x49, 0x47, 0x32, 0x30, 0x31, 0x34, 0x32, 0x5a, 0x42, 0x33,
875 0x33, 0x30, 0x30, 0x30, 0x32, 0x2d, 0x32, 0x34, 0x24, 0x05, 0x00, 0x24, 0x06, 0x00, 0x25,
876 0x07, 0x94, 0x26, 0x24, 0x08, 0x00, 0x25, 0x09, 0xf1, 0xff, 0x25, 0x0a, 0x00, 0x80, 0x18,
877 0x31, 0x7e, 0x30, 0x7c, 0x02, 0x01, 0x03, 0x80, 0x14, 0x62, 0xfa, 0x82, 0x33, 0x59, 0xac,
878 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30,
879 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0a, 0x06,
880 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x04, 0x48, 0x30, 0x46, 0x02, 0x21,
881 0x00, 0x92, 0x62, 0x96, 0xf7, 0x57, 0x81, 0x58, 0xbe, 0x7c, 0x45, 0x93, 0x88, 0x33, 0x6c,
882 0xa7, 0x38, 0x37, 0x66, 0xc9, 0xee, 0xdd, 0x98, 0x55, 0xcb, 0xda, 0x6f, 0x4c, 0xf6, 0xbd,
883 0xf4, 0x32, 0x11, 0x02, 0x21, 0x00, 0xe0, 0xdb, 0xf4, 0xa2, 0xbc, 0xec, 0x4e, 0xa2, 0x74,
884 0xba, 0xf0, 0xde, 0xa2, 0x08, 0xb3, 0x36, 0x5c, 0x6e, 0xd5, 0x44, 0x08, 0x6d, 0x10, 0x1a,
885 0xfd, 0xaf, 0x07, 0x9a, 0x2c, 0x23, 0xe0, 0xde,
886 ];
887
888 #[test]
891 fn test_parse_cms_signed_data_01() {
892 let cms = unwrap!(CmsSignedData::parse(TEST_CMS_SIGNED_MESSAGE_01));
893
894 assert_eq!(cms.signer_key_id, &cd_keys::TEST_CD_KID);
896
897 assert_eq!(cms.cd_content, TEST_CMS_CD_CONTENT_01);
899
900 assert!(cms.signature_raw.iter().any(|&b| b != 0));
902 }
903
904 #[test]
905 fn test_parse_cms_signed_data_02() {
906 let cms = unwrap!(CmsSignedData::parse(TEST_CMS_SIGNED_MESSAGE_02));
907
908 assert_eq!(cms.signer_key_id, &cd_keys::TEST_CD_KID);
909 assert_eq!(cms.cd_content, TEST_CMS_CD_CONTENT_02);
910 }
911
912 #[test]
913 fn test_cms_extract_cd_content() {
914 let cms = unwrap!(CmsSignedData::parse(TEST_CMS_SIGNED_MESSAGE_01));
915 assert_eq!(cms.cd_content, TEST_CMS_CD_CONTENT_01);
916 }
917
918 #[test]
919 fn test_cms_extract_key_id() {
920 let cms = unwrap!(CmsSignedData::parse(TEST_CMS_SIGNED_MESSAGE_01));
921 assert_eq!(cms.signer_key_id, &cd_keys::TEST_CD_KID);
922 }
923
924 #[test]
925 fn test_parse_cms_invalid_data() {
926 assert!(CmsSignedData::parse(&[]).is_err());
928
929 assert!(CmsSignedData::parse(&[0x01, 0x02, 0x03]).is_err());
931
932 assert!(CmsSignedData::parse(&[0x30, 0x06, 0x06, 0x02, 0x55, 0x04, 0x00, 0x00]).is_err());
934 }
935
936 #[test]
939 fn test_decode_cd_content_01() {
940 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_01));
941 assert_eq!(cd, expected_cd_01());
942 }
943
944 #[test]
945 fn test_decode_cd_content_02() {
946 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_02));
947 assert_eq!(cd, expected_cd_02());
948 }
949
950 #[test]
953 fn test_verify_cd_01_with_test_key_allowed() {
954 let crypto = test_only_crypto();
955 let cd = unwrap!(CertificationElements::verify(
956 &crypto,
957 TEST_CMS_SIGNED_MESSAGE_01,
958 true,
959 ));
960
961 assert_eq!(cd, expected_cd_01());
962 }
963
964 #[test]
965 fn test_verify_cd_02_with_test_key_allowed() {
966 let crypto = test_only_crypto();
967 let cd = unwrap!(CertificationElements::verify(
968 &crypto,
969 TEST_CMS_SIGNED_MESSAGE_02,
970 true,
971 ));
972
973 assert_eq!(cd, expected_cd_02());
974 }
975
976 #[test]
977 fn test_verify_cd_test_key_not_allowed() {
978 let crypto = test_only_crypto();
979 let result = CertificationElements::verify(
980 &crypto,
981 TEST_CMS_SIGNED_MESSAGE_01,
982 false, );
984
985 assert_eq!(
986 result.map_err(|e| e.code()),
987 Err(ErrorCode::CdSigningKeyNotFound)
988 );
989 }
990
991 #[test]
994 fn test_validate_cd_success_basic() {
995 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_01));
996 let device_info = DeviceInfoForAttestation {
997 vendor_id: 0xFFF1,
998 product_id: 0x8000,
999 dac_vendor_id: 0xFFF1,
1000 dac_product_id: 0x8000,
1001 pai_vendor_id: 0xFFF1,
1002 pai_product_id: 0, paa_skid: [0u8; KEY_IDENTIFIER_LEN],
1004 };
1005
1006 unwrap!(cd.validate(&device_info));
1007 }
1008
1009 #[test]
1010 fn test_validate_cd_wrong_vendor_id() {
1011 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_01));
1012 let device_info = DeviceInfoForAttestation {
1013 vendor_id: 0xFFF2, product_id: 0x8000,
1015 dac_vendor_id: 0xFFF1,
1016 dac_product_id: 0x8000,
1017 pai_vendor_id: 0xFFF1,
1018 pai_product_id: 0,
1019 paa_skid: [0u8; KEY_IDENTIFIER_LEN],
1020 };
1021
1022 assert_eq!(
1023 cd.validate(&device_info).map_err(|e| e.code()),
1024 Err(ErrorCode::CdInvalidVendorId)
1025 );
1026 }
1027
1028 #[test]
1029 fn test_validate_cd_wrong_product_id() {
1030 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_01));
1031 let device_info = DeviceInfoForAttestation {
1032 vendor_id: 0xFFF1,
1033 product_id: 0x9999, dac_vendor_id: 0xFFF1,
1035 dac_product_id: 0x8000,
1036 pai_vendor_id: 0xFFF1,
1037 pai_product_id: 0,
1038 paa_skid: [0u8; KEY_IDENTIFIER_LEN],
1039 };
1040
1041 assert_eq!(
1042 cd.validate(&device_info).map_err(|e| e.code()),
1043 Err(ErrorCode::CdInvalidProductId)
1044 );
1045 }
1046
1047 #[test]
1048 fn test_validate_cd_wrong_dac_vendor_id() {
1049 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_01));
1051 let device_info = DeviceInfoForAttestation {
1052 vendor_id: 0xFFF1,
1053 product_id: 0x8000,
1054 dac_vendor_id: 0xFFF2, dac_product_id: 0x8000,
1056 pai_vendor_id: 0xFFF1,
1057 pai_product_id: 0,
1058 paa_skid: [0u8; KEY_IDENTIFIER_LEN],
1059 };
1060
1061 assert_eq!(
1062 cd.validate(&device_info).map_err(|e| e.code()),
1063 Err(ErrorCode::CdInvalidVendorId)
1064 );
1065 }
1066
1067 #[test]
1068 fn test_validate_cd_with_dac_origin() {
1069 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_02));
1071 let device_info = DeviceInfoForAttestation {
1072 vendor_id: 0xFFF2,
1073 product_id: 0x8001, dac_vendor_id: 0xFFF1, dac_product_id: 0x8000, pai_vendor_id: 0xFFF1, pai_product_id: 0,
1078 paa_skid: [0u8; KEY_IDENTIFIER_LEN],
1079 };
1080
1081 unwrap!(cd.validate(&device_info));
1082 }
1083
1084 #[test]
1085 fn test_validate_cd_dac_origin_wrong_dac_vid() {
1086 let cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_02));
1087 let device_info = DeviceInfoForAttestation {
1088 vendor_id: 0xFFF2,
1089 product_id: 0x8001,
1090 dac_vendor_id: 0xFFF2, dac_product_id: 0x8000,
1092 pai_vendor_id: 0xFFF1,
1093 pai_product_id: 0,
1094 paa_skid: [0u8; KEY_IDENTIFIER_LEN],
1095 };
1096
1097 assert_eq!(
1098 cd.validate(&device_info).map_err(|e| e.code()),
1099 Err(ErrorCode::CdInvalidVendorId)
1100 );
1101 }
1102
1103 #[test]
1104 fn test_validate_cd_wrong_format_version() {
1105 let mut cd = unwrap!(CertificationElements::decode(TEST_CMS_CD_CONTENT_01));
1107 cd.format_version = 2;
1108 let device_info = DeviceInfoForAttestation {
1109 vendor_id: 0xFFF1,
1110 product_id: 0x8000,
1111 dac_vendor_id: 0xFFF1,
1112 dac_product_id: 0x8000,
1113 pai_vendor_id: 0xFFF1,
1114 pai_product_id: 0,
1115 paa_skid: [0u8; KEY_IDENTIFIER_LEN],
1116 };
1117
1118 assert_eq!(
1119 cd.validate(&device_info).map_err(|e| e.code()),
1120 Err(ErrorCode::CdInvalidFormat)
1121 );
1122 }
1123
1124 #[test]
1127 fn test_certification_type_from_u8() {
1128 assert_eq!(
1129 unwrap!(CertificationType::from_u8(0)),
1130 CertificationType::DevelopmentAndTest
1131 );
1132 assert_eq!(
1133 unwrap!(CertificationType::from_u8(1)),
1134 CertificationType::Provisional
1135 );
1136 assert_eq!(
1137 unwrap!(CertificationType::from_u8(2)),
1138 CertificationType::Official
1139 );
1140 assert!(CertificationType::from_u8(3).is_err());
1141 assert!(CertificationType::from_u8(255).is_err());
1142 }
1143}