From 14e4bec260fad061b201ff4b3c1b14346f0d0a85 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Thu, 27 Jan 2022 18:51:56 -0500 Subject: [PATCH 1/4] add support for RFC5914 structs. make adjusts to some prior structure definitions to work around macro-related issues. Notes on non-use of macros In trust_anchor_format.rs - Choice was not used on TrustAnchorChoice owing to types not available in Asn1Type enum - Sequence was not used on TrustAnchorInfo owing to lack of DecodeValue required by context_specific error[E0277]: the trait bound `TrustAnchorInfo<'a>: DecodeValue<'_>` is not satisfied --> x509/src/trust_anchor_format.rs:295:22 | 295 | .context_specific::>(TAC_TA_INFO_TAG, TagMode::Explicit)?; | ^^^^^^^^^^^^^^^^ the trait `DecodeValue<'_>` is not implemented for `TrustAnchorInfo<'a>` | note: required by a bound in `Decoder::<'a>::context_specific` --> der/src/decoder.rs:172:12 | 172 | T: DecodeValue<'a> + FixedTag, | ^^^^^^^^^^^^^^^ required by this bound in `Decoder::<'a>::context_specific` - Sequence was not used on CertPathControls to use decode_implicit for each field (trailing bits were left using the default decoder even with mods to Certificate and NameConstraints) In certificate.rs - Sequence was not used on Certificate due to same issue as described above for TrustAnchorInfo (i.e., tension between use of DecodeValue and Decodable) - Sequence was not used on NameConstraints for the same DecodeValue vs Decodable issue. --- x509/src/certificate.rs | 31 +- x509/src/lib.rs | 1 + x509/src/pkix_extensions.rs | 25 +- x509/src/trust_anchor_format.rs | 343 +++++++++++++++ x509/tests/examples/eca.der | Bin 0 -> 859 bytes x509/tests/examples/eca_policies.ta | Bin 0 -> 1954 bytes x509/tests/examples/entrust.der | Bin 0 -> 936 bytes x509/tests/examples/entrust_dnConstraint.ta | Bin 0 -> 1443 bytes x509/tests/examples/exostar.der | Bin 0 -> 972 bytes x509/tests/examples/exostar_policyFlags.ta | Bin 0 -> 1495 bytes x509/tests/examples/raytheon.der | Bin 0 -> 907 bytes .../examples/raytheon_pathLenConstraint.ta | Bin 0 -> 1324 bytes x509/tests/trust_anchor_format.rs | 410 ++++++++++++++++++ 13 files changed, 795 insertions(+), 15 deletions(-) create mode 100644 x509/src/trust_anchor_format.rs create mode 100644 x509/tests/examples/eca.der create mode 100755 x509/tests/examples/eca_policies.ta create mode 100644 x509/tests/examples/entrust.der create mode 100755 x509/tests/examples/entrust_dnConstraint.ta create mode 100644 x509/tests/examples/exostar.der create mode 100755 x509/tests/examples/exostar_policyFlags.ta create mode 100644 x509/tests/examples/raytheon.der create mode 100644 x509/tests/examples/raytheon_pathLenConstraint.ta create mode 100644 x509/tests/trust_anchor_format.rs diff --git a/x509/src/certificate.rs b/x509/src/certificate.rs index 85ac1cfc4..57528c6a3 100644 --- a/x509/src/certificate.rs +++ b/x509/src/certificate.rs @@ -2,7 +2,7 @@ use crate::{Name, Validity}; use der::asn1::{BitString, ContextSpecific, ObjectIdentifier, UIntBytes}; -use der::{Sequence, TagMode, TagNumber}; +use der::{DecodeValue, Decoder, Length, Sequence, TagMode, TagNumber}; use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo}; /// returns false in support of integer DEFAULT fields set to 0 @@ -192,7 +192,7 @@ impl<'a> ::core::fmt::Debug for TBSCertificate<'a> { /// ``` /// /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 -#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Certificate<'a> { /// tbsCertificate TBSCertificate, pub tbs_certificate: TBSCertificate<'a>, @@ -202,6 +202,33 @@ pub struct Certificate<'a> { pub signature: BitString<'a>, } +impl<'a> DecodeValue<'a> for Certificate<'a> { + fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result { + let tbs_certificate = decoder.decode()?; + let signature_algorithm = decoder.decode()?; + let signature = decoder.decode()?; + Ok(Self { + tbs_certificate, + signature_algorithm, + signature, + }) + // }) + } +} + +impl<'a> ::der::Sequence<'a> for Certificate<'a> { + fn fields(&self, f: F) -> ::der::Result + where + F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, + { + f(&[ + &self.tbs_certificate, + &self.signature_algorithm, + &self.signature, + ]) + } +} + /// Extension as defined in [RFC 5280 Section 4.1.2.9]. /// /// The ASN.1 definition for Extension objects is below. The extnValue type may be further parsed using a decoder corresponding to the extnID value. diff --git a/x509/src/lib.rs b/x509/src/lib.rs index 746a7da82..f711ba27c 100644 --- a/x509/src/lib.rs +++ b/x509/src/lib.rs @@ -22,6 +22,7 @@ pub mod pkix_extensions; pub mod pkix_oids; mod rdn; mod time; +pub mod trust_anchor_format; mod validity; pub use crate::{ diff --git a/x509/src/pkix_extensions.rs b/x509/src/pkix_extensions.rs index be00ff5a5..f15b960a9 100644 --- a/x509/src/pkix_extensions.rs +++ b/x509/src/pkix_extensions.rs @@ -338,20 +338,19 @@ pub struct NameConstraints<'a> { const PERMITTED_SUBTREES_TAG: TagNumber = TagNumber::new(0); const EXCLUDED_SUBTREES_TAG: TagNumber = TagNumber::new(1); -impl<'a> ::der::Decodable<'a> for NameConstraints<'a> { - fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { - decoder.sequence(|decoder| { - let permitted_subtrees = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? - .map(|cs| cs.value); - let excluded_subtrees = - ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? - .map(|cs| cs.value); - Ok(Self { - permitted_subtrees, - excluded_subtrees, - }) +impl<'a> DecodeValue<'a> for NameConstraints<'a> { + fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result { + let permitted_subtrees = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? + .map(|cs| cs.value); + let excluded_subtrees = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? + .map(|cs| cs.value); + Ok(Self { + permitted_subtrees, + excluded_subtrees, }) + // }) } } diff --git a/x509/src/trust_anchor_format.rs b/x509/src/trust_anchor_format.rs new file mode 100644 index 000000000..ead33f489 --- /dev/null +++ b/x509/src/trust_anchor_format.rs @@ -0,0 +1,343 @@ +//! Trust anchor-related structures as defined in RFC 5914 + +use crate::{Certificate, CertificatePolicies, Extensions, Name, NameConstraints}; +use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String}; +use der::{ + DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Tag, TagMode, + TagNumber, +}; +use spki::SubjectPublicKeyInfo; + +/// TrustAnchorInfo ::= SEQUENCE { +/// version TrustAnchorInfoVersion DEFAULT v1, +/// pubKey SubjectPublicKeyInfo, +/// keyId KeyIdentifier, +/// taTitle TrustAnchorTitle OPTIONAL, +/// certPath CertPathControls OPTIONAL, +/// exts \[1\] EXPLICIT Extensions OPTIONAL, +/// taTitleLangTag \[2\] UTF8String OPTIONAL } +/// +/// TrustAnchorInfoVersion ::= INTEGER { v1(1) } +/// +/// TrustAnchorTitle ::= UTF8String (SIZE (1..64)) +#[derive(Clone, Eq, PartialEq)] +pub struct TrustAnchorInfo<'a> { + /// version TrustAnchorInfoVersion DEFAULT v1, + pub version: Option, + + /// pubKey SubjectPublicKeyInfo, + pub pub_key: SubjectPublicKeyInfo<'a>, + + /// keyId KeyIdentifier, + pub key_id: OctetString<'a>, + + /// taTitle TrustAnchorTitle OPTIONAL, + pub ta_title: Option>, + + /// certPath CertPathControls OPTIONAL, + pub cert_path: Option>, + + /// exts \[1\] EXPLICIT Extensions OPTIONAL, + pub extensions: Option>, + + /// taTitleLangTag \[2\] UTF8String OPTIONAL } + pub ta_title_lang_tag: Option>, +} + +// impl<'a> ::der::Decodable<'a> for TrustAnchorInfo<'a> { +// fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { +impl<'a> DecodeValue<'a> for TrustAnchorInfo<'a> { + fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result { + let version = match decoder.decode()? { + Some(v) => Some(v), + _ => Some(1), + }; + + let pub_key = decoder.decode()?; + let key_id = decoder.decode()?; + let ta_title = decoder.decode()?; + let cert_path = decoder.decode()?; + let extensions = + ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N1)? + .map(|cs| cs.value); + let ta_title_lang_tag = + ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N2)? + .map(|cs| cs.value); + Ok(Self { + version, + pub_key, + key_id, + ta_title, + cert_path, + extensions, + ta_title_lang_tag, + }) + } +} + +const TAF_EXTENSIONS_TAG: TagNumber = TagNumber::new(1); +const TA_TITLE_LANG_TAG: TagNumber = TagNumber::new(0); +impl<'a> ::der::Sequence<'a> for TrustAnchorInfo<'a> { + fn fields(&self, f: F) -> ::der::Result + where + F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, + { + #[allow(unused_imports)] + use core::convert::TryFrom; + f(&[ + &::der::asn1::OptionalRef(if self.version == Some(1) { + None + } else { + Some(&self.version) + }), + &self.pub_key, + &self.key_id, + &self.ta_title, + &self.cert_path, + &self.extensions.as_ref().map(|exts| ContextSpecific { + tag_number: TAF_EXTENSIONS_TAG, + tag_mode: TagMode::Explicit, + value: exts.clone(), + }), + &self + .ta_title_lang_tag + .as_ref() + .map(|ta_title_lang_tag| ContextSpecific { + tag_number: TA_TITLE_LANG_TAG, + tag_mode: TagMode::Implicit, + value: *ta_title_lang_tag, + }), + ]) + } +} + +impl<'a> ::core::fmt::Debug for TrustAnchorInfo<'a> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.write_fmt(format_args!("\n\tVersion: {:02X?}\n", self.version))?; + f.write_fmt(format_args!("\tPublic Key Info: {:?}\n", self.pub_key))?; + f.write_fmt(format_args!("\tKey ID: {:?}\n", self.key_id))?; + f.write_fmt(format_args!("\tTA title: {:?}\n", self.ta_title))?; + f.write_fmt(format_args!( + "\tTA title language tag: {:?}\n", + self.ta_title_lang_tag + ))?; + f.write_fmt(format_args!( + "\tCertificate path controls: {:?}\n", + self.cert_path + ))?; + if let Some(exts) = self.extensions.as_ref() { + for (i, e) in exts.iter().enumerate() { + f.write_fmt(format_args!("\tExtension #{}: {:?}\n", i, e))?; + } + } else { + f.write_fmt(format_args!("\tExtensions: None\n"))?; + } + Ok(()) + } +} + +/// CertPathControls ::= SEQUENCE { +/// taName Name, +/// certificate \[0\] Certificate OPTIONAL, +/// policySet \[1\] CertificatePolicies OPTIONAL, +/// policyFlags \[2\] CertPolicyFlags OPTIONAL, +/// nameConstr \[3\] NameConstraints OPTIONAL, +/// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL} +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CertPathControls<'a> { + /// taName Name, + pub ta_name: Name<'a>, + + /// certificate \[0\] Certificate OPTIONAL, + pub certificate: Option>, + + /// policySet \[1\] CertificatePolicies OPTIONAL, + pub policy_set: Option>, + + /// policyFlags \[2\] CertPolicyFlags OPTIONAL, + pub policy_flags: Option>, + + /// nameConstr \[3\] NameConstraints OPTIONAL, + pub name_constr: Option>, + + /// pathLenConstraint\[4\] INTEGER (0..MAX) OPTIONAL} + pub path_len_constraint: Option, +} + +impl<'a> ::der::Decodable<'a> for CertPathControls<'a> { + fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { + decoder.sequence(|decoder| { + let ta_name = decoder.decode()?; + + let certificate = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? + .map(|cs| cs.value); + let policy_set = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? + .map(|cs| cs.value); + let policy_flags = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N2)? + .map(|cs| cs.value); + + let name_constr = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N3)? + .map(|cs| cs.value); + let path_len_constraint = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N4)? + .map(|cs| cs.value); + Ok(Self { + ta_name, + certificate, + policy_set, + policy_flags, + name_constr, + path_len_constraint, + }) + }) + } +} +const CPC_CERTIFICATE_TAG: TagNumber = TagNumber::new(0); +const CPC_POLICY_SET_TAG: TagNumber = TagNumber::new(1); +const CPC_POLICY_FLAGS_TAG: TagNumber = TagNumber::new(2); +const CPC_NAME_CONSTRAINTS_TAG: TagNumber = TagNumber::new(3); +const CPC_PATH_LEN_CONSTRAINT_TAG: TagNumber = TagNumber::new(4); +impl<'a> ::der::Sequence<'a> for CertPathControls<'a> { + fn fields(&self, f: F) -> ::der::Result + where + F: FnOnce(&[&dyn der::Encodable]) -> ::der::Result, + { + #[allow(unused_imports)] + use core::convert::TryFrom; + f(&[ + &self.ta_name, + &self.certificate.as_ref().map(|exts| ContextSpecific { + tag_number: CPC_CERTIFICATE_TAG, + tag_mode: TagMode::Implicit, + value: exts.clone(), + }), + &self.policy_set.as_ref().map(|exts| ContextSpecific { + tag_number: CPC_POLICY_SET_TAG, + tag_mode: TagMode::Implicit, + value: exts.clone(), + }), + &self + .policy_flags + .as_ref() + .map(|policy_flags| ContextSpecific { + tag_number: CPC_POLICY_FLAGS_TAG, + tag_mode: TagMode::Implicit, + value: *policy_flags, + }), + &self.name_constr.as_ref().map(|exts| ContextSpecific { + tag_number: CPC_NAME_CONSTRAINTS_TAG, + tag_mode: TagMode::Implicit, + value: exts.clone(), + }), + &self + .path_len_constraint + .as_ref() + .map(|path_len_constraint| ContextSpecific { + tag_number: CPC_PATH_LEN_CONSTRAINT_TAG, + tag_mode: TagMode::Implicit, + value: *path_len_constraint, + }), + ]) + } +} + +/// CertPolicyFlags ::= BIT STRING { +/// inhibitPolicyMapping (0), +/// requireExplicitPolicy (1), +/// inhibitAnyPolicy (2) } +pub type CertPolicyFlags<'a> = BitString<'a>; + +/// TrustAnchorChoice ::= CHOICE { +/// certificate Certificate, +/// tbsCert \[1\] EXPLICIT TBSCertificate, +/// taInfo \[2\] EXPLICIT TrustAnchorInfo } +#[derive(Clone, Debug, Eq, PartialEq)] +#[allow(clippy::large_enum_variant)] +pub enum TrustAnchorChoice<'a> { + /// certificate Certificate, + Certificate(Certificate<'a>), + // Not supporting TBSCertificate option + // tbsCert \[1\] EXPLICIT TBSCertificate, + //TbsCertificate(TBSCertificate<'a>), + /// taInfo \[2\] EXPLICIT TrustAnchorInfo } + TaInfo(TrustAnchorInfo<'a>), +} + +//const TAC_TBS_CERTIFICATE_TAG: TagNumber = TagNumber::new(1); +const TAC_TA_INFO_TAG: TagNumber = TagNumber::new(2); + +impl<'a> DecodeValue<'a> for TrustAnchorChoice<'a> { + fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result { + let t = decoder.peek_tag()?; + let o = t.octet(); + // Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here + match o { + 0x30 => { + let cert = decoder.decode()?; + Ok(TrustAnchorChoice::Certificate(cert)) + } + // TODO - need DecodeValue on TBSCertificate to support this + // 0xA1 => { + // let on = decoder + // .context_specific::>(TAC_TBS_CERTIFICATE_TAG, TagMode::Explicit)?; + // match on { + // Some(on) => Ok(TrustAnchorChoice::TbsCertificate(on)), + // _ => Err(ErrorKind::Failed.into()), + // } + // } + 0xA2 => { + let on = decoder + .context_specific::>(TAC_TA_INFO_TAG, TagMode::Explicit)?; + match on { + Some(on) => Ok(TrustAnchorChoice::TaInfo(on)), + _ => Err(ErrorKind::Failed.into()), + } + } + _ => Err(ErrorKind::TagUnknown { byte: o }.into()), + } + } +} + +impl<'a> EncodeValue for TrustAnchorChoice<'a> { + fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> { + match self { + Self::Certificate(certificate) => certificate.encode(encoder), + // Self::TbsCertificate(variant) => ContextSpecific { + // tag_number: TAC_TBS_CERTIFICATE_TAG, + // tag_mode: TagMode::Explicit, + // value: variant.clone(), + // }.encode(encoder), + Self::TaInfo(variant) => variant.encode(encoder), + } + } + fn value_len(&self) -> ::der::Result<::der::Length> { + match self { + Self::Certificate(certificate) => certificate.encoded_len(), + // Self::TbsCertificate(variant) => ContextSpecific { + // tag_number: TAC_TBS_CERTIFICATE_TAG, + // tag_mode: TagMode::Explicit, + // value: variant.clone(), + // }.encoded_len(), + Self::TaInfo(variant) => variant.encoded_len(), + } + } +} + +//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag +impl<'a> FixedTag for TrustAnchorChoice<'a> { + const TAG: Tag = ::der::Tag::ContextSpecific { + constructed: true, + number: TAC_TA_INFO_TAG, + }; +} + +// Not supporting these structures +// TrustAnchorList ::= SEQUENCE SIZE (1..MAX) OF TrustAnchorChoice +// +// id-ct-trustAnchorList OBJECT IDENTIFIER ::= { iso(1) +// member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) +// id-smime(16) id-ct(1) 34 } diff --git a/x509/tests/examples/eca.der b/x509/tests/examples/eca.der new file mode 100644 index 0000000000000000000000000000000000000000..fdd35d53bafeeccbc4230fa8c880ffd7e56ce9d2 GIT binary patch literal 859 zcmXqLVh%TGVzOVr%*4pV#K>sC%f_kI=F#?@mywa1mBGN*klTQhjX9KsO_(V(*igbi z48-9Q<`2~i)>ClLFH0@T%T3KIG2}7e0?BX-GrKxF8j2Z+fOyQpyg;5pP=0=i0+2Q_ zkQ3)MG%_$YGB7kVG&V4a66ZCtG&C}Xaj9)x6QdHchZtEIn41{+84Q{jxtN+585wpI zWQOl;h;lq2`E2U*D^0?8bS%8Pnoh}d#P-$M@Z_gHytPB7P-5FX5su2+$GT>|{iS#} zNrFN5gUFdEzw&vXUX+V4iyT||cazY9^#AhaRTm2Sr(G{l;nSV#8fU{G{BD7jq0|J0 z@YE*;;>S+1RNj^Qzu?6C_%#fwR*d_n&0(3^w>D_=#DXth-WWgR=MZn&(Z*$OdGXE! z%j0%CjWwHZa=j3K5NFG)+HfT2ypCRj)&*S$Pw69bwtY6!RC{B{P%L$}eCpb#I^G{e zG|gTZ#@zcF(_a?7n(^BC=V#Sdf7wzT;AEoO9v&qZGShlCf`rv%&X z%=_{EMN{ZS4VBb}r((x9xNu)?3366GaqD;7H^#P`=KKHV?Fcx*`^=`~7k6I&r=G+2 z1GdlC_^~cPX4aW~WqDq=zHHSnT_{^Lu}n(gz10bq2?u1^syCe7aq!uuXnzmUDCNaj zC)uKEdOBPRJ7+!5x@XI|{^IF9Q90W~JrvX(~vs sRXw|N$A0Nc#SRRIeb;oRd@Nqj+{q_ZGGoOQn{6d;ruZ?5zBbGQ05#}5)&Kwi literal 0 HcmV?d00001 diff --git a/x509/tests/examples/eca_policies.ta b/x509/tests/examples/eca_policies.ta new file mode 100755 index 0000000000000000000000000000000000000000..717364a2e446299a83f558757b8e7e4118e3c196 GIT binary patch literal 1954 zcmZ3~#6HiUiG7wq6Qhy=FB_*;n@8JsUPeYnRtDxKMt%mMI2ThBBO}9(g3R!}4N;B< zB%e)vex*tHj*f+QSJNq(j@Z6B8=m~shqrdf6iRHnC&E#A`&ie^x4#tcCP^^peh@hm zXNA zmdd+w{}-HiAHRk{)rxWdv^gwO`_=|+o>=hZ%Nyf|{2byMvW01Ds4$+ry*eLS|aeW_)$@?$pH_inhHeSg5tC_1DLh7E?so znV1daH??sod|itJ`R=K@(e)fv+LA0Vf-CC<~h~Q)sZEgn<}{ z!zIigsu!%M;GSQWT9lWYnpa}TW55NH;TC3gb#^oqGY|psn1y+PJcXeA{1OEqZL*+= zIozO$$$kMd6C)E7BO`LuaI-Q{*){_?ab80s17jltLo-8T1CuCmUL#9GBV!ns+SZMx zH6qg5VkZL!16g3=kmX|$!;$d#K+^n-jQ?3!fC;6|fFHyc2Ju-9n1K{pvH|93Muu(j zi4|@8wpJC2cYVC>$|U-Im%`DflQUK0L<=qb4qm+RqS|?PujIu83iqG;uI`GhZ&2v% zeb1E9b(LX`;&#?G&$st)oDyuiGw;Xu7fqoTHB?d?o{Amc;KF^mCCFL%#I4_P-x%9& zn(zOcwFIDO?40#H>z*y=`irOcOkK_q-WRygQ1Ei$ zhvxSI$KO@vSSu(0n3a~tr>P*lR`u-89s8v(6+18-_FdDN^09b9b0?ov$&3|KY_^rW znc~MJ`r0sWVG~oX0XMLuQ(mXbgT@yE1|qjM8zT#2dI}>@ z9?pGJ11u()P`PXdJZxNG#XX8#z-)`mLn!@kz|6+P1J?=WiXgat&9zKyaBkx^O-4p8 zR4xbH;7KtcAHb{u@z_D`2Ros0cQDX}LTG$pG(On>Nb1?)rg`#%LIY+th{p=zCA0`I zwr4Xkv4Xt^=P`i-HKEOeF_nptTNtbm!~v#D9yK7!aQE73Bp^||DNQ7Hh(mA!LBr`2DIk6-&KTpB2v?L?HD6=Fpwb)SC zKpUb)1*%KIH!&|UJvBukIJKxOGdZdJy=OjA%@V(7%3a>Zt*&fm|Fe5lj&rtyd(-4cC$_&$D=N*F$avO`D-7bZ8ZZMX16hy&ABz}^h*U|aTvsc{{D4@yE;q)m#yLOd z$sva_FaZKXnUTRD@1g7C!{YO;e@1yc|Cse&-pOXOr<;uL^GJg;3KFN7eYE7950w3? zee|?w?zI@JoSzI#=5e#v?4Mi^^qoUMROs^uliHb`7GAgi@V&nAdwtXzlqi z_x;P08!Q|>`q_Pi9-WiyJG!gF%&DZffoY!et;1IeYTlIed<&M5=al@n=-{^p&vWj) zIct>L7f~v4Xli>g(+qCYzRcp3J{-A(7><_3Q{($mj(!oX&a auHWW2E7*4%9Xz_@$gZn@N|e4DyaxaQ7h)m+ literal 0 HcmV?d00001 diff --git a/x509/tests/examples/entrust_dnConstraint.ta b/x509/tests/examples/entrust_dnConstraint.ta new file mode 100755 index 0000000000000000000000000000000000000000..ff784dd755c929690605b351a3c3c51e403040ab GIT binary patch literal 1443 zcmZ3~#5&)giFLL?6Qhy=FB_*;n@8JsUPeYnRtDxKMt%mMI2ThBBO}8m_eU4>_!fLw z)m|7P>r~zv`d<0a_7@WAvX2<&F4)C)@0m|jv&8S2a+kMpt1H{t|Lk6s5btekk%Z+8g;&zczx72>BA9P`dT?wT`MGwG+FIH&t^?ZjJan8kt~ zq-oS99y%^}r?4R;J$u%?DdrFU9%sCB;XlXB?#U|+v?M;(H=1sG-KFjq!?RX!U2u~k zhitB$eBFE#j~?5wV`BgGZwG=smag+#*JRCl{&(zx*;&HB9Fs&+_!Ou3CoQ*lZ)k8? zlZly;fssW-sw7mdtCeGZK&)Mt8)H}FoS*aL44PP?4T=o84LI4DLs{5_nL>jN1q}E= z94=vY*SwOV(&7?BB?ASJ2)D4Lb81mZW?E))Vo7Fxo`Pd(Nk)EAW=Uphv7xSkHbjjI zRF{HpVqRi;YKlT|YEfBca%!=HpPQ#bP=0=ig0tg-CgvpuO-wxtn3))vm{`1)AIe3J zLRMCWfpC?9oH(z6B?uat7@3`$btm;Sj2GT1*9?rm;ixg2qS|*-b2^NhsEbx|BUi@{xR#l zypzplPd6Fg=aB|y6eLbD`)J8IA1M1(`{-%W+-osbIX@Yg%;RRS*+01;=sSmisLD}zCu zA-4f18*?ZNn=n&ou%U>75QxJi%2ym?Ni9k&Nlj7kOi9fv$t|3%S=vH z2+GedQE+xtFf@=8=QT7iFf}wVG&eCeF^m%DHHLCc4UCMV2Hbs3j7rEs#>mRR+{DPw zV9>fak*7=2v*f9KT-z5{t>Db>btA2mA8bfoc17^%*UvdH<${UGm8 znC5|%t?SqIX)3?krBUn_sk0_CHqumec_D43}oN?jt{|0~WpOqi}3P;|ZR<%-BOz6CLm#NZA z)vz;@a&!#DpGQRAy}#_IXv^iBw=Zk39X-c&R$P0=qE+)Z=Dbo}A!EKn=GihmL7%K- z>#~Tm_gQ9nb?)~M+5D4#+6T5g<;oRDS>=3g@g*4km65;mcKf4USqXMs9CI1Yp4zk0 z`p1i9N5tGV&6f{({x$G*?}j!-CT2zk#>Gws4hDR{=$GYZWc<&<0!(_x4ERBOVGy6y zfEh>`$btm;Sj1RF^!80$R-R>U^YyRnw!0N4mp%QWq<|dCz=R15Wkv=y%hY{8W4>>+ zQrP3rP*WwctG1wCL;6|lN7*M;Mk+~rJvFS42d$Rb6L8J5{QUI83lC29(|0|+@`VU>dE&fmMqTDQ0bi^y63#gfutV&%X`)y+%e&6nn8e*dDY$S z_zxE~|DAlFoTzd_Nv-etQ%1v`YJy$j(xuD)-Hj3Pb8X18Ft~ka<4n)hf~MPbR$6DW zZul3{)aJs!ck9O1<8^23YXa*vUj!=zGa4V3iaopJAe+&iWj{j?8J5HeOuixfZd=LL zJ38&!e|#qt^55*7oL~Rc`&YbE8Sg#*dqH@8t!F($lu5=~$?tC&{1#OA F0ss%@c4+_r literal 0 HcmV?d00001 diff --git a/x509/tests/examples/exostar_policyFlags.ta b/x509/tests/examples/exostar_policyFlags.ta new file mode 100755 index 0000000000000000000000000000000000000000..46e5f9248c57c6c4cb5f45a3e7f8abdf9b8d48ef GIT binary patch literal 1495 zcmZ3~#CqAFiS@ie6Qhy=FB_*;n@8JsUPeYnRtDxKMt%mMI2ThBBO}AUS*w3f3IMb2FFJYuQH_9UCEBAxEKVg~&R<^ER*QcrcYL{A(THtMd z=N*mH61H=u$NwAry?<7I_$wTFcUsj-T`{5a;$5anFIB_NOv=$Q5Pu#KdH4RZpQ0_7 zZ{EJF!FKc<*I9Ax8H-lU-U^YyRnw!0N4mp%QWq+rm*(r-{_$Zf#M#vIDRCd?EXY$#$N z1mbWBbGugL7ndX!Dfsv}8!8znfJC{4C7n}?N;1Q&RIvGD|8If>VpiGLuskg7WiA6r3Fu3>P#ppD<`* z+OU9`iIIs(py1AKR zUSlZN)WFC%YQWt$nj4774U3%&91Qq?iB*=Lk?}tZ3oz*&GvEjDg+Y8)17;v)APW-U zV-drVcaX{)V8R5JIgAWymZ|%G#(dvsrLf1Lp{7b?S8YMPhV--8kFrmyj8u~LdTLl7 z4_Ym=C*YcA`T6OG7aq)(dG^KNhsCbCYH!}IX)dai5rAwFp zyBj0o=h~2GVQ~A<#+jb01x>f>thCN#-S97@sm+Cd@79g2$Lr46*96vUz6e$bW;8x5 z6?=BcK{lg3%YKF)GAxM`n0!O{-L{ggcXZmd|M*TQvR50W*-~!2T3p2aqy8r+MD?c*; literal 0 HcmV?d00001 diff --git a/x509/tests/examples/raytheon.der b/x509/tests/examples/raytheon.der new file mode 100644 index 0000000000000000000000000000000000000000..9b577432dcfb8cdc13c71832635847dafcba5927 GIT binary patch literal 907 zcmXqLVs1BRV#;5@%*4pV#Nrk^OUr@5qwPB{BO@y-gF%R)uz?^O7w06k&dHy2 zQWz!0n3MB!4J8c3Py{)O5-UqGQuFf+c?`JNm_u2(gqfWkiw#8$gh4!RVV)qUf}s5T z5(7DLUPBWDLqk&|LsL^@%P4VPBM8^X2*jn4txb$d$lhaQWngY%|M>rtfyG9Y<{cY{r>~C6YL=9PRN}yD#UsbL)jwb7!#M z6_OD-GwEGgXSCLSH?wZx$r7qBX5J7ySp7xR|8nXvj)v+vd9rehOYCBf->SWM`N6NE z{U^5zvkAo}{bXWhWMEvJXb^9}4~#TfVMfOPEUX61K+1p*B)|_6U;(BTHUn7@pN~b1 zMMT4(?P{2)lYmpqhiVzYP0gKGDiwgz;6(JTmfewoMZkbhnj_3Y&(O+5&w!bUWdd>} z0Mi&S5*QiO%_=7I2QTJ6GLhS--|?p+m(7=JESAL`Gn>4-WU4sNE?RDQUqbB4<&XPo z_MSE6<*vFG5ZbnY|NpFAGlMlgtzFi>dta^I)wZ3<$?Vdack5qtAK#YQH(6!HEAfhl zDQWW)uNJ>to&3zk?T>54ip*ff59NOsv0ZlV%4c1J{or&bdh3ntb-Sb$0sy4$O$7h| literal 0 HcmV?d00001 diff --git a/x509/tests/examples/raytheon_pathLenConstraint.ta b/x509/tests/examples/raytheon_pathLenConstraint.ta new file mode 100644 index 0000000000000000000000000000000000000000..eb0fd7f3511a7bba891936b3e25e9ebc943d6592 GIT binary patch literal 1324 zcmZ3~#HwM?#HwP@#HeJz%f_kI=F#?@mywZ?m4Ug5k)Hu5&c)Qk$jC5lnr|cPnoQ^S z*UXicPv;U4t=YnK`p5s{Dc2098ecp$Vfxw=%O?vn&MdJg7hIEf|BlD9wmWCH&N4n$ z%9ydc#^7e#sp}@&)~J2k*?4(dvgv1KkApdZa}S#5pZ^!IZl$vLbjKqxx7Tm0t}9z1 zC@om7tyin>_RZ``i?_4bA_19A-|bpEj@m@JXdg{G%uhDdt19jNqo`&MTD)22IRQ3_=Wr4FuV^I47xfPX45m!YC=m zoSdI)C}ALmBFIscSXq*hnxALLW5C769LmBa%ECME z9T`{z4EUru!YuR*txWU`n3-56prtWjLBPnMZdNgwKX@_sk%`f~oOZhu@WR%8Y_{s s8YYJ6n#`JaM61ip@GS2!_P5z@(~emM*d{i8aDHvOK2252wuO-i0J { + assert_eq!( + tai.pub_key.algorithm.oid.to_string(), + "1.2.840.113549.1.1.1" + ); + + assert_eq!( + &hex!("335BA56F7A55602B814B2614CC79BF4ABA8B32BD"), + tai.key_id.as_bytes() + ); + + let policy_ids: [&str; 42] = [ + "1.2.36.1.334.1.2.1.2", + "1.2.840.113549.5.6.1.3.1.12", + "1.2.840.113549.5.6.1.3.1.18", + "1.3.6.1.4.1.103.100.1.1.3.1", + "1.3.6.1.4.1.13948.1.1.1.2", + "1.3.6.1.4.1.13948.1.1.1.6", + "1.3.6.1.4.1.1569.10.1.1", + "1.3.6.1.4.1.1569.10.1.2", + "1.3.6.1.4.1.16304.3.6.2.12", + "1.3.6.1.4.1.16304.3.6.2.20", + "1.3.6.1.4.1.16334.509.2.6", + "1.3.6.1.4.1.23337.1.1.10", + "1.3.6.1.4.1.23337.1.1.8", + "1.3.6.1.4.1.2396.2.1.2", + "1.3.6.1.4.1.2396.2.1.7", + "1.3.6.1.4.1.24019.1.1.1.18", + "1.3.6.1.4.1.24019.1.1.1.19", + "1.3.6.1.4.1.24019.1.1.1.2", + "1.3.6.1.4.1.24019.1.1.1.7", + "1.3.6.1.4.1.73.15.3.1.12", + "1.3.6.1.4.1.73.15.3.1.5", + "2.16.528.1.1003.1.2.5.1", + "2.16.528.1.1003.1.2.5.2", + "2.16.840.1.101.2.1.11.19", + "2.16.840.1.101.3.2.1.12.2", + "2.16.840.1.101.3.2.1.12.3", + "2.16.840.1.101.3.2.1.3.12", + "2.16.840.1.101.3.2.1.3.13", + "2.16.840.1.101.3.2.1.3.16", + "2.16.840.1.101.3.2.1.3.18", + "2.16.840.1.101.3.2.1.3.24", + "2.16.840.1.101.3.2.1.3.4", + "2.16.840.1.101.3.2.1.3.7", + "2.16.840.1.101.3.2.1.5.4", + "2.16.840.1.101.3.2.1.5.5", + "2.16.840.1.101.3.2.1.6.12", + "2.16.840.1.101.3.2.1.6.4", + "2.16.840.1.113733.1.7.23.3.1.18", + "2.16.840.1.113733.1.7.23.3.1.7", + "2.16.840.1.114027.200.3.10.7.2", + "2.16.840.1.114027.200.3.10.7.4", + "2.16.840.1.114027.200.3.10.7.6", + ]; + + let cert_path = tai.cert_path.as_ref().unwrap(); + let mut counter = 0; + let exts = cert_path.policy_set.as_ref().unwrap(); + let i = exts.iter(); + for ext in i { + assert_eq!(policy_ids[counter], ext.policy_identifier.to_string()); + counter += 1; + } + + counter = 0; + let i = cert_path.ta_name.iter(); + for rdn in i { + let i1 = rdn.iter(); + for atav in i1 { + if 0 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.6"); + assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + } else if 1 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.10"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "U.S. Government" + ); + } else if 2 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.11"); + assert_eq!(atav.value.printable_string().unwrap().to_string(), "ECA"); + } else if 3 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.3"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "ECA Root CA 4" + ); + } + counter += 1; + } + } + + let reencoded_cert = cert_path.certificate.to_vec().unwrap(); + assert_eq!(der_encoded_cert, reencoded_cert.as_slice()); + } + _ => panic!("Unexpected TrustAnchorChoice contents"), + } +} + +#[test] +fn decode_ta2() { + // features an Entrust cert wrapped in a TrustAnchorInfo that contains an excluded subtree in the + // name constraint in the cert path controls field + let der_encoded_tac = include_bytes!("examples/entrust_dnConstraint.ta"); + let der_encoded_cert = include_bytes!("examples/entrust.der"); + + let mut decoder = Decoder::new(der_encoded_tac).unwrap(); + let header = decoder.peek_header().unwrap(); + let tac = TrustAnchorChoice::decode_value(&mut decoder, header.length).unwrap(); + let reencoded_tac = tac.to_vec().unwrap(); + println!("Original : {:02X?}", der_encoded_cert); + println!("Reencoded: {:02X?}", reencoded_tac); + assert_eq!(der_encoded_tac, reencoded_tac.as_slice()); + + match tac { + TrustAnchorChoice::TaInfo(tai) => { + assert_eq!( + tai.pub_key.algorithm.oid.to_string(), + "1.2.840.113549.1.1.1" + ); + + assert_eq!( + &hex!("1A74551E8A85089F505D3E8A46018A819CF99E1E"), + tai.key_id.as_bytes() + ); + + let cert_path = tai.cert_path.as_ref().unwrap(); + + let mut counter = 0; + let i = cert_path.ta_name.iter(); + for rdn in i { + let i1 = rdn.iter(); + for atav in i1 { + if 0 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.6"); + assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + } else if 1 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.10"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "Entrust" + ); + } else if 2 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.11"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "Certification Authorities" + ); + } else if 3 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.11"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "Entrust Managed Services NFI Root CA" + ); + } + counter += 1; + } + } + + let nc = cert_path.name_constr.as_ref().unwrap(); + counter = 0; + let gsi = nc.excluded_subtrees.as_ref().unwrap().iter(); + for gs in gsi { + match &gs.base { + GeneralName::DirectoryName(dn) => { + let i = dn.iter(); + for rdn in i { + let i1 = rdn.iter(); + for atav in i1 { + if 0 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.6"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "US" + ); + } else if 1 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.10"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "U.S. Government" + ); + } else if 2 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.11"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "DoD" + ); + } + counter += 1; + } + } + } + _ => panic!("Unexpected GeneralSubtree type"), + } + } + + let reencoded_cert = cert_path.certificate.to_vec().unwrap(); + assert_eq!(der_encoded_cert, reencoded_cert.as_slice()); + } + _ => panic!("Unexpected TrustAnchorChoice contents"), + } +} + +#[test] +fn decode_ta3() { + // features an Exostar cert wrapped in a TrustAnchorInfo that contains an excluded subtree in the + // name constraint and policy flags in the cert path controls field + let der_encoded_tac = include_bytes!("examples/exostar_policyFlags.ta"); + let der_encoded_cert = include_bytes!("examples/exostar.der"); + + let mut decoder = Decoder::new(der_encoded_tac).unwrap(); + let header = decoder.peek_header().unwrap(); + let tac = TrustAnchorChoice::decode_value(&mut decoder, header.length).unwrap(); + let reencoded_tac = tac.to_vec().unwrap(); + println!("Original : {:02X?}", der_encoded_cert); + println!("Reencoded: {:02X?}", reencoded_tac); + assert_eq!(der_encoded_tac, reencoded_tac.as_slice()); + + match tac { + TrustAnchorChoice::TaInfo(tai) => { + assert_eq!( + tai.pub_key.algorithm.oid.to_string(), + "1.2.840.113549.1.1.1" + ); + + assert_eq!( + &hex!("2EBE91A6776A373CF5FD1DB6DD78C9A6E5F42220"), + tai.key_id.as_bytes() + ); + + let cert_path = tai.cert_path.as_ref().unwrap(); + + let cpf = cert_path.policy_flags.unwrap(); + let b = cpf.raw_bytes(); + if 0x80 != 0x80 & b[0] { + panic!("Missing policy flag bit 0") + } + if 0x40 != 0x40 & b[0] { + panic!("Missing policy flag bit 1") + } + if 0x20 != 0x20 & b[0] { + panic!("Missing policy flag bit 2") + } + if cpf.unused_bits() != 5 { + panic!("Wrong unused bits for policy flags") + } + + let mut counter = 0; + let i = cert_path.ta_name.iter(); + for rdn in i { + let i1 = rdn.iter(); + for atav in i1 { + if 0 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.6"); + assert_eq!(atav.value.printable_string().unwrap().to_string(), "US"); + } else if 1 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.10"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "Exostar LLC" + ); + } else if 2 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.11"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "Certification Authorities" + ); + } else if 3 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.3"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "Exostar Federated Identity Service Root CA 1" + ); + } + counter += 1; + } + } + + let nc = cert_path.name_constr.as_ref().unwrap(); + counter = 0; + let gsi = nc.excluded_subtrees.as_ref().unwrap().iter(); + for gs in gsi { + match &gs.base { + GeneralName::DirectoryName(dn) => { + let i = dn.iter(); + for rdn in i { + let i1 = rdn.iter(); + for atav in i1 { + if 0 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.6"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "US" + ); + } else if 1 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.10"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "U.S. Government" + ); + } else if 2 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.11"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "DoD" + ); + } + counter += 1; + } + } + } + _ => panic!("Unexpected GeneralSubtree type"), + } + } + + let reencoded_cert = cert_path.certificate.to_vec().unwrap(); + assert_eq!(der_encoded_cert, reencoded_cert.as_slice()); + } + _ => panic!("Unexpected TrustAnchorChoice contents"), + } +} + +#[test] +fn decode_ta4() { + // features an Exostar cert wrapped in a TrustAnchorInfo that contains path length constraint in + // the cert path controls field + let der_encoded_tac = include_bytes!("examples/raytheon_pathLenConstraint.ta"); + let der_encoded_cert = include_bytes!("examples/raytheon.der"); + + let mut decoder = Decoder::new(der_encoded_tac).unwrap(); + let header = decoder.peek_header().unwrap(); + let tac = TrustAnchorChoice::decode_value(&mut decoder, header.length).unwrap(); + let reencoded_tac = tac.to_vec().unwrap(); + println!("Original : {:02X?}", der_encoded_cert); + println!("Reencoded: {:02X?}", reencoded_tac); + assert_eq!(der_encoded_tac, reencoded_tac.as_slice()); + + match tac { + TrustAnchorChoice::TaInfo(tai) => { + assert_eq!( + tai.pub_key.algorithm.oid.to_string(), + "1.2.840.113549.1.1.1" + ); + + assert_eq!( + &hex!("283086D556154210425CF07B1C11B28389D47920"), + tai.key_id.as_bytes() + ); + + let cert_path = tai.cert_path.as_ref().unwrap(); + + let mut counter = 0; + let i = cert_path.ta_name.iter(); + for rdn in i { + let i1 = rdn.iter(); + for atav in i1 { + if 0 == counter { + assert_eq!(atav.oid.to_string(), "0.9.2342.19200300.100.1.25"); + assert_eq!(atav.value.ia5_string().unwrap().to_string(), "com"); + } else if 1 == counter { + assert_eq!(atav.oid.to_string(), "0.9.2342.19200300.100.1.25"); + assert_eq!(atav.value.ia5_string().unwrap().to_string(), "raytheon"); + } else if 2 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.10"); + assert_eq!(atav.value.printable_string().unwrap().to_string(), "CAs"); + } else if 3 == counter { + assert_eq!(atav.oid.to_string(), "2.5.4.11"); + assert_eq!( + atav.value.printable_string().unwrap().to_string(), + "RaytheonRoot" + ); + } + counter += 1; + } + } + + let pl = cert_path.path_len_constraint.unwrap(); + if 2 != pl { + panic!("Wrong path length constraint"); + } + + let reencoded_cert = cert_path.certificate.to_vec().unwrap(); + assert_eq!(der_encoded_cert, reencoded_cert.as_slice()); + } + _ => panic!("Unexpected TrustAnchorChoice contents"), + } +} From 9cef56fcd744c01244b01a82ed80debe4b64f190 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Thu, 27 Jan 2022 19:12:10 -0500 Subject: [PATCH 2/4] add just enough CMS support to allow incoming path builder implementation to parse certs-only SignedData messages. The inclusion of SetOf caused several structs to need Ord and/or PartialOrd added. --- Cargo.lock | 1 + der/src/asn1/integer/bigint.rs | 2 +- pkcs7/Cargo.toml | 1 + pkcs7/src/cryptographic_message_syntax2004.rs | 568 ++++++++++++++++++ pkcs7/src/lib.rs | 3 + .../tests/cryptographic_message_syntax2004.rs | 23 + .../DODJITCINTEROPERABILITYROOTCA2_IT.p7c | Bin 0 -> 1935 bytes pkcs7/tests/examples/DODROOTCA3_IB.p7c | Bin 0 -> 31371 bytes .../tests/examples/caCertsIssuedTofbcag4.p7c | Bin 0 -> 11536 bytes spki/src/algorithm.rs | 2 +- 10 files changed, 598 insertions(+), 2 deletions(-) create mode 100644 pkcs7/src/cryptographic_message_syntax2004.rs create mode 100644 pkcs7/tests/cryptographic_message_syntax2004.rs create mode 100644 pkcs7/tests/examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c create mode 100644 pkcs7/tests/examples/DODROOTCA3_IB.p7c create mode 100644 pkcs7/tests/examples/caCertsIssuedTofbcag4.p7c diff --git a/Cargo.lock b/Cargo.lock index 58987971d..19d3cf893 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -554,6 +554,7 @@ dependencies = [ "der", "hex-literal", "spki", + "x509", ] [[package]] diff --git a/der/src/asn1/integer/bigint.rs b/der/src/asn1/integer/bigint.rs index 6ab288c2d..2fa60bc4a 100644 --- a/der/src/asn1/integer/bigint.rs +++ b/der/src/asn1/integer/bigint.rs @@ -13,7 +13,7 @@ use crate::{ /// /// Intended for use cases like very large integers that are used in /// cryptographic applications (e.g. keys, signatures). -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct UIntBytes<'a> { /// Inner value inner: ByteSlice<'a>, diff --git a/pkcs7/Cargo.toml b/pkcs7/Cargo.toml index e04988460..517680ef9 100644 --- a/pkcs7/Cargo.toml +++ b/pkcs7/Cargo.toml @@ -17,6 +17,7 @@ rust-version = "1.57" [dependencies] der = { version = "=0.6.0-pre.0", features = ["oid"], path = "../der" } spki = { version = "=0.6.0-pre", path = "../spki" } +x509 = { version = "0.0.1", path = "../x509" } [dev-dependencies] hex-literal = "0.3" diff --git a/pkcs7/src/cryptographic_message_syntax2004.rs b/pkcs7/src/cryptographic_message_syntax2004.rs new file mode 100644 index 000000000..2d5b0118d --- /dev/null +++ b/pkcs7/src/cryptographic_message_syntax2004.rs @@ -0,0 +1,568 @@ +//! Selected structures from RFC5652 + +use der::asn1::{BitString, ContextSpecific, SetOf, UIntBytes}; +use der::{ + Any, DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Tag, TagMode, + TagNumber, +}; +use der::{OrdIsValueOrd, Sequence}; +use spki::{AlgorithmIdentifier, ObjectIdentifier}; +use x509::{AttributeTypeAndValue, Name, SubjectKeyIdentifier}; + +/// ContentInfo ::= SEQUENCE { +/// contentType ContentType, +/// content \[0\] EXPLICIT ANY DEFINED BY contentType } +#[derive(Clone, Eq, PartialEq)] +pub struct ContentInfo2004<'a> { + /// contentType ContentType, + pub content_type: ObjectIdentifier, + /// content \[0\] EXPLICIT ANY DEFINED BY contentType } + pub content: Option>, +} + +const CONTENT_TAG: TagNumber = TagNumber::new(0); + +impl<'a> ::der::Decodable<'a> for ContentInfo2004<'a> { + fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { + decoder.sequence(|decoder| { + let content_type = decoder.decode()?; + //let content = decoder.decode()?; + let content = + ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N0)? + .map(|cs| cs.value); + Ok(Self { + content_type, + content, + }) + }) + } +} +impl<'a> ::der::Sequence<'a> for ContentInfo2004<'a> { + fn fields(&self, f: F) -> ::der::Result + where + F: FnOnce(&[&dyn Encodable]) -> ::der::Result, + { + f(&[ + &self.content_type, + &self.content.as_ref().map(|content| ContextSpecific { + tag_number: CONTENT_TAG, + tag_mode: TagMode::Explicit, + value: *content, + }), + ]) + } +} + +/// ContentType ::= OBJECT IDENTIFIER +pub type ContentType = ObjectIdentifier; + +/// SignedData ::= SEQUENCE { +/// version CMSVersion, +/// digestAlgorithms DigestAlgorithmIdentifiers, +/// encapContentInfo EncapsulatedContentInfo, +/// certificates \[0\] IMPLICIT CertificateSet OPTIONAL, +/// crls \[1\] IMPLICIT RevocationInfoChoices OPTIONAL, +/// signerInfos SignerInfos } +#[derive(Clone, Eq, PartialEq)] +pub struct SignedData<'a> { + /// version CMSVersion, + pub version: u8, + /// digestAlgorithms DigestAlgorithmIdentifiers, + pub digest_algorithms: DigestAlgorithmIdentifiers<'a>, + /// encapContentInfo EncapsulatedContentInfo, + pub encap_content_info: EncapsulatedContentInfo<'a>, + // Using Any as a means of deferring most of the decoding of the certificates (will still need + // to call to_vec on the resulting Any to restore tag and length values). + /// certificates \[0\] IMPLICIT CertificateSet OPTIONAL, + pub certificates: Option>>, + // TODO support CRLs - placeholder to placate zero length SETs + //pub crls: SetOf, 10>, + /// crls \[1\] IMPLICIT RevocationInfoChoices OPTIONAL, + pub crls: Option>>, + /// signerInfos SignerInfos } + pub signer_infos: SetOf, 10>, +} +const CERTIFICATES_TAG: TagNumber = TagNumber::new(0); +const CRLS_TAG: TagNumber = TagNumber::new(1); + +impl<'a> ::der::Decodable<'a> for SignedData<'a> { + fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { + decoder.sequence(|decoder| { + let version = decoder.decode()?; + let digest_algorithms = decoder.decode()?; + let encap_content_info = decoder.decode()?; + //let certificates = decoder.decode()?; + let certificates = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N0)? + .map(|cs| cs.value); + let crls = + ::der::asn1::ContextSpecific::decode_implicit(decoder, ::der::TagNumber::N1)? + .map(|cs| cs.value); + let signer_infos = decoder.decode()?; + Ok(Self { + version, + digest_algorithms, + encap_content_info, + certificates, + crls, + signer_infos, + }) + }) + } +} + +impl<'a> ::der::Sequence<'a> for SignedData<'a> { + fn fields(&self, f: F) -> ::der::Result + where + F: FnOnce(&[&dyn Encodable]) -> ::der::Result, + { + f(&[ + &self.version, + &self.digest_algorithms, + &self.encap_content_info, + &self + .certificates + .as_ref() + .map(|certificates| ContextSpecific { + tag_number: CERTIFICATES_TAG, + tag_mode: TagMode::Implicit, + value: certificates.clone(), + }), + &self.crls.as_ref().map(|certificates| ContextSpecific { + tag_number: CRLS_TAG, + tag_mode: TagMode::Implicit, + value: certificates.clone(), + }), + &self.signer_infos, + ]) + } +} + +/// DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier +pub type DigestAlgorithmIdentifiers<'a> = SetOf; +// TODO - make dynamic + +/* + SignerInfos ::= SET OF SignerInfo +*/ + +/// EncapsulatedContentInfo ::= SEQUENCE { +/// eContentType ContentType, +/// eContent \[0\] EXPLICIT OCTET STRING OPTIONAL } +#[derive(Clone, Eq, PartialEq)] +pub struct EncapsulatedContentInfo<'a> { + /// eContentType ContentType, + pub econtent_type: ObjectIdentifier, + /// eContent \[0\] EXPLICIT OCTET STRING OPTIONAL } + pub econtent: Option>, +} +const ECONTENT_TAG: TagNumber = TagNumber::new(0); + +impl<'a> ::der::Decodable<'a> for EncapsulatedContentInfo<'a> { + fn decode(decoder: &mut ::der::Decoder<'a>) -> ::der::Result { + decoder.sequence(|decoder| { + let econtent_type = decoder.decode()?; + // let econtent = decoder.decode()?; + let econtent = + ::der::asn1::ContextSpecific::decode_explicit(decoder, ::der::TagNumber::N0)? + .map(|cs| cs.value); + Ok(Self { + econtent_type, + econtent, + }) + }) + } +} +impl<'a> ::der::Sequence<'a> for EncapsulatedContentInfo<'a> { + fn fields(&self, f: F) -> ::der::Result + where + F: FnOnce(&[&dyn Encodable]) -> ::der::Result, + { + f(&[ + &self.econtent_type, + &self.econtent.as_ref().map(|econtent| ContextSpecific { + tag_number: ECONTENT_TAG, + tag_mode: TagMode::Explicit, + value: *econtent, + }), + ]) + } +} + +/// SignerInfo ::= SEQUENCE { +/// version CMSVersion, +/// sid SignerIdentifier, +/// digestAlgorithm DigestAlgorithmIdentifier, +/// signedAttrs \[0\] IMPLICIT SignedAttributes OPTIONAL, +/// signatureAlgorithm SignatureAlgorithmIdentifier, +/// signature SignatureValue, +/// unsignedAttrs \[1\] IMPLICIT UnsignedAttributes OPTIONAL } +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Sequence)] +pub struct SignerInfo<'a> { + /// version CMSVersion, + pub version: u8, + /// sid SignerIdentifier, + pub sid: SignerIdentifier<'a>, + /// digestAlgorithm DigestAlgorithmIdentifier, + pub digest_algorithm: AlgorithmIdentifier<'a>, + /// signedAttrs \[0\] IMPLICIT SignedAttributes OPTIONAL, + pub signed_attrs: SignedAttributes<'a>, + /// signatureAlgorithm SignatureAlgorithmIdentifier, + pub signature_algorithm: AlgorithmIdentifier<'a>, + /// signature SignatureValue, + pub signature: BitString<'a>, + /// unsignedAttrs \[1\] IMPLICIT UnsignedAttributes OPTIONAL } + pub unsigned_attrs: UnsignedAttributes<'a>, +} +impl OrdIsValueOrd for SignerInfo<'_> {} + +/// SignerIdentifier ::= CHOICE { +/// issuerAndSerialNumber IssuerAndSerialNumber, +/// subjectKeyIdentifier \[0\] SubjectKeyIdentifier } +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] +pub enum SignerIdentifier<'a> { + /// issuerAndSerialNumber IssuerAndSerialNumber, + IssuerAndSerialNumber(IssuerAndSerialNumber<'a>), + /// subjectKeyIdentifier \[0\] SubjectKeyIdentifier } + SubjectKeyIdentifier(SubjectKeyIdentifier<'a>), +} + +const SKID_TAG: TagNumber = TagNumber::new(0); +const IASN_TAG: TagNumber = TagNumber::new(30); + +impl<'a> DecodeValue<'a> for SignerIdentifier<'a> { + fn decode_value(decoder: &mut Decoder<'a>, _length: Length) -> der::Result { + let t = decoder.peek_tag()?; + let o = t.octet(); + // Context specific support always returns an Option<>, just ignore since OPTIONAL does not apply here + match o { + //TODO FIX + // 0x30 => { + // let on = decoder + // .context_specific::>(IASN_TAG, TagMode::Implicit)?; + // match on { + // Some(on) => Ok(SignerIdentifier::IssuerAndSerialNumber(on)), + // _ => Err(ErrorKind::Failed.into()), + // } + // } + 0xA0 => { + let on = decoder + .context_specific::>(SKID_TAG, TagMode::Implicit)?; + match on { + Some(on) => Ok(SignerIdentifier::SubjectKeyIdentifier(on)), + _ => Err(ErrorKind::Failed.into()), + } + } + _ => Err(ErrorKind::TagUnknown { byte: o }.into()), + } + } +} + +impl<'a> EncodeValue for SignerIdentifier<'a> { + fn encode_value(&self, encoder: &mut ::der::Encoder<'_>) -> ::der::Result<()> { + match self { + Self::IssuerAndSerialNumber(variant) => ContextSpecific { + tag_number: IASN_TAG, + tag_mode: TagMode::Implicit, + value: variant.clone(), + } + .encode(encoder), + Self::SubjectKeyIdentifier(variant) => ContextSpecific { + tag_number: SKID_TAG, + tag_mode: TagMode::Implicit, + value: *variant, + } + .encode(encoder), + } + } + fn value_len(&self) -> ::der::Result<::der::Length> { + match self { + Self::IssuerAndSerialNumber(variant) => ContextSpecific { + tag_number: IASN_TAG, + tag_mode: TagMode::Implicit, + value: variant.clone(), + } + .encoded_len(), + Self::SubjectKeyIdentifier(variant) => ContextSpecific { + tag_number: SKID_TAG, + tag_mode: TagMode::Implicit, + value: *variant, + } + .encoded_len(), + } + } +} + +//TODO - see why this is necessary to avoid problem at line 78 in context_specific.rs due to mismatched tag +impl<'a> FixedTag for SignerIdentifier<'a> { + const TAG: Tag = ::der::Tag::Sequence; +} + +/// SignedAttributes ::= SET SIZE (1..MAX) OF Attribute +pub type SignedAttributes<'a> = SetOf, 10>; + +/// UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute +pub type UnsignedAttributes<'a> = SetOf, 10>; + +/* + Attribute ::= SEQUENCE { + attrType OBJECT IDENTIFIER, + attrValues SET OF AttributeValue } + + AttributeValue ::= ANY + + SignatureValue ::= OCTET STRING + + EnvelopedData ::= SEQUENCE { + version CMSVersion, + originatorInfo \[0\] IMPLICIT OriginatorInfo OPTIONAL, + recipientInfos RecipientInfos, + encryptedContentInfo EncryptedContentInfo, + unprotectedAttrs \[1\] IMPLICIT UnprotectedAttributes OPTIONAL } + + OriginatorInfo ::= SEQUENCE { + certs \[0\] IMPLICIT CertificateSet OPTIONAL, + crls \[1\] IMPLICIT RevocationInfoChoices OPTIONAL } + + RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo + + EncryptedContentInfo ::= SEQUENCE { + contentType ContentType, + contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + encryptedContent \[0\] IMPLICIT EncryptedContent OPTIONAL } + + EncryptedContent ::= OCTET STRING + + UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute + + RecipientInfo ::= CHOICE { + ktri KeyTransRecipientInfo, + kari \[1\] KeyAgreeRecipientInfo, + kekri \[2\] KEKRecipientInfo, + pwri \[3\] PasswordRecipientInfo, + ori \[4\] OtherRecipientInfo } + + EncryptedKey ::= OCTET STRING + + KeyTransRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 0 or 2 + rid RecipientIdentifier, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey EncryptedKey } + + RecipientIdentifier ::= CHOICE { + issuerAndSerialNumber IssuerAndSerialNumber, + subjectKeyIdentifier \[0\] SubjectKeyIdentifier } + + KeyAgreeRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 3 + originator \[0\] EXPLICIT OriginatorIdentifierOrKey, + ukm \[1\] EXPLICIT UserKeyingMaterial OPTIONAL, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + recipientEncryptedKeys RecipientEncryptedKeys } + + OriginatorIdentifierOrKey ::= CHOICE { + issuerAndSerialNumber IssuerAndSerialNumber, + subjectKeyIdentifier \[0\] SubjectKeyIdentifier, + originatorKey \[1\] OriginatorPublicKey } + + OriginatorPublicKey ::= SEQUENCE { + algorithm AlgorithmIdentifier, + publicKey BIT STRING } + + RecipientEncryptedKeys ::= SEQUENCE OF RecipientEncryptedKey + + RecipientEncryptedKey ::= SEQUENCE { + rid KeyAgreeRecipientIdentifier, + encryptedKey EncryptedKey } + + KeyAgreeRecipientIdentifier ::= CHOICE { + issuerAndSerialNumber IssuerAndSerialNumber, + rKeyId \[0\] IMPLICIT RecipientKeyIdentifier } + + RecipientKeyIdentifier ::= SEQUENCE { + subjectKeyIdentifier SubjectKeyIdentifier, + date GeneralizedTime OPTIONAL, + other OtherKeyAttribute OPTIONAL } + + SubjectKeyIdentifier ::= OCTET STRING + + KEKRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 4 + kekid KEKIdentifier, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey EncryptedKey } + + KEKIdentifier ::= SEQUENCE { + keyIdentifier OCTET STRING, + date GeneralizedTime OPTIONAL, + other OtherKeyAttribute OPTIONAL } + + PasswordRecipientInfo ::= SEQUENCE { + version CMSVersion, -- always set to 0 + keyDerivationAlgorithm \[0\] KeyDerivationAlgorithmIdentifier + OPTIONAL, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey EncryptedKey } + + OtherRecipientInfo ::= SEQUENCE { + oriType OBJECT IDENTIFIER, + oriValue ANY DEFINED BY oriType } + + DigestedData ::= SEQUENCE { + version CMSVersion, + digestAlgorithm DigestAlgorithmIdentifier, + encapContentInfo EncapsulatedContentInfo, + digest Digest } + + Digest ::= OCTET STRING + + EncryptedData ::= SEQUENCE { + version CMSVersion, + encryptedContentInfo EncryptedContentInfo, + unprotectedAttrs \[1\] IMPLICIT UnprotectedAttributes OPTIONAL } + + AuthenticatedData ::= SEQUENCE { + version CMSVersion, + originatorInfo \[0\] IMPLICIT OriginatorInfo OPTIONAL, + recipientInfos RecipientInfos, + macAlgorithm MessageAuthenticationCodeAlgorithm, + digestAlgorithm \[1\] DigestAlgorithmIdentifier OPTIONAL, + encapContentInfo EncapsulatedContentInfo, + authAttrs \[2\] IMPLICIT AuthAttributes OPTIONAL, + mac MessageAuthenticationCode, + unauthAttrs \[3\] IMPLICIT UnauthAttributes OPTIONAL } + + AuthAttributes ::= SET SIZE (1..MAX) OF Attribute + + UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute + + MessageAuthenticationCode ::= OCTET STRING + + DigestAlgorithmIdentifier ::= AlgorithmIdentifier + + SignatureAlgorithmIdentifier ::= AlgorithmIdentifier + + KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + + ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + + MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier + + KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier + + RevocationInfoChoices ::= SET OF RevocationInfoChoice + + RevocationInfoChoice ::= CHOICE { + crl CertificateList, + other \[1\] IMPLICIT OtherRevocationInfoFormat } + + OtherRevocationInfoFormat ::= SEQUENCE { + otherRevInfoFormat OBJECT IDENTIFIER, + otherRevInfo ANY DEFINED BY otherRevInfoFormat } + + CertificateChoices ::= CHOICE { + certificate Certificate, + extendedCertificate \[0\] IMPLICIT ExtendedCertificate, -- Obsolete + v1AttrCert \[1\] IMPLICIT AttributeCertificateV1, -- Obsolete + v2AttrCert \[2\] IMPLICIT AttributeCertificateV2, + other \[3\] IMPLICIT OtherCertificateFormat } + + AttributeCertificateV2 ::= AttributeCertificate + + OtherCertificateFormat ::= SEQUENCE { + otherCertFormat OBJECT IDENTIFIER, + otherCert ANY DEFINED BY otherCertFormat } + + CertificateSet ::= SET OF CertificateChoices +*/ + +/// IssuerAndSerialNumber ::= SEQUENCE { +/// issuer Name, +/// serialNumber CertificateSerialNumber } +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Sequence)] +pub struct IssuerAndSerialNumber<'a> { + /// issuer Name, + pub issuer: Name<'a>, + /// serialNumber CertificateSerialNumber } + pub serial_number: UIntBytes<'a>, +} + +/* + CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) } + + UserKeyingMaterial ::= OCTET STRING + + OtherKeyAttribute ::= SEQUENCE { + keyAttrId OBJECT IDENTIFIER, + keyAttr ANY DEFINED BY keyAttrId OPTIONAL } + + -- Content Type Object Identifiers + + id-ct-contentInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-ct(1) 6 } + + id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 } + + id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 } + + id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 } + + id-digestedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 5 } + + id-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 } + + id-ct-authData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-ct(1) 2 } + + -- The CMS Attributes + + MessageDigest ::= OCTET STRING + + SigningTime ::= Time + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Countersignature ::= SignerInfo + + -- Attribute Object Identifiers + + id-contentType OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 3 } + + id-messageDigest OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 4 } + + id-signingTime OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 5 } + + id-countersignature OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 6 } + + -- Obsolete Extended Certificate syntax from PKCS #6 + + ExtendedCertificateOrCertificate ::= CHOICE { + certificate Certificate, + extendedCertificate \[0\] IMPLICIT ExtendedCertificate } + + ExtendedCertificate ::= SEQUENCE { + extendedCertificateInfo ExtendedCertificateInfo, + signatureAlgorithm SignatureAlgorithmIdentifier, + signature Signature } + + ExtendedCertificateInfo ::= SEQUENCE { + version CMSVersion, + certificate Certificate, + attributes UnauthAttributes } + + Signature ::= BIT STRING + + END -- of CryptographicMessageSyntax2004 +*/ diff --git a/pkcs7/src/lib.rs b/pkcs7/src/lib.rs index 8ca7f3d28..098937212 100644 --- a/pkcs7/src/lib.rs +++ b/pkcs7/src/lib.rs @@ -15,12 +15,15 @@ mod content_type; pub use crate::{content_info::ContentInfo, content_type::ContentType}; +pub mod cryptographic_message_syntax2004; pub mod data_content; pub mod encrypted_data_content; pub mod enveloped_data_content; use der::asn1::ObjectIdentifier; +extern crate alloc; + /// `pkcs-7` Object Identifier (OID). pub const PKCS_7_OID: ObjectIdentifier = ObjectIdentifier::new("1.2.840.113549.1.7"); diff --git a/pkcs7/tests/cryptographic_message_syntax2004.rs b/pkcs7/tests/cryptographic_message_syntax2004.rs new file mode 100644 index 000000000..4927a480b --- /dev/null +++ b/pkcs7/tests/cryptographic_message_syntax2004.rs @@ -0,0 +1,23 @@ +use pkcs7::cryptographic_message_syntax2004::*; +use x509::der::{Decodable, Encodable}; + +#[test] +fn signed_data_parse_test1() { + let der_encoded_sd = include_bytes!("examples/caCertsIssuedTofbcag4.p7c"); + let ci = ContentInfo2004::from_der(der_encoded_sd).unwrap(); + let content = ci.content.unwrap().to_vec().unwrap(); + let _sd = SignedData::from_der(content.as_slice()).unwrap(); + //assert_eq!(1, sd.certificates.unwrap().len()); + + let der_encoded_sd = include_bytes!("examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c"); + let ci = ContentInfo2004::from_der(der_encoded_sd).unwrap(); + let content = ci.content.unwrap().to_vec().unwrap(); + let sd = SignedData::from_der(content.as_slice()).unwrap(); + assert_eq!(1, sd.certificates.unwrap().len()); + + let der_encoded_sd = include_bytes!("examples/DODROOTCA3_IB.p7c"); + let ci = ContentInfo2004::from_der(der_encoded_sd).unwrap(); + let content = ci.content.unwrap().to_vec().unwrap(); + let sd = SignedData::from_der(content.as_slice()).unwrap(); + assert_eq!(26, sd.certificates.unwrap().len()); +} diff --git a/pkcs7/tests/examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c b/pkcs7/tests/examples/DODJITCINTEROPERABILITYROOTCA2_IT.p7c new file mode 100644 index 0000000000000000000000000000000000000000..a37ec8b7c8fce72e1a80c40d8f73597f89ddef1a GIT binary patch literal 1935 zcmbW2c~BE)7>Dz1HXA}hGL)2~Qy?NBawOk|Tfr$LAQ6Wk25KD?Ls%d~NJ5BKL760$ z6c3b&gJ7uz#YzRHUUd+!mRgHF>~Kk~9ksAUV{=lOH%YwghO<#zy=#ek(a7jiIWHi~&5*^6;+=t+UAhag+LSS*@vSVfrhrG(y$ zyF&qGxE?O4ndyp%g-OY{8yUdy2$d;RDR=-BQONZ0Q4!^4Q7TbDnAAE^vPoM|M2KWb zqWMwK&&y4U!$>>^kB0HlkxNOGIw353)i^xp`uuLD+#tuNiOhzF!_enJW|B^{K%tU} z6nZmZG8oA~`C6UUyhfB|Fqo-KQfLDK&P@R<2Ga%v4Dz`vVgrD2!CPK3Bds_b>wI$g zaU1sefz-mTjUVVzzscq=ACrhi@)n(|32zfExc5VobkESvi+3tcjv<}9ayai>A0NCS zOIvcK|J*}8BkDxR@n!uXr$Q&2MjPrUVg{qO$4RU9G)%c_2CfxOR_gv>xH6`?R^8FsYUFJh*`c=QEEfr<`0esVy%z?dvc^s^MoGa=P28}`D zuUSH1`|jDXYkbzm_3F{E$<{A=s)jmDPnTQE{RO!<6OT2-mium4@_TrX;!;{zA1LZR zRX^XwyngeIb%T}L7{NPspJ;42@^S9du`6D+#qxiLH8JCL$8|Wu1Prjp#=H$}%yH;T zcGj1VdZNOu*F(C#8tc`B+_^eF)jw-#=|2|-pg*N&qU0D^g!lVgs!vLP%jF&%ZuYPW zSJ!rJuZMivQX1*W@o>D#4Acf#Bmn5;92Mg@Vk3Y%izd)%5lR!9X%Ps$ zX{BCvfQQlyI(>@Aq8W{o;m}Nmlfh_4>15b6lj&q!XvR5#E6m4S!Z5&Mu>lv&fhw5w zf+5SET5L8O=SUfY#kq89(YZtUI7}dyDlW7~d8PqhD+LL7z4+_$KZF z-7#0rw%`r*cf$*Sk0+CC@9S1gktI3rMDmY<+=+mZeoN|ox_xWs%&Mk@eZf<_o5c5d zqo!8gwOh-AOCs?@g|+)?iW@6RtBemuY8RcUs!q=qc8q$}xsT*D51VI9-Hta_z9naT znf~qOA%wYdVYcPa*&nMf5E8av+0nqCY6d~aWRK)_Z~L4vt+Mif!E*FWz;)ejr{_9yHep9i|^ z!Mzb>0d>t=?ys<|9Rk+&2l+mSny=tqqBD_ m9&+BVE?d3<;9q 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Ý{MTF7uaOeUaI4WbGlAyvd#-!o{k_llN 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 literal 0 HcmV?d00001 diff --git a/spki/src/algorithm.rs b/spki/src/algorithm.rs index b138e5075..2560d3f31 100644 --- a/spki/src/algorithm.rs +++ b/spki/src/algorithm.rs @@ -16,7 +16,7 @@ use der::{ /// ``` /// /// [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2 -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct AlgorithmIdentifier<'a> { /// Algorithm OID, i.e. the `algorithm` field in the `AlgorithmIdentifier` /// ASN.1 schema. From d93883085dc176d39843a31419804ddde5bf06b5 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Thu, 27 Jan 2022 20:07:29 -0500 Subject: [PATCH 3/4] dropped Ord, PartialOrd for AlgorithmIdentifier and SignerInfo, stubbed out ValueOrd for SignerInfo (not needed for dealing with certs-only SignedData structures) --- pkcs7/src/cryptographic_message_syntax2004.rs | 12 +++++++++--- spki/src/algorithm.rs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pkcs7/src/cryptographic_message_syntax2004.rs b/pkcs7/src/cryptographic_message_syntax2004.rs index 2d5b0118d..9fc118ad4 100644 --- a/pkcs7/src/cryptographic_message_syntax2004.rs +++ b/pkcs7/src/cryptographic_message_syntax2004.rs @@ -1,11 +1,13 @@ //! Selected structures from RFC5652 +use core::cmp::Ordering; use der::asn1::{BitString, ContextSpecific, SetOf, UIntBytes}; +use der::Sequence; +use der::ValueOrd; use der::{ Any, DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Tag, TagMode, TagNumber, }; -use der::{OrdIsValueOrd, Sequence}; use spki::{AlgorithmIdentifier, ObjectIdentifier}; use x509::{AttributeTypeAndValue, Name, SubjectKeyIdentifier}; @@ -197,7 +199,7 @@ impl<'a> ::der::Sequence<'a> for EncapsulatedContentInfo<'a> { /// signatureAlgorithm SignatureAlgorithmIdentifier, /// signature SignatureValue, /// unsignedAttrs \[1\] IMPLICIT UnsignedAttributes OPTIONAL } -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Sequence)] +#[derive(Clone, Eq, PartialEq, Sequence)] pub struct SignerInfo<'a> { /// version CMSVersion, pub version: u8, @@ -214,7 +216,11 @@ pub struct SignerInfo<'a> { /// unsignedAttrs \[1\] IMPLICIT UnsignedAttributes OPTIONAL } pub unsigned_attrs: UnsignedAttributes<'a>, } -impl OrdIsValueOrd for SignerInfo<'_> {} +impl ValueOrd for SignerInfo<'_> { + fn value_cmp(&self, _other: &Self) -> der::Result { + todo!() + } +} /// SignerIdentifier ::= CHOICE { /// issuerAndSerialNumber IssuerAndSerialNumber, diff --git a/spki/src/algorithm.rs b/spki/src/algorithm.rs index 2560d3f31..b138e5075 100644 --- a/spki/src/algorithm.rs +++ b/spki/src/algorithm.rs @@ -16,7 +16,7 @@ use der::{ /// ``` /// /// [RFC 5280 Section 4.1.1.2]: https://tools.ietf.org/html/rfc5280#section-4.1.1.2 -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct AlgorithmIdentifier<'a> { /// Algorithm OID, i.e. the `algorithm` field in the `AlgorithmIdentifier` /// ASN.1 schema. From 7348c3822b1d83c608c17ce1e0a1df558ee9c258 Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Wed, 2 Feb 2022 10:16:49 -0500 Subject: [PATCH 4/4] use X501 crate for Name and AttributeTypeAndValue --- Cargo.lock | 1 + pkcs7/Cargo.toml | 1 + pkcs7/src/cryptographic_message_syntax2004.rs | 4 +++- x509/src/trust_anchor_format.rs | 3 ++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20d65fdbf..1a49b037a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,6 +565,7 @@ dependencies = [ "der", "hex-literal", "spki", + "x501", "x509", ] diff --git a/pkcs7/Cargo.toml b/pkcs7/Cargo.toml index 517680ef9..d2b46786e 100644 --- a/pkcs7/Cargo.toml +++ b/pkcs7/Cargo.toml @@ -18,6 +18,7 @@ rust-version = "1.57" der = { version = "=0.6.0-pre.0", features = ["oid"], path = "../der" } spki = { version = "=0.6.0-pre", path = "../spki" } x509 = { version = "0.0.1", path = "../x509" } +x501 = { version = "0.1.0", path = "../x501" } [dev-dependencies] hex-literal = "0.3" diff --git a/pkcs7/src/cryptographic_message_syntax2004.rs b/pkcs7/src/cryptographic_message_syntax2004.rs index 9fc118ad4..757a1dcca 100644 --- a/pkcs7/src/cryptographic_message_syntax2004.rs +++ b/pkcs7/src/cryptographic_message_syntax2004.rs @@ -9,7 +9,9 @@ use der::{ TagNumber, }; use spki::{AlgorithmIdentifier, ObjectIdentifier}; -use x509::{AttributeTypeAndValue, Name, SubjectKeyIdentifier}; +use x501::attr::AttributeTypeAndValue; +use x501::name::Name; +use x509::SubjectKeyIdentifier; /// ContentInfo ::= SEQUENCE { /// contentType ContentType, diff --git a/x509/src/trust_anchor_format.rs b/x509/src/trust_anchor_format.rs index ead33f489..1dcb78119 100644 --- a/x509/src/trust_anchor_format.rs +++ b/x509/src/trust_anchor_format.rs @@ -1,12 +1,13 @@ //! Trust anchor-related structures as defined in RFC 5914 -use crate::{Certificate, CertificatePolicies, Extensions, Name, NameConstraints}; +use crate::{Certificate, CertificatePolicies, Extensions, NameConstraints}; use der::asn1::{BitString, ContextSpecific, OctetString, Utf8String}; use der::{ DecodeValue, Decoder, Encodable, EncodeValue, ErrorKind, FixedTag, Length, Tag, TagMode, TagNumber, }; use spki::SubjectPublicKeyInfo; +use x501::name::Name; /// TrustAnchorInfo ::= SEQUENCE { /// version TrustAnchorInfoVersion DEFAULT v1,