1 //! Name tests
2
3 use const_oid::ObjectIdentifier;
4 use der::asn1::{Ia5StringRef, OctetStringRef, PrintableStringRef, SetOfVec, Utf8StringRef};
5 use der::{Any, Decode, Encode, Tag, Tagged};
6 use hex_literal::hex;
7 use x509_cert::attr::AttributeTypeAndValue;
8 use x509_cert::name::{Name, RdnSequence, RelativeDistinguishedName};
9
10 #[test]
decode_name()11 fn decode_name() {
12 // 134 64: SEQUENCE {
13 // 136 11: SET {
14 // 138 9: SEQUENCE {
15 // 140 3: OBJECT IDENTIFIER countryName (2 5 4 6)
16 // 145 2: PrintableString 'US'
17 // : }
18 // : }
19 // 149 31: SET {
20 // 151 29: SEQUENCE {
21 // 153 3: OBJECT IDENTIFIER organizationName (2 5 4 10)
22 // 158 22: PrintableString 'Test Certificates 2011'
23 // : }
24 // : }
25 // 182 16: SET {
26 // 184 14: SEQUENCE {
27 // 186 3: OBJECT IDENTIFIER commonName (2 5 4 3)
28 // 191 7: PrintableString 'Good CA'
29 // : }
30 // : }
31 // : }
32 let rdn1 =
33 Name::from_der(&hex!("3040310B3009060355040613025553311F301D060355040A1316546573742043657274696669636174657320323031313110300E06035504031307476F6F64204341")[..]);
34 let rdn1a = rdn1.unwrap();
35
36 let mut counter = 0;
37 let i = rdn1a.0.iter();
38 for rdn in i {
39 let i1 = rdn.0.iter();
40 for atav in i1 {
41 if 0 == counter {
42 assert_eq!(atav.oid.to_string(), "2.5.4.6");
43 assert_eq!(
44 PrintableStringRef::try_from(&atav.value)
45 .unwrap()
46 .to_string(),
47 "US"
48 );
49 } else if 1 == counter {
50 assert_eq!(atav.oid.to_string(), "2.5.4.10");
51 assert_eq!(
52 PrintableStringRef::try_from(&atav.value)
53 .unwrap()
54 .to_string(),
55 "Test Certificates 2011"
56 );
57 } else if 2 == counter {
58 assert_eq!(atav.oid.to_string(), "2.5.4.3");
59 assert_eq!(
60 PrintableStringRef::try_from(&atav.value)
61 .unwrap()
62 .to_string(),
63 "Good CA"
64 );
65 }
66 counter += 1;
67 }
68 }
69
70 #[cfg(feature = "std")]
71 {
72 // https://datatracker.ietf.org/doc/html/rfc4514.html#section-2.1
73 // If the RDNSequence is an empty sequence, the result is the empty or
74 // zero-length string.
75 // Otherwise, the output consists of the string encodings of each
76 // RelativeDistinguishedName in the RDNSequence (according to Section 2.2),
77 // starting with the last element of the sequence and moving backwards
78 // toward the first.
79 // The encodings of adjoining RelativeDistinguishedNames are separated by
80 // a comma (',' U+002C) character.
81 let name = rdn1a.to_string();
82 assert_eq!(name, "CN=Good CA,O=Test Certificates 2011,C=US");
83
84 // https://github.com/RustCrypto/formats/issues/1121
85 let rdn1 = Name::from_der(&hex!("3081c0310b30090603550406130255533113301106035504080c0a43616c69666f726e69613116301406035504070c0d4d6f756e7461696e205669657731133011060355040a0c0a476f6f676c65204c4c43311e301c06035504030c154f51464176444e4457732e676f6f676c652e636f6d31243022060355040b0c1b6d616e6167656d656e743a64732e67726f75702e3338393131313131293027060a0992268993f22c6401010c196964656e746974793a64732e67726f75702e33383931313131")[..]);
86 let rdn1a = rdn1.unwrap();
87 let name = rdn1a.to_string();
88 assert_eq!(name, "UID=identity:ds.group.3891111,OU=management:ds.group.3891111,CN=OQFAvDNDWs.google.com,O=Google LLC,L=Mountain View,ST=California,C=US");
89 }
90 }
91
92 #[test]
decode_rdn()93 fn decode_rdn() {
94 // 0 11: SET {
95 // 2 9: SEQUENCE {
96 // 4 3: OBJECT IDENTIFIER countryName (2 5 4 6)
97 // 9 2: PrintableString 'US'
98 // : }
99 // : }
100 let rdn1 =
101 RelativeDistinguishedName::from_der(&hex!("310B3009060355040613025553")[..]).unwrap();
102 let i = rdn1.0.iter();
103 for atav in i {
104 let oid = atav.oid;
105 assert_eq!(oid.to_string(), "2.5.4.6");
106 let value = &atav.value;
107 assert_eq!(value.tag(), Tag::PrintableString);
108 let ps = PrintableStringRef::try_from(value).unwrap();
109 assert_eq!(ps.to_string(), "US");
110 }
111
112 // 0 31: SET {
113 // 2 17: SEQUENCE {
114 // 4 3: OBJECT IDENTIFIER commonName (2 5 4 3)
115 // 9 10: UTF8String 'JOHN SMITH'
116 // : }
117 // 21 10: SEQUENCE {
118 // 23 3: OBJECT IDENTIFIER organizationName (2 5 4 10)
119 // 28 3: UTF8String '123'
120 // : }
121 // : }
122
123 // reordered the encoding so second element above appears first so parsing can succeed
124 let rdn2a = RelativeDistinguishedName::from_der(
125 &hex!("311F300A060355040A0C03313233301106035504030C0A4A4F484E20534D495448")[..],
126 )
127 .unwrap();
128 let mut i = rdn2a.0.iter();
129 let atav1a = i.next().unwrap();
130 let oid2 = atav1a.oid;
131 assert_eq!(oid2.to_string(), "2.5.4.10");
132 let value2 = &atav1a.value;
133 assert_eq!(value2.tag(), Tag::Utf8String);
134 let utf8b = Utf8StringRef::try_from(value2).unwrap();
135 assert_eq!(utf8b.to_string(), "123");
136
137 let atav2a = i.next().unwrap();
138 let oid1 = atav2a.oid;
139 assert_eq!(oid1.to_string(), "2.5.4.3");
140 let value1 = &atav2a.value;
141 assert_eq!(value1.tag(), Tag::Utf8String);
142 let utf8a = Utf8StringRef::try_from(value1).unwrap();
143 assert_eq!(utf8a.to_string(), "JOHN SMITH");
144
145 let mut from_scratch = RelativeDistinguishedName::default();
146 assert!(from_scratch.0.insert(atav1a.clone()).is_ok());
147 assert!(from_scratch.0.insert(atav2a.clone()).is_ok());
148 let reencoded = from_scratch.to_der().unwrap();
149 assert_eq!(
150 reencoded,
151 &hex!("311F300A060355040A0C03313233301106035504030C0A4A4F484E20534D495448")
152 );
153
154 let mut from_scratch2 = RelativeDistinguishedName::default();
155 assert!(from_scratch2.0.insert_ordered(atav2a.clone()).is_ok());
156 // fails when caller adds items not in DER lexicographical order
157 assert!(from_scratch2.0.insert_ordered(atav1a.clone()).is_err());
158
159 // allow out-of-order RDNs (see: RustCrypto/formats#625)
160 assert!(RelativeDistinguishedName::from_der(
161 &hex!("311F301106035504030C0A4A4F484E20534D495448300A060355040A0C03313233")[..],
162 )
163 .is_ok());
164 }
165
166 // #[test]
167 // fn encode_atav() {
168 // // 0 11: SET {
169 // // 2 9: SEQUENCE {
170 // // 4 3: OBJECT IDENTIFIER countryName (2 5 4 6)
171 // // 9 2: PrintableString 'US'
172 // // : }
173 // // : }
174 // let rdn1 =
175 // RelativeDistinguishedName::from_der(&hex!("310B3009060355040613025553")[..]).unwrap();
176 //
177 // // Re-encode and compare to reference
178 // let b1 = rdn1.to_vec().unwrap();
179 // assert_eq!(b1, &hex!("310B3009060355040613025553")[..]);
180 // let mut i = rdn1.iter();
181 // let atav1 = i.next().unwrap();
182 //
183 // // 0 31: SET {
184 // // 2 17: SEQUENCE {
185 // // 4 3: OBJECT IDENTIFIER commonName (2 5 4 3)
186 // // 9 10: UTF8String 'JOHN SMITH'
187 // // : }
188 // // 21 10: SEQUENCE {
189 // // 23 3: OBJECT IDENTIFIER organizationName (2 5 4 10)
190 // // 28 3: UTF8String '123'
191 // // : }
192 // // : }
193 // let rdn2 = RelativeDistinguishedName::from_der(
194 // &hex!("311F301106035504030C0A4A4F484E20534D495448300A060355040A0C03313233")[..],
195 // )
196 // .unwrap();
197 //
198 // // Re-encode and compare to reference
199 // let b1 = rdn2.to_vec().unwrap();
200 // assert_eq!(
201 // b1,
202 // &hex!("311F301106035504030C0A4A4F484E20534D495448300A060355040A0C03313233")[..]
203 // );
204 //
205 // let mut i = rdn2.iter();
206 // let atav2 = i.next().unwrap();
207 //
208 // // Create new AttributeTypeAndValue with OID from second item above and value from first
209 // let atav3: AttributeTypeAndValue = AttributeTypeAndValue {
210 // oid: atav2.oid,
211 // value: atav1.value,
212 // };
213 // let b3 = atav3.to_vec().unwrap();
214 // assert_eq!(b3, &hex!("3009060355040313025553")[..]);
215 // }
216
217 /// Tests RdnSequence string serialization and deserialization
218 #[test]
rdns_serde()219 fn rdns_serde() {
220 #[allow(clippy::type_complexity)]
221 let values: &[(&[&str], &str, &[&[AttributeTypeAndValue]])] = &[
222 (
223 &[
224 "CN=foo,SN=bar,C=baz+L=bat",
225 "commonName=foo,sn=bar,COUNTRYNAME=baz+l=bat",
226 ],
227 "CN=foo,SN=bar,C=baz+L=bat",
228 &[
229 &[
230 AttributeTypeAndValue {
231 oid: const_oid::db::rfc4519::C,
232 value: Any::from(PrintableStringRef::new("baz").unwrap()),
233 },
234 AttributeTypeAndValue {
235 oid: const_oid::db::rfc4519::L,
236 value: Any::from(Utf8StringRef::new("bat").unwrap()),
237 },
238 ],
239 &[AttributeTypeAndValue {
240 oid: const_oid::db::rfc4519::SN,
241 value: Any::from(Utf8StringRef::new("bar").unwrap()),
242 }],
243 &[AttributeTypeAndValue {
244 oid: const_oid::db::rfc4519::CN,
245 value: Any::from(Utf8StringRef::new("foo").unwrap()),
246 }],
247 ],
248 ),
249 (
250 &["UID=jsmith,DC=example,DC=net"],
251 "UID=jsmith,DC=example,DC=net",
252 &[
253 &[AttributeTypeAndValue {
254 oid: const_oid::db::rfc4519::DC,
255 value: Any::from(Ia5StringRef::new("net").unwrap()),
256 }],
257 &[AttributeTypeAndValue {
258 oid: const_oid::db::rfc4519::DC,
259 value: Any::from(Ia5StringRef::new("example").unwrap()),
260 }],
261 &[AttributeTypeAndValue {
262 oid: const_oid::db::rfc4519::UID,
263 value: Any::from(Utf8StringRef::new("jsmith").unwrap()),
264 }],
265 ],
266 ),
267 (
268 &["OU=Sales+CN=J. Smith,DC=example,DC=net"],
269 "OU=Sales+CN=J. Smith,DC=example,DC=net",
270 &[
271 &[AttributeTypeAndValue {
272 oid: const_oid::db::rfc4519::DC,
273 value: Any::from(Ia5StringRef::new("net").unwrap()),
274 }],
275 &[AttributeTypeAndValue {
276 oid: const_oid::db::rfc4519::DC,
277 value: Any::from(Ia5StringRef::new("example").unwrap()),
278 }],
279 &[
280 AttributeTypeAndValue {
281 oid: const_oid::db::rfc4519::OU,
282 value: Any::from(Utf8StringRef::new("Sales").unwrap()),
283 },
284 AttributeTypeAndValue {
285 oid: const_oid::db::rfc4519::CN,
286 value: Any::from(Utf8StringRef::new("J. Smith").unwrap()),
287 },
288 ],
289 ],
290 ),
291 (
292 &["CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net"],
293 "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net",
294 &[
295 &[AttributeTypeAndValue {
296 oid: const_oid::db::rfc4519::DC,
297 value: Any::from(Ia5StringRef::new("net").unwrap()),
298 }],
299 &[AttributeTypeAndValue {
300 oid: const_oid::db::rfc4519::DC,
301 value: Any::from(Ia5StringRef::new("example").unwrap()),
302 }],
303 &[AttributeTypeAndValue {
304 oid: const_oid::db::rfc4519::CN,
305 value: Any::from(Utf8StringRef::new(r#"James "Jim" Smith, III"#).unwrap()),
306 }],
307 ],
308 ),
309 (
310 &["CN=Before\\0dAfter,DC=example,DC=net"],
311 "CN=Before\\0dAfter,DC=example,DC=net",
312 &[
313 &[AttributeTypeAndValue {
314 oid: const_oid::db::rfc4519::DC,
315 value: Any::from(Ia5StringRef::new("net").unwrap()),
316 }],
317 &[AttributeTypeAndValue {
318 oid: const_oid::db::rfc4519::DC,
319 value: Any::from(Ia5StringRef::new("example").unwrap()),
320 }],
321 &[AttributeTypeAndValue {
322 oid: const_oid::db::rfc4519::CN,
323 value: Any::from(Utf8StringRef::new("Before\rAfter").unwrap()),
324 }],
325 ],
326 ),
327 (
328 &["1.3.6.1.4.1.1466.0=#04024869"],
329 "1.3.6.1.4.1.1466.0=#04024869",
330 &[&[AttributeTypeAndValue {
331 oid: ObjectIdentifier::new("1.3.6.1.4.1.1466.0").unwrap(),
332 value: Any::from(OctetStringRef::new(&[b'H', b'i']).unwrap()),
333 }]],
334 ),
335 ];
336
337 for (inputs, output, rdns) in values {
338 let mut brdns = RdnSequence::default();
339 for rdn in rdns.iter() {
340 let sofv = SetOfVec::try_from(rdn.to_vec()).unwrap();
341 brdns.0.push(RelativeDistinguishedName::from(sofv));
342 }
343
344 // Check that serialization matches the expected output.
345 eprintln!("output: {}", output);
346 assert_eq!(*output, format!("{}", brdns));
347
348 // Check that all inputs deserializize as expected.
349 for input in inputs.iter() {
350 eprintln!("input: {}", input);
351
352 let der = input
353 .parse::<RdnSequence>()
354 .and_then(|rdn| rdn.to_der())
355 .unwrap();
356
357 let rdns = RdnSequence::from_der(&der).unwrap();
358
359 for (l, r) in brdns.0.iter().zip(rdns.0.iter()) {
360 for (ll, rr) in l.0.iter().zip(r.0.iter()) {
361 assert_eq!(ll, rr);
362 }
363
364 assert_eq!(l, r);
365 }
366
367 assert_eq!(brdns, rdns);
368 }
369 }
370 }
371