cms/
content_info.rs

1//! ContentInfo types
2
3use crate::cert::CertificateChoices;
4use crate::revocation::RevocationInfoChoices;
5use crate::signed_data::EncapsulatedContentInfo;
6use crate::signed_data::{CertificateSet, SignedData, SignerInfos};
7use core::cmp::Ordering;
8use der::asn1::SetOfVec;
9use der::Encode;
10use der::{asn1::ObjectIdentifier, Any, AnyRef, Enumerated, Sequence, ValueOrd};
11use x509_cert::{Certificate, PkiPath};
12
13/// The `OtherCertificateFormat` type is defined in [RFC 5652 Section 10.2.5].
14///
15/// ```text
16///  CMSVersion ::= INTEGER  { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }
17/// ```
18///
19/// [RFC 5652 Section 10.2.5]: https://www.rfc-editor.org/rfc/rfc5652#section-10.2.5
20#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Enumerated)]
21#[asn1(type = "INTEGER")]
22#[repr(u8)]
23#[allow(missing_docs)]
24pub enum CmsVersion {
25    V0 = 0,
26    V1 = 1,
27    V2 = 2,
28    V3 = 3,
29    V4 = 4,
30    V5 = 5,
31}
32
33impl ValueOrd for CmsVersion {
34    fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
35        (*self as u8).value_cmp(&(*other as u8))
36    }
37}
38
39/// The `ContentInfo` type is defined in [RFC 5652 Section 3].
40///
41/// ```text
42///   ContentInfo ::= SEQUENCE {
43///       contentType        CONTENT-TYPE.
44///                       &id({ContentSet}),
45///       content            [0] EXPLICIT CONTENT-TYPE.
46///                       &Type({ContentSet}{@contentType})}
47/// ```
48///
49/// [RFC 5652 Section 3]: https://www.rfc-editor.org/rfc/rfc5652#section-3
50#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
51#[allow(missing_docs)]
52pub struct ContentInfo {
53    pub content_type: ObjectIdentifier,
54    #[asn1(context_specific = "0", tag_mode = "EXPLICIT")]
55    pub content: Any,
56}
57
58/// Convert a Certificate to a certs-only SignedData message
59impl TryFrom<Certificate> for ContentInfo {
60    type Error = der::Error;
61
62    fn try_from(cert: Certificate) -> der::Result<Self> {
63        let mut certs = CertificateSet(Default::default());
64        certs.0.insert(CertificateChoices::Certificate(cert))?;
65
66        // include empty CRLs field instead of omitting it to match OpenSSL's behavior
67        let sd = SignedData {
68            version: CmsVersion::V1,
69            digest_algorithms: SetOfVec::default(),
70            encap_content_info: EncapsulatedContentInfo {
71                econtent_type: const_oid::db::rfc5911::ID_DATA,
72                econtent: None,
73            },
74            certificates: Some(certs),
75            crls: Some(RevocationInfoChoices(Default::default())),
76            signer_infos: SignerInfos(Default::default()),
77        };
78
79        let signed_data = sd.to_der()?;
80        let content = AnyRef::try_from(signed_data.as_slice())?;
81
82        Ok(ContentInfo {
83            content_type: const_oid::db::rfc5911::ID_SIGNED_DATA,
84            content: Any::from(content),
85        })
86    }
87}
88
89/// Convert a vector of Certificates to a certs-only SignedData message
90impl TryFrom<PkiPath> for ContentInfo {
91    type Error = der::Error;
92
93    fn try_from(pki_path: PkiPath) -> der::Result<Self> {
94        let mut certs = CertificateSet(Default::default());
95        for cert in pki_path {
96            certs.0.insert(CertificateChoices::Certificate(cert))?;
97        }
98
99        // include empty CRLs field instead of omitting it to match OpenSSL's behavior
100        let sd = SignedData {
101            version: CmsVersion::V1,
102            digest_algorithms: SetOfVec::default(),
103            encap_content_info: EncapsulatedContentInfo {
104                econtent_type: const_oid::db::rfc5911::ID_DATA,
105                econtent: None,
106            },
107            certificates: Some(certs),
108            crls: Some(RevocationInfoChoices(Default::default())),
109            signer_infos: SignerInfos(Default::default()),
110        };
111
112        let signed_data = sd.to_der()?;
113        let content = AnyRef::try_from(signed_data.as_slice())?;
114
115        Ok(ContentInfo {
116            content_type: const_oid::db::rfc5911::ID_SIGNED_DATA,
117            content: Any::from(content),
118        })
119    }
120}