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