Botan 3.9.0
Crypto and TLS for C&
x509_dn.cpp
Go to the documentation of this file.
1/*
2* X509_DN
3* (C) 1999-2007,2018 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/pkix_types.h>
9
10#include <botan/ber_dec.h>
11#include <botan/der_enc.h>
12#include <botan/internal/stl_util.h>
13#include <cctype>
14#include <ostream>
15#include <sstream>
16
17namespace Botan {
18
19namespace {
20
21namespace {
22
23bool caseless_eq(char a, char b) {
24 return (std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b)));
25}
26
27bool is_space(char c) {
28 return c == ' ' || c == '\t';
29}
30
31} // namespace
32
33/*
34* X.500 String Comparison
35*/
36bool x500_name_cmp(std::string_view name1, std::string_view name2) {
37 // MSVC uses an actual iterator type for string_view, so we must use plain `auto` here
38 auto p1 = name1.begin(); // NOLINT(readability-qualified-auto)
39 auto p2 = name2.begin(); // NOLINT(readability-qualified-auto)
40
41 while((p1 != name1.end()) && is_space(*p1)) {
42 ++p1;
43 }
44 while((p2 != name2.end()) && is_space(*p2)) {
45 ++p2;
46 }
47
48 while(p1 != name1.end() && p2 != name2.end()) {
49 if(is_space(*p1)) {
50 if(!is_space(*p2)) {
51 return false;
52 }
53
54 while((p1 != name1.end()) && is_space(*p1)) {
55 ++p1;
56 }
57 while((p2 != name2.end()) && is_space(*p2)) {
58 ++p2;
59 }
60
61 if(p1 == name1.end() && p2 == name2.end()) {
62 return true;
63 }
64 if(p1 == name1.end() || p2 == name2.end()) {
65 return false;
66 }
67 }
68
69 if(!caseless_eq(*p1, *p2)) {
70 return false;
71 }
72 ++p1;
73 ++p2;
74 }
75
76 // Accept/ignore trailing spaces
77 while((p1 != name1.end()) && is_space(*p1)) {
78 ++p1;
79 }
80 if(p1 != name1.end()) {
81 return false;
82 }
83
84 while((p2 != name2.end()) && is_space(*p2)) {
85 ++p2;
86 }
87 if(p2 != name2.end()) {
88 return false;
89 }
90
91 // accept:
92 return true;
93}
94
95} // namespace
96
97/*
98* Add an attribute to a X509_DN
99*/
100void X509_DN::add_attribute(std::string_view type, std::string_view str) {
102}
103
104/*
105* Add an attribute to a X509_DN
106*/
107void X509_DN::add_attribute(const OID& oid, const ASN1_String& str) {
108 if(str.empty()) {
109 return;
110 }
111
112 m_rdn.push_back(std::make_pair(oid, str));
113 m_dn_bits.clear();
114}
115
116/*
117* Get the attributes of this X509_DN
118*/
119std::multimap<OID, std::string> X509_DN::get_attributes() const {
120 std::multimap<OID, std::string> retval;
121
122 for(const auto& i : m_rdn) {
123 retval.emplace(i.first, i.second.value());
124 }
125 return retval;
126}
127
128/*
129* Get the contents of this X.500 Name
130*/
131std::multimap<std::string, std::string> X509_DN::contents() const {
132 std::multimap<std::string, std::string> retval;
133
134 for(const auto& i : m_rdn) {
135 retval.emplace(i.first.to_formatted_string(), i.second.value());
136 }
137 return retval;
138}
139
140bool X509_DN::has_field(std::string_view attr) const {
141 try {
142 const OID o = OID::from_string(deref_info_field(attr));
143 if(o.has_value()) {
144 return has_field(o);
145 }
146 } catch(Lookup_Error&) {}
147
148 return false;
149}
150
151bool X509_DN::has_field(const OID& oid) const {
152 for(const auto& i : m_rdn) {
153 if(i.first == oid) {
154 return true;
155 }
156 }
157
158 return false;
159}
160
161std::string X509_DN::get_first_attribute(std::string_view attr) const {
162 const OID oid = OID::from_string(deref_info_field(attr));
163 return get_first_attribute(oid).value();
164}
165
167 for(const auto& i : m_rdn) {
168 if(i.first == oid) {
169 return i.second;
170 }
171 }
172
173 return ASN1_String();
174}
175
176/*
177* Get a single attribute type
178*/
179std::vector<std::string> X509_DN::get_attribute(std::string_view attr) const {
180 const OID oid = OID::from_string(deref_info_field(attr));
181
182 std::vector<std::string> values;
183
184 for(const auto& i : m_rdn) {
185 if(i.first == oid) {
186 values.push_back(i.second.value());
187 }
188 }
189
190 return values;
191}
192
193/*
194* Deref aliases in a subject/issuer info request
195*/
196std::string X509_DN::deref_info_field(std::string_view info) {
197 if(info == "Name" || info == "CommonName" || info == "CN") {
198 return "X520.CommonName";
199 }
200 if(info == "SerialNumber" || info == "SN") {
201 return "X520.SerialNumber";
202 }
203 if(info == "Country" || info == "C") {
204 return "X520.Country";
205 }
206 if(info == "Organization" || info == "O") {
207 return "X520.Organization";
208 }
209 if(info == "Organizational Unit" || info == "OrgUnit" || info == "OU") {
210 return "X520.OrganizationalUnit";
211 }
212 if(info == "Locality" || info == "L") {
213 return "X520.Locality";
214 }
215 if(info == "State" || info == "Province" || info == "ST") {
216 return "X520.State";
217 }
218 if(info == "Email") {
219 return "RFC822";
220 }
221 return std::string(info);
222}
223
224/*
225* Compare two X509_DNs for equality
226*/
227bool operator==(const X509_DN& dn1, const X509_DN& dn2) {
228 auto attr1 = dn1.get_attributes();
229 auto attr2 = dn2.get_attributes();
230
231 if(attr1.size() != attr2.size()) {
232 return false;
233 }
234
235 auto p1 = attr1.begin();
236 auto p2 = attr2.begin();
237
238 while(true) {
239 if(p1 == attr1.end() && p2 == attr2.end()) {
240 break;
241 }
242 if(p1 == attr1.end()) {
243 return false;
244 }
245 if(p2 == attr2.end()) {
246 return false;
247 }
248 if(p1->first != p2->first) {
249 return false;
250 }
251 if(!x500_name_cmp(p1->second, p2->second)) {
252 return false;
253 }
254 ++p1;
255 ++p2;
256 }
257 return true;
258}
259
260/*
261* Compare two X509_DNs for inequality
262*/
263bool operator!=(const X509_DN& dn1, const X509_DN& dn2) {
264 return !(dn1 == dn2);
265}
266
267/*
268* Induce an arbitrary ordering on DNs
269*/
270bool operator<(const X509_DN& dn1, const X509_DN& dn2) {
271 auto attr1 = dn1.get_attributes();
272 auto attr2 = dn2.get_attributes();
273
274 // If they are not the same size, choose the smaller as the "lessor"
275 if(attr1.size() < attr2.size()) {
276 return true;
277 }
278 if(attr1.size() > attr2.size()) {
279 return false;
280 }
281
282 // We know they are the same # of elements, now compare the OIDs:
283 auto p1 = attr1.begin();
284 auto p2 = attr2.begin();
285
286 while(p1 != attr1.end() && p2 != attr2.end()) {
287 if(p1->first != p2->first) {
288 return (p1->first < p2->first);
289 }
290
291 ++p1;
292 ++p2;
293 }
294
295 // We know this is true because maps have the same size
296 BOTAN_ASSERT_NOMSG(p1 == attr1.end());
297 BOTAN_ASSERT_NOMSG(p2 == attr2.end());
298
299 // Now we know all elements have the same OIDs, compare
300 // their string values:
301
302 p1 = attr1.begin();
303 p2 = attr2.begin();
304 while(p1 != attr1.end() && p2 != attr2.end()) {
305 BOTAN_DEBUG_ASSERT(p1->first == p2->first);
306
307 // They may be binary different but same by X.500 rules, check this
308 if(!x500_name_cmp(p1->second, p2->second)) {
309 // If they are not (by X.500) the same string, pick the
310 // lexicographic first as the lessor
311 return (p1->second < p2->second);
312 }
313
314 ++p1;
315 ++p2;
316 }
317
318 // if we reach here, then the DNs should be identical
319 BOTAN_DEBUG_ASSERT(dn1 == dn2);
320 return false;
321}
322
323std::vector<uint8_t> X509_DN::DER_encode() const {
324 std::vector<uint8_t> result;
325 DER_Encoder der(result);
326 this->encode_into(der);
327 return result;
328}
329
330/*
331* DER encode a DistinguishedName
332*/
334 der.start_sequence();
335
336 if(!m_dn_bits.empty()) {
337 /*
338 If we decoded this from somewhere, encode it back exactly as
339 we received it
340 */
341 der.raw_bytes(m_dn_bits);
342 } else {
343 for(const auto& dn : m_rdn) {
344 der.start_set().start_sequence().encode(dn.first).encode(dn.second).end_cons().end_cons();
345 }
346 }
347
348 der.end_cons();
349}
350
351/*
352* Decode a BER encoded DistinguishedName
353*/
355 std::vector<uint8_t> bits;
356
357 source.start_sequence().raw_bytes(bits).end_cons();
358
359 BER_Decoder sequence(bits);
360
361 m_rdn.clear();
362
363 while(sequence.more_items()) {
364 BER_Decoder rdn = sequence.start_set();
365
366 while(rdn.more_items()) {
367 OID oid;
368 ASN1_String str;
369
370 rdn.start_sequence()
371 .decode(oid)
372 .decode(str) // TODO support Any
373 .end_cons();
374
375 add_attribute(oid, str);
376 }
377 }
378
379 // Have to assign last as add_attribute zaps m_dn_bits
380 m_dn_bits = bits;
381}
382
383namespace {
384
385std::string to_short_form(const OID& oid) {
386 std::string long_id = oid.to_formatted_string();
387
388 if(long_id == "X520.CommonName") {
389 return "CN";
390 }
391
392 if(long_id == "X520.Country") {
393 return "C";
394 }
395
396 if(long_id == "X520.Organization") {
397 return "O";
398 }
399
400 if(long_id == "X520.OrganizationalUnit") {
401 return "OU";
402 }
403
404 return long_id;
405}
406
407} // namespace
408
409std::string X509_DN::to_string() const {
410 std::ostringstream out;
411 out << *this;
412 return out.str();
413}
414
415std::ostream& operator<<(std::ostream& out, const X509_DN& dn) {
416 const auto& info = dn.dn_info();
417
418 for(size_t i = 0; i != info.size(); ++i) {
419 out << to_short_form(info[i].first) << "=\"";
420 for(char c : info[i].second.value()) {
421 if(c == '\\' || c == '\"') {
422 out << "\\";
423 }
424 out << c;
425 }
426 out << "\"";
427
428 if(i + 1 < info.size()) {
429 out << ",";
430 }
431 }
432 return out;
433}
434
435std::istream& operator>>(std::istream& in, X509_DN& dn) {
436 in >> std::noskipws;
437 // NOLINTNEXTLINE(*-avoid-do-while)
438 do {
439 std::string key;
440 std::string val;
441 char c = 0;
442
443 while(in.good()) {
444 in >> c;
445
446 if(is_space(c) && key.empty()) {
447 continue;
448 } else if(!is_space(c)) {
449 key.push_back(c);
450 break;
451 } else {
452 break;
453 }
454 }
455
456 while(in.good()) {
457 in >> c;
458
459 if(!is_space(c) && c != '=') {
460 key.push_back(c);
461 } else if(c == '=') {
462 break;
463 } else {
464 throw Invalid_Argument("Ill-formed X.509 DN");
465 }
466 }
467
468 bool in_quotes = false;
469 while(in.good()) {
470 in >> c;
471
472 if(is_space(c)) {
473 if(!in_quotes && !val.empty()) {
474 break;
475 } else if(in_quotes) {
476 val.push_back(' ');
477 }
478 } else if(c == '"') {
479 in_quotes = !in_quotes;
480 } else if(c == '\\') {
481 if(in.good()) {
482 in >> c;
483 }
484 val.push_back(c);
485 } else if(c == ',' && !in_quotes) {
486 break;
487 } else {
488 val.push_back(c);
489 }
490 }
491
492 if(!key.empty() && !val.empty()) {
494 } else {
495 break;
496 }
497 } while(in.good());
498 return in;
499}
500} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
const std::string & value() const
Definition asn1_obj.h:437
bool empty() const
Definition asn1_obj.h:441
BER_Decoder start_set()
Definition ber_dec.h:127
BER_Decoder & decode(bool &out)
Definition ber_dec.h:188
bool more_items() const
Definition ber_dec.cpp:204
BER_Decoder & raw_bytes(std::vector< uint8_t, Alloc > &out)
Definition ber_dec.h:174
BER_Decoder & end_cons()
Definition ber_dec.cpp:312
BER_Decoder start_sequence()
Definition ber_dec.h:125
DER_Encoder & start_set()
Definition der_enc.h:67
DER_Encoder & start_sequence()
Definition der_enc.h:65
DER_Encoder & raw_bytes(const uint8_t val[], size_t len)
Definition der_enc.cpp:209
DER_Encoder & end_cons()
Definition der_enc.cpp:173
DER_Encoder & encode(bool b)
Definition der_enc.cpp:252
std::string to_formatted_string() const
Definition asn1_oid.cpp:139
bool has_value() const
Definition asn1_obj.h:271
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:86
bool has_field(const OID &oid) const
Definition x509_dn.cpp:151
std::multimap< std::string, std::string > contents() const
Definition x509_dn.cpp:131
std::vector< std::string > get_attribute(std::string_view attr) const
Definition x509_dn.cpp:179
void add_attribute(std::string_view key, std::string_view val)
Definition x509_dn.cpp:100
std::multimap< OID, std::string > get_attributes() const
Definition x509_dn.cpp:119
ASN1_String get_first_attribute(const OID &oid) const
Definition x509_dn.cpp:166
const std::vector< std::pair< OID, ASN1_String > > & dn_info() const
Definition pkix_types.h:82
void encode_into(DER_Encoder &to) const override
Definition x509_dn.cpp:333
std::vector< uint8_t > DER_encode() const
Definition x509_dn.cpp:323
std::string to_string() const
Definition x509_dn.cpp:409
void decode_from(BER_Decoder &from) override
Definition x509_dn.cpp:354
static std::string deref_info_field(std::string_view key)
Definition x509_dn.cpp:196
bool operator<(const OID &a, const OID &b)
Definition asn1_oid.cpp:173
std::ostream & operator<<(std::ostream &out, const OID &oid)
Definition asn1_obj.h:334
bool operator!=(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition alg_id.cpp:68
bool operator==(const AlgorithmIdentifier &a1, const AlgorithmIdentifier &a2)
Definition alg_id.cpp:53
int operator>>(int fd, Pipe &pipe)
Definition fd_unix.cpp:39