8#include <botan/pkix_types.h>
10#include <botan/assert.h>
11#include <botan/ber_dec.h>
12#include <botan/der_enc.h>
13#include <botan/internal/charset.h>
14#include <botan/internal/loadstor.h>
15#include <botan/internal/x509_utils.h>
27bool is_space(
char c) {
28 return c ==
' ' || c ==
'\t';
31std::optional<uint8_t> hex_digit_value(
char c) {
32 if(c >=
'0' && c <=
'9') {
33 return static_cast<uint8_t
>(c -
'0');
34 }
else if(c >=
'a' && c <=
'f') {
35 return static_cast<uint8_t
>(c -
'a' + 10);
36 }
else if(c >=
'A' && c <=
'F') {
37 return static_cast<uint8_t
>(c -
'A' + 10);
46bool is_escapable_char(
char c) {
64bool is_unescaped_special_value_char(
char c) {
80class X500_Char_Iterator final {
82 explicit X500_Char_Iterator(std::string_view s) : m_str(s), m_pos(0) {
84 while(m_pos < m_str.size() && is_space(m_str[m_pos])) {
90 std::optional<char> next() {
91 if(m_pos >= m_str.size()) {
95 if(is_space(m_str[m_pos])) {
97 while(m_pos < m_str.size() && is_space(m_str[m_pos])) {
101 if(m_pos < m_str.size()) {
107 const char c = m_str[m_pos++];
109 if(c >=
'A' && c <=
'Z') {
110 return static_cast<char>(c + (
'a' -
'A'));
115 static std::string canonicalize(std::string_view name) {
117 result.reserve(name.size());
119 X500_Char_Iterator it(name);
120 while(
auto c = it.next()) {
128 std::string_view m_str;
150 m_rdn.push_back({std::make_pair(oid, str)});
152 update_canonical_bits();
159 m_rdn.push_back(std::move(rdn));
161 update_canonical_bits();
165 std::vector<std::pair<OID, ASN1_String>> flat;
166 for(
const auto& rdn : m_rdn) {
167 for(
const auto& ava : rdn) {
178 std::multimap<OID, std::string> retval;
180 for(
const auto& rdn : m_rdn) {
181 for(
const auto& ava : rdn) {
182 retval.emplace(ava.first, ava.second.value());
192 std::multimap<std::string, std::string> retval;
194 for(
const auto& rdn : m_rdn) {
195 for(
const auto& ava : rdn) {
196 retval.emplace(ava.first.to_formatted_string(), ava.second.value());
214 for(
const auto& rdn : m_rdn) {
215 for(
const auto& ava : rdn) {
216 if(ava.first == oid) {
231 for(
const auto& rdn : m_rdn) {
232 for(
const auto& ava : rdn) {
233 if(ava.first == oid) {
248 std::vector<std::string> values;
250 for(
const auto& rdn : m_rdn) {
251 for(
const auto& ava : rdn) {
252 if(ava.first == oid) {
253 values.push_back(ava.second.value());
265 if(info ==
"Name" || info ==
"CommonName" || info ==
"CN") {
266 return "X520.CommonName";
268 if(info ==
"SerialNumber" || info ==
"SN") {
269 return "X520.SerialNumber";
271 if(info ==
"Country" || info ==
"C") {
272 return "X520.Country";
274 if(info ==
"Organization" || info ==
"O") {
275 return "X520.Organization";
277 if(info ==
"Organizational Unit" || info ==
"OrgUnit" || info ==
"OU") {
278 return "X520.OrganizationalUnit";
280 if(info ==
"Locality" || info ==
"L") {
281 return "X520.Locality";
283 if(info ==
"State" || info ==
"Province" || info ==
"ST") {
286 if(info ==
"Email") {
289 return std::string(info);
299std::vector<std::pair<OID, std::string>> canonicalize_rdn(
const std::vector<std::pair<OID, ASN1_String>>& rdn) {
300 std::vector<std::pair<OID, std::string>> result;
301 result.reserve(rdn.size());
302 for(
const auto& ava : rdn) {
303 result.emplace_back(ava.first, X500_Char_Iterator::canonicalize(ava.second.value()));
305 if(result.size() != 1) {
306 std::sort(result.begin(), result.end());
311std::vector<uint8_t> canonicalize_dn(
const std::vector<std::vector<std::pair<OID, ASN1_String>>>& rdns) {
312 auto append_canonical_data = []<
typename T>(std::vector<uint8_t>& out,
const T& data) {
313 const std::array<uint8_t, 8> data_len =
store_le(
static_cast<uint64_t
>(data.size()));
314 out.insert(out.end(), data_len.begin(), data_len.end());
315 out.insert(out.end(), data.begin(), data.end());
318 std::vector<uint8_t> canonical_bits;
320 for(
const auto& rdn : rdns) {
321 std::vector<uint8_t> rdn_bits;
323 for(
const auto& [oid, value] : canonicalize_rdn(rdn)) {
324 append_canonical_data(rdn_bits, oid.BER_encode());
325 append_canonical_data(rdn_bits, value);
328 append_canonical_data(canonical_bits, rdn_bits);
331 return canonical_bits;
347 return !(dn1 == dn2);
362 if(constraint_bits.size() > name_bits.size()) {
366 return std::equal(constraint_bits.begin(), constraint_bits.end(), name_bits.begin());
369void X509_DN::update_canonical_bits() {
370 m_canonical_dn_bits = canonicalize_dn(m_rdn);
374 std::vector<uint8_t> result;
386 if(!m_dn_bits.empty()) {
393 for(
const auto& rdn : m_rdn) {
395 for(
const auto& ava : rdn) {
409 std::vector<uint8_t> bits;
415 std::vector<std::vector<std::pair<OID, ASN1_String>>>
rdns;
419 constexpr size_t MAX_AVAS_PER_RDN = 32;
424 std::vector<std::pair<OID, ASN1_String>> rdn;
434 rdn.emplace_back(std::move(oid), std::move(str));
436 if(rdn.size() > MAX_AVAS_PER_RDN) {
437 throw Decoding_Error(
"X.500 RDN has too many attribute-value assertions");
447 throw Decoding_Error(
"X.500 RDN must contain at least one attribute-value assertion");
449 rdns.push_back(std::move(rdn));
452 auto canonical_bits = canonicalize_dn(
rdns);
454 m_rdn = std::move(
rdns);
455 m_dn_bits = std::move(bits);
456 m_canonical_dn_bits = std::move(canonical_bits);
461std::string to_short_form(
const OID& oid) {
464 if(long_id ==
"X520.CommonName") {
468 if(long_id ==
"X520.Country") {
472 if(long_id ==
"X520.Organization") {
476 if(long_id ==
"X520.OrganizationalUnit") {
486 std::ostringstream out;
492 const auto& rdns = dn.
rdns();
496 auto hex_escape = [](std::ostream& s,
char c) {
497 const auto b =
static_cast<uint8_t
>(c);
504 bool first_rdn =
true;
505 for(
const auto& rdn : rdns) {
511 bool first_ava =
true;
512 for(
const auto& ava : rdn) {
517 out << to_short_form(ava.first) <<
"=\"";
518 const std::string_view value = ava.second.value();
520 while(pos < value.size()) {
521 const size_t start = pos;
528 hex_escape(out, value[start]);
533 if(cp ==
'\\' || cp ==
'"') {
534 out << '\\' << static_cast<char>(cp);
536 for(
size_t i = start; i < pos; ++i) {
537 hex_escape(out, value[i]);
540 out << value.substr(start, pos - start);
559 std::vector<std::pair<OID, ASN1_String>> pending_rdn;
563 char terminator =
'\0';
566 while(pos < str.size()) {
569 while(pos < str.size() && is_space(str[pos])) {
572 if(pos == str.size()) {
577 const size_t type_start = pos;
578 while(pos < str.size() && str[pos] !=
'=' && !is_space(str[pos])) {
581 const std::string_view type = str.substr(type_start, pos - type_start);
582 if(type.empty() || pos == str.size() || str[pos] !=
'=') {
594 size_t value_len = 0;
599 enum class Quote : uint8_t {
None, Open, Closed };
600 Quote quote = Quote::None;
604 while(pos < str.size()) {
605 const char c = str[pos];
608 if(quote == Quote::Open) {
609 quote = Quote::Closed;
610 }
else if(quote == Quote::None && value.empty()) {
616 }
else if(c ==
'\\') {
617 if(quote == Quote::Closed) {
622 if(pos == str.size()) {
625 if(
const auto hi = hex_digit_value(str[pos])) {
626 const auto lo = (pos + 1 < str.size()) ? hex_digit_value(str[pos + 1]) : std::nullopt;
630 value.push_back(
static_cast<char>((*hi << 4) | *lo));
632 }
else if(is_escapable_char(str[pos])) {
633 value.push_back(str[pos]);
638 value_len = value.size();
639 }
else if((c ==
',' || c ==
'+') && quote != Quote::Open) {
643 }
else if(quote == Quote::Closed) {
648 }
else if(quote != Quote::Open && is_unescaped_special_value_char(c)) {
652 if(is_space(c) && quote != Quote::Open) {
660 value_len = value.size();
665 if(quote == Quote::Open) {
668 value.resize(value_len);
674 pending_rdn.emplace_back(std::move(oid),
ASN1_String(value));
679 if(terminator !=
'+') {
680 dn.
add_rdn(std::move(pending_rdn));
686 if(terminator ==
',' || terminator ==
'+') {
693 const std::istreambuf_iterator<char> begin(in);
694 const std::istreambuf_iterator<char> end;
695 const std::string contents(begin, end);
698 dn = std::move(*parsed);
700 in.setstate(std::ios::failbit);
const std::string & value() const
BER_Decoder & decode(bool &out)
BER_Decoder & raw_bytes(std::vector< uint8_t, Alloc > &out)
BER_Decoder start_sequence()
DER_Encoder & start_set()
DER_Encoder & start_sequence()
DER_Encoder & raw_bytes(const uint8_t val[], size_t len)
DER_Encoder & encode(bool b)
std::string to_formatted_string() const
static OID from_string(std::string_view str)
const std::vector< std::vector< std::pair< OID, ASN1_String > > > & rdns() const
void add_rdn(std::vector< std::pair< OID, ASN1_String > > rdn)
static std::optional< X509_DN > parse(std::string_view str)
bool has_field(const OID &oid) const
std::multimap< std::string, std::string > contents() const
std::vector< std::pair< OID, ASN1_String > > dn_info() const
std::vector< std::string > get_attribute(std::string_view attr) const
void add_attribute(std::string_view key, std::string_view val)
std::multimap< OID, std::string > get_attributes() const
ASN1_String get_first_attribute(const OID &oid) const
void encode_into(DER_Encoder &to) const override
std::vector< uint8_t > DER_encode() const
std::string to_string() const
const std::vector< uint8_t > & _canonical_bytes() const
void decode_from(BER_Decoder &from) override
static std::string deref_info_field(std::string_view key)
constexpr char nibble_to_hex(uint8_t b)
bool is_unicode_control_char(uint32_t cp)
bool operator<(const OID &a, const OID &b)
std::ostream & operator<<(std::ostream &out, const OID &oid)
constexpr auto store_le(ParamTs &&... params)
bool x509_dn_subtree_match(const X509_DN &name, const X509_DN &constraint)
int operator>>(int fd, Pipe &pipe)
bool operator!=(const AlgorithmIdentifier &x, const AlgorithmIdentifier &y)
uint32_t next_utf8_codepoint(std::string_view utf8, size_t &pos)
bool operator==(const AlgorithmIdentifier &x, const AlgorithmIdentifier &y)