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