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