1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! ContentInfo types

use crate::cert::CertificateChoices;
use crate::revocation::RevocationInfoChoices;
use crate::signed_data::EncapsulatedContentInfo;
use crate::signed_data::{CertificateSet, SignedData, SignerInfos};
use core::cmp::Ordering;
use der::asn1::SetOfVec;
use der::Encode;
use der::{asn1::ObjectIdentifier, Any, AnyRef, Enumerated, Sequence, ValueOrd};
use x509_cert::{Certificate, PkiPath};

/// The `OtherCertificateFormat` type is defined in [RFC 5652 Section 10.2.5].
///
/// ```text
///  CMSVersion ::= INTEGER  { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }
/// ```
///
/// [RFC 5652 Section 10.2.5]: https://www.rfc-editor.org/rfc/rfc5652#section-10.2.5
#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Enumerated)]
#[asn1(type = "INTEGER")]
#[repr(u8)]
#[allow(missing_docs)]
pub enum CmsVersion {
    V0 = 0,
    V1 = 1,
    V2 = 2,
    V3 = 3,
    V4 = 4,
    V5 = 5,
}

impl ValueOrd for CmsVersion {
    fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
        (*self as u8).value_cmp(&(*other as u8))
    }
}

/// The `ContentInfo` type is defined in [RFC 5652 Section 3].
///
/// ```text
///   ContentInfo ::= SEQUENCE {
///       contentType        CONTENT-TYPE.
///                       &id({ContentSet}),
///       content            [0] EXPLICIT CONTENT-TYPE.
///                       &Type({ContentSet}{@contentType})}
/// ```
///
/// [RFC 5652 Section 3]: https://www.rfc-editor.org/rfc/rfc5652#section-3
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
#[allow(missing_docs)]
pub struct ContentInfo {
    pub content_type: ObjectIdentifier,
    #[asn1(context_specific = "0", tag_mode = "EXPLICIT")]
    pub content: Any,
}

/// Convert a Certificate to a certs-only SignedData message
impl TryFrom<Certificate> for ContentInfo {
    type Error = der::Error;

    fn try_from(cert: Certificate) -> der::Result<Self> {
        let mut certs = CertificateSet(Default::default());
        certs.0.insert(CertificateChoices::Certificate(cert))?;

        // include empty CRLs field instead of omitting it to match OpenSSL's behavior
        let sd = SignedData {
            version: CmsVersion::V1,
            digest_algorithms: SetOfVec::default(),
            encap_content_info: EncapsulatedContentInfo {
                econtent_type: const_oid::db::rfc5911::ID_DATA,
                econtent: None,
            },
            certificates: Some(certs),
            crls: Some(RevocationInfoChoices(Default::default())),
            signer_infos: SignerInfos(Default::default()),
        };

        let signed_data = sd.to_der()?;
        let content = AnyRef::try_from(signed_data.as_slice())?;

        Ok(ContentInfo {
            content_type: const_oid::db::rfc5911::ID_SIGNED_DATA,
            content: Any::from(content),
        })
    }
}

/// Convert a vector of Certificates to a certs-only SignedData message
impl TryFrom<PkiPath> for ContentInfo {
    type Error = der::Error;

    fn try_from(pki_path: PkiPath) -> der::Result<Self> {
        let mut certs = CertificateSet(Default::default());
        for cert in pki_path {
            certs.0.insert(CertificateChoices::Certificate(cert))?;
        }

        // include empty CRLs field instead of omitting it to match OpenSSL's behavior
        let sd = SignedData {
            version: CmsVersion::V1,
            digest_algorithms: SetOfVec::default(),
            encap_content_info: EncapsulatedContentInfo {
                econtent_type: const_oid::db::rfc5911::ID_DATA,
                econtent: None,
            },
            certificates: Some(certs),
            crls: Some(RevocationInfoChoices(Default::default())),
            signer_infos: SignerInfos(Default::default()),
        };

        let signed_data = sd.to_der()?;
        let content = AnyRef::try_from(signed_data.as_slice())?;

        Ok(ContentInfo {
            content_type: const_oid::db::rfc5911::ID_SIGNED_DATA,
            content: Any::from(content),
        })
    }
}