rasn_pkix/
est.rs

1//! # Enrollment over Secure Transport
2//!
3//! This module implements [RFC 7030]'s data types in certificate enrollment
4//! for clients using Certificate Management over CMS (CMC) messages over a
5//! secure transport. This profile, called Enrollment over Secure Transport
6//! (EST), describes a simple, yet functional, certificate management protocol
7//! targeting Public Key Infrastructure (PKI) clients that need to acquire
8//! client certificates and associated Certification Authority (CA) certificates.
9
10use rasn::prelude::*;
11
12/// The identifier of the secret key to be used by the server to encrypt the
13/// private key.
14pub type AsymmetricDecryptKeyIdentifier = rasn::types::OctetString;
15/// The main body of a CSR attribute request.
16pub type CsrAttrs = SequenceOf<AttrOrOid>;
17
18/// The OID identifying a `DecryptKeyIdentifier` attribute.
19pub const ASYMMETRIC_DECRYPT_KEY_OID: &Oid =
20    Oid::ISO_MEMBER_BODY_US_RSADSI_PKCS9_SMIME_AA_ASYMMETRIC_DECRYPT_KEY;
21
22/// Either an OID pointing a specific signature scheme or an attribute for
23/// a particular crypto system.
24#[derive(AsnType, Decode, Encode, Debug, PartialEq, Clone)]
25#[rasn(choice)]
26pub enum AttrOrOid {
27    Oid(ObjectIdentifier),
28    Attribute(Attribute),
29}
30
31#[derive(AsnType, Decode, Encode, Debug, PartialEq, Clone)]
32pub struct Attribute {
33    pub r#type: ObjectIdentifier,
34    pub values: SetOf<Any>,
35}
36
37#[cfg(test)]
38mod tests {
39    use pretty_assertions::assert_eq;
40
41    use alloc::{borrow::Cow, collections::BTreeSet, string::ToString, vec};
42
43    use super::*;
44
45    #[test]
46    fn csr_attributes_encode() {
47        let data = vec![
48            // ecdsaWithSHA256 (ANSI X9.62 ECDSA algorithm with SHA256)
49            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
50                vec![1, 2, 840, 10045, 4, 3, 2],
51            ))),
52            // commonName (X.520 DN component)
53            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
54                vec![2, 5, 4, 3],
55            ))),
56            // emailAddress (PKCS #9. Deprecated, use an altName extension instead)
57            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
58                vec![1, 2, 840, 113549, 1, 9, 1],
59            ))),
60            // challengePassword (PKCS #9)
61            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
62                vec![1, 2, 840, 113549, 1, 9, 7],
63            ))),
64            // ocsp (PKIX)
65            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
66                vec![1, 3, 6, 1, 5, 5, 7, 48, 1],
67            ))),
68            // requestClientInfo (Microsoft attribute)
69            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
70                vec![1, 3, 6, 1, 4, 1, 311, 21, 20],
71            ))),
72            // 1.2.840.113549.1.1.5 (sha1WithRsaEncryption)
73            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
74                vec![1, 2, 840, 113549, 1, 1, 5],
75            ))),
76            AttrOrOid::Attribute(Attribute {
77                r#type: rasn::types::ObjectIdentifier::new_unchecked(Cow::from(vec![
78                    1, 3, 6, 1, 5, 5, 7, 48, 1,
79                ])),
80                values: {
81                    let mut b = BTreeSet::new();
82                    b.insert(rasn::types::Any::new(
83                        rasn::der::encode(
84                            &rasn::types::PrintableString::try_from("And me second".to_string())
85                                .unwrap(),
86                        )
87                        .unwrap(),
88                    ));
89                    b.insert(rasn::types::Any::new(rasn::der::encode(&false).unwrap()));
90                    b.insert(rasn::types::Any::new(
91                        rasn::der::encode(&rasn::types::Open::Null).unwrap(),
92                    ));
93                    b.insert(rasn::types::Any::new(
94                        rasn::der::encode(
95                            &rasn::types::VisibleString::try_from("Me first!").unwrap(),
96                        )
97                        .unwrap(),
98                    ));
99                    b
100                },
101            }),
102        ];
103
104        let bin = rasn::der::encode(&data).unwrap();
105        assert_eq!(
106            bin,
107            [
108                48, 114, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 6, 3, 85, 4, 3, 6, 9, 42, 134, 72,
109                134, 247, 13, 1, 9, 1, 6, 9, 42, 134, 72, 134, 247, 13, 1, 9, 7, 6, 8, 43, 6, 1, 5,
110                5, 7, 48, 1, 6, 9, 43, 6, 1, 4, 1, 130, 55, 21, 20, 6, 9, 42, 134, 72, 134, 247,
111                13, 1, 1, 5, 48, 43, 6, 8, 43, 6, 1, 5, 5, 7, 48, 1, 49, 31, 1, 1, 0, 5, 0, 19, 13,
112                65, 110, 100, 32, 109, 101, 32, 115, 101, 99, 111, 110, 100, 26, 9, 77, 101, 32,
113                102, 105, 114, 115, 116, 33
114            ]
115        );
116    }
117
118    #[test]
119    fn csr_attributes_decode_1() {
120        let data = vec![
121            // challengePassword (PKCS #9)
122            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
123                vec![1, 2, 840, 113549, 1, 9, 7],
124            ))),
125            // ecPublicKey (ANSI X9.62 public key type)
126            AttrOrOid::Attribute(Attribute {
127                r#type: rasn::types::ObjectIdentifier::new_unchecked(Cow::from(vec![
128                    1, 2, 840, 10045, 2, 1,
129                ])),
130                values: {
131                    let mut b = BTreeSet::new();
132                    b.insert(rasn::types::Any::new(
133                        // secp384r1 (SECG (Certicom) named elliptic curve)
134                        rasn::der::encode(&rasn::types::ObjectIdentifier::new_unchecked(
135                            Cow::from(vec![1, 3, 132, 0, 34]),
136                        ))
137                        .unwrap(),
138                    ));
139                    b
140                },
141            }),
142            AttrOrOid::Attribute(Attribute {
143                // extensionRequest (PKCS #9 via CRMF)
144                r#type: rasn::types::ObjectIdentifier::new_unchecked(Cow::from(vec![
145                    1, 2, 840, 113549, 1, 9, 14,
146                ])),
147                values: {
148                    let mut b = BTreeSet::new();
149                    b.insert(rasn::types::Any::new(
150                        rasn::der::encode(&rasn::types::ObjectIdentifier::new_unchecked(
151                            Cow::from(vec![1, 3, 6, 1, 1, 1, 1, 22]),
152                        ))
153                        .unwrap(),
154                    ));
155                    b
156                },
157            }),
158            // ecdsaWithSHA384 (ANSI X9.62 ECDSA algorithm with SHA384)
159            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
160                vec![1, 2, 840, 10045, 4, 3, 3],
161            ))),
162        ];
163
164        let data_bin = rasn::der::encode(&data).unwrap();
165        let txt = "MEEGCSqGSIb3DQEJBzASBgcqhkjOPQIBMQcGBSuBBAAiMBYGCSqGSIb3DQEJDjEJBgcrBgEBAQEWBggqhkjOPQQDAw==";
166        let bin = base64::decode(txt).unwrap();
167        assert_eq!(data_bin, bin);
168        let decoded_data = rasn::der::decode::<CsrAttrs>(&bin);
169        assert!(decoded_data.is_ok());
170        let decoded_data = decoded_data.unwrap();
171        assert_eq!(decoded_data, data);
172    }
173
174    #[test]
175    fn csr_attributes_decode_2() {
176        let data = vec![
177            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
178                vec![1, 3, 6, 1, 1, 1, 1, 22],
179            ))),
180            // ecPublicKey (ANSI X9.62 public key type)
181            AttrOrOid::Attribute(Attribute {
182                r#type: rasn::types::ObjectIdentifier::new_unchecked(Cow::from(vec![2, 999, 1])),
183                values: {
184                    let mut b = BTreeSet::new();
185                    b.insert(rasn::types::Any::new(
186                        rasn::der::encode(
187                            &rasn::types::PrintableString::try_from(
188                                "Parse SET as 2.999.1 data".to_string(),
189                            )
190                            .unwrap(),
191                        )
192                        .unwrap(),
193                    ));
194                    b
195                },
196            }),
197            // challengePassword (PKCS #9)
198            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
199                vec![1, 2, 840, 113549, 1, 9, 7],
200            ))),
201            AttrOrOid::Attribute(Attribute {
202                r#type: rasn::types::ObjectIdentifier::new_unchecked(Cow::from(vec![2, 999, 2])),
203                values: {
204                    let mut b = BTreeSet::new();
205                    b.insert(rasn::types::Any::new(
206                        rasn::der::encode(&rasn::types::ObjectIdentifier::new_unchecked(
207                            Cow::from(vec![2, 999, 3]),
208                        ))
209                        .unwrap(),
210                    ));
211                    b.insert(rasn::types::Any::new(
212                        rasn::der::encode(&rasn::types::ObjectIdentifier::new_unchecked(
213                            Cow::from(vec![2, 999, 4]),
214                        ))
215                        .unwrap(),
216                    ));
217                    b.insert(rasn::types::Any::new(
218                        rasn::der::encode(
219                            &rasn::types::PrintableString::try_from(
220                                "Parse SET as 2.999.2 data".to_string(),
221                            )
222                            .unwrap(),
223                        )
224                        .unwrap(),
225                    ));
226                    b
227                },
228            }),
229            // brainpoolP384r1 (ECC Brainpool Standard Curves and Curve Generation)
230            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
231                vec![1, 3, 36, 3, 3, 2, 8, 1, 1, 11],
232            ))),
233            // sha-384 (NIST Algorithm)
234            AttrOrOid::Oid(rasn::types::ObjectIdentifier::new_unchecked(Cow::from(
235                vec![2, 16, 840, 1, 101, 3, 4, 2, 2],
236            ))),
237        ];
238
239        let data_bin = rasn::der::encode(&data).unwrap();
240        let txt = "MHwGBysGAQEBARYwIgYDiDcBMRsTGVBhcnNlIFNFVCBhcyAyLjk5OS4xIGRhdGEGCSqGSIb3DQEJBzAsBgOINwIxJQYDiDcDBgOINwQTGVBhcnNlIFNFVCBhcyAyLjk5OS4yIGRhdGEGCSskAwMCCAEBCwYJYIZIAWUDBAIC";
241        let bin = base64::decode(txt).unwrap();
242        assert_eq!(bin, data_bin);
243        let decoded_data = rasn::der::decode::<CsrAttrs>(&bin);
244        assert!(decoded_data.is_ok());
245        let decoded_data = decoded_data.unwrap();
246        assert_eq!(decoded_data, data);
247    }
248}