picky_asn1_x509/
attribute.rs

1#[cfg(feature = "pkcs7")]
2use crate::pkcs7::content_info::SpcSpOpusInfo;
3use crate::{oids, Extension, Extensions};
4use picky_asn1::date::UTCTime;
5use picky_asn1::wrapper::{Asn1SequenceOf, Asn1SetOf, ObjectIdentifierAsn1, OctetStringAsn1, UtcTimeAsn1};
6use serde::{de, ser};
7
8pub type Attributes = Asn1SequenceOf<Attribute>;
9
10/// [RFC 2985 page 15 and 16](https://tools.ietf.org/html/rfc2985#page-15)
11///
12/// Accepted attribute types are `challengePassword` (TODO), `extensionRequest`,
13/// `contentType`, `messageDigest` and `spcSpOpusInfo`
14///
15/// `spcSpOpusInfo` is behind the `pkcs7` feature.
16///
17/// `contentType`, `messageDigest`, `spcSpOpusInfo` and `SigningTime` are used for [microsoft authenticode](http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx)
18#[derive(Clone, Debug, PartialEq)]
19pub enum AttributeValues {
20    /// `extensionRequest`
21    Extensions(Asn1SetOf<Extensions>), // the set will always have 1 element in this variant
22    // TODO: support for challenge password
23    // ChallengePassword(Asn1SetOf<ChallengePassword>))
24    ContentType(Asn1SetOf<ObjectIdentifierAsn1>),
25    SpcStatementType(Asn1SetOf<Asn1SequenceOf<ObjectIdentifierAsn1>>),
26    MessageDigest(Asn1SetOf<OctetStringAsn1>),
27    SigningTime(Asn1SetOf<UtcTimeAsn1>),
28    #[cfg(feature = "pkcs7")]
29    SpcSpOpusInfo(Asn1SetOf<SpcSpOpusInfo>),
30    Custom(picky_asn1_der::Asn1RawDer), // fallback
31}
32
33#[derive(Clone, Debug, PartialEq)]
34pub struct Attribute {
35    pub ty: ObjectIdentifierAsn1,
36    pub value: AttributeValues,
37}
38
39impl Attribute {
40    pub fn new_extension_request(extensions: Vec<Extension>) -> Self {
41        Self {
42            ty: oids::extension_request().into(),
43            value: AttributeValues::Extensions(Asn1SetOf(vec![Extensions(extensions)])),
44        }
45    }
46
47    pub fn new_content_type_pkcs7() -> Self {
48        Self {
49            ty: oids::content_type().into(),
50            value: AttributeValues::ContentType(vec![oids::content_info_type_data().into()].into()),
51        }
52    }
53
54    pub fn new_signing_time(signing_time: UTCTime) -> Self {
55        Self {
56            ty: oids::signing_time().into(),
57            value: AttributeValues::SigningTime(vec![signing_time.into()].into()),
58        }
59    }
60
61    pub fn new_message_digest(digest: Vec<u8>) -> Self {
62        Self {
63            ty: oids::message_digest().into(),
64            value: AttributeValues::MessageDigest(vec![digest.into()].into()),
65        }
66    }
67}
68
69impl ser::Serialize for Attribute {
70    fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
71    where
72        S: ser::Serializer,
73    {
74        use ser::SerializeSeq;
75        let mut seq = serializer.serialize_seq(Some(2))?;
76        seq.serialize_element(&self.ty)?;
77        match &self.value {
78            AttributeValues::Extensions(extensions) => seq.serialize_element(extensions)?,
79            AttributeValues::Custom(der) => seq.serialize_element(der)?,
80            AttributeValues::ContentType(oid) => seq.serialize_element(oid)?,
81            AttributeValues::MessageDigest(octet_string) => seq.serialize_element(octet_string)?,
82            AttributeValues::SigningTime(signing_time) => seq.serialize_element(signing_time)?,
83            #[cfg(feature = "pkcs7")]
84            AttributeValues::SpcSpOpusInfo(spc_sp_opus_info) => seq.serialize_element(spc_sp_opus_info)?,
85            AttributeValues::SpcStatementType(spc_statement_type) => seq.serialize_element(spc_statement_type)?,
86        }
87        seq.end()
88    }
89}
90
91impl<'de> de::Deserialize<'de> for Attribute {
92    fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
93    where
94        D: de::Deserializer<'de>,
95    {
96        use std::fmt;
97
98        struct Visitor;
99
100        impl<'de> de::Visitor<'de> for Visitor {
101            type Value = Attribute;
102
103            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
104                formatter.write_str("a valid DER-encoded attribute")
105            }
106
107            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
108            where
109                A: de::SeqAccess<'de>,
110            {
111                let ty: ObjectIdentifierAsn1 = seq_next_element!(seq, Attribute, "type oid");
112
113                let value = match Into::<String>::into(&ty.0).as_str() {
114                    oids::EXTENSION_REQ => {
115                        AttributeValues::Extensions(seq_next_element!(seq, Attribute, "at extension request"))
116                    }
117                    oids::CONTENT_TYPE => {
118                        AttributeValues::ContentType(seq_next_element!(seq, Attribute, "message digest oid"))
119                    }
120                    oids::MESSAGE_DIGEST => {
121                        AttributeValues::MessageDigest(seq_next_element!(seq, Attribute, "an octet string"))
122                    }
123                    oids::SIGNING_TIME => AttributeValues::SigningTime(seq_next_element!(seq, Attribute, "UTCTime")),
124                    #[cfg(feature = "pkcs7")]
125                    oids::SPC_SP_OPUS_INFO_OBJID => {
126                        AttributeValues::SpcSpOpusInfo(seq_next_element!(seq, Attribute, "an SpcSpOpusInfo object"))
127                    }
128                    oids::SPC_STATEMENT_TYPE => {
129                        AttributeValues::SpcStatementType(seq_next_element!(seq, Attribute, "an SpcStatementType"))
130                    }
131                    _ => AttributeValues::Custom(seq_next_element!(seq, Attribute, "at custom value")),
132                };
133
134                Ok(Attribute { ty, value })
135            }
136        }
137
138        deserializer.deserialize_seq(Visitor)
139    }
140}