|  | // Copyright 2016 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "parse_name.h" | 
|  |  | 
|  | #include <cassert> | 
|  |  | 
|  | #include <openssl/bytestring.h> | 
|  | #include <openssl/mem.h> | 
|  |  | 
|  | #include "parse_values.h" | 
|  | #include "string_util.h" | 
|  |  | 
|  | namespace bssl { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Returns a string containing the dotted numeric form of |oid|, or an empty | 
|  | // string on error. | 
|  | std::string OidToString(der::Input oid) { | 
|  | CBS cbs; | 
|  | CBS_init(&cbs, oid.data(), oid.size()); | 
|  | bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs)); | 
|  | if (!text) { | 
|  | return std::string(); | 
|  | } | 
|  | return text.get(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool X509NameAttribute::ValueAsString(std::string *out) const { | 
|  | switch (value_tag) { | 
|  | case CBS_ASN1_T61STRING: | 
|  | return der::ParseTeletexStringAsLatin1(value, out); | 
|  | case CBS_ASN1_IA5STRING: | 
|  | return der::ParseIA5String(value, out); | 
|  | case CBS_ASN1_PRINTABLESTRING: | 
|  | return der::ParsePrintableString(value, out); | 
|  | case CBS_ASN1_UTF8STRING: | 
|  | *out = BytesAsStringView(value); | 
|  | return true; | 
|  | case CBS_ASN1_UNIVERSALSTRING: | 
|  | return der::ParseUniversalString(value, out); | 
|  | case CBS_ASN1_BMPSTRING: | 
|  | return der::ParseBmpString(value, out); | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool X509NameAttribute::ValueAsStringWithUnsafeOptions( | 
|  | PrintableStringHandling printable_string_handling, std::string *out) const { | 
|  | if (printable_string_handling == PrintableStringHandling::kAsUTF8Hack && | 
|  | value_tag == CBS_ASN1_PRINTABLESTRING) { | 
|  | *out = BytesAsStringView(value); | 
|  | return true; | 
|  | } | 
|  | return ValueAsString(out); | 
|  | } | 
|  |  | 
|  | bool X509NameAttribute::ValueAsStringUnsafe(std::string *out) const { | 
|  | switch (value_tag) { | 
|  | case CBS_ASN1_IA5STRING: | 
|  | case CBS_ASN1_PRINTABLESTRING: | 
|  | case CBS_ASN1_T61STRING: | 
|  | case CBS_ASN1_UTF8STRING: | 
|  | *out = BytesAsStringView(value); | 
|  | return true; | 
|  | case CBS_ASN1_UNIVERSALSTRING: | 
|  | return der::ParseUniversalString(value, out); | 
|  | case CBS_ASN1_BMPSTRING: | 
|  | return der::ParseBmpString(value, out); | 
|  | default: | 
|  | assert(0);  // NOTREACHED | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool X509NameAttribute::AsRFC2253String(std::string *out) const { | 
|  | std::string type_string; | 
|  | std::string value_string; | 
|  | // TODO(mattm): Add streetAddress and domainComponent here? | 
|  | if (type == der::Input(kTypeCommonNameOid)) { | 
|  | type_string = "CN"; | 
|  | } else if (type == der::Input(kTypeSurnameOid)) { | 
|  | type_string = "SN"; | 
|  | } else if (type == der::Input(kTypeCountryNameOid)) { | 
|  | type_string = "C"; | 
|  | } else if (type == der::Input(kTypeLocalityNameOid)) { | 
|  | type_string = "L"; | 
|  | } else if (type == der::Input(kTypeStateOrProvinceNameOid)) { | 
|  | type_string = "ST"; | 
|  | } else if (type == der::Input(kTypeOrganizationNameOid)) { | 
|  | type_string = "O"; | 
|  | } else if (type == der::Input(kTypeOrganizationUnitNameOid)) { | 
|  | type_string = "OU"; | 
|  | } else if (type == der::Input(kTypeGivenNameOid)) { | 
|  | type_string = "givenName"; | 
|  | } else if (type == der::Input(kTypeEmailAddressOid)) { | 
|  | type_string = "emailAddress"; | 
|  | } else { | 
|  | type_string = OidToString(type); | 
|  | if (type_string.empty()) { | 
|  | return false; | 
|  | } | 
|  | value_string = "#" + bssl::string_util::HexEncode(value); | 
|  | } | 
|  |  | 
|  | if (value_string.empty()) { | 
|  | std::string unescaped; | 
|  | if (!ValueAsStringUnsafe(&unescaped)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool nonprintable = false; | 
|  | for (unsigned int i = 0; i < unescaped.length(); ++i) { | 
|  | uint8_t c = static_cast<uint8_t>(unescaped[i]); | 
|  | if (i == 0 && c == '#') { | 
|  | value_string += "\\#"; | 
|  | } else if (i == 0 && c == ' ') { | 
|  | value_string += "\\ "; | 
|  | } else if (i == unescaped.length() - 1 && c == ' ') { | 
|  | value_string += "\\ "; | 
|  | } else if (c == ',' || c == '+' || c == '"' || c == '\\' || c == '<' || | 
|  | c == '>' || c == ';') { | 
|  | value_string += "\\"; | 
|  | value_string += c; | 
|  | } else if (c < 32 || c > 126) { | 
|  | nonprintable = true; | 
|  | value_string += "\\" + bssl::string_util::HexEncode(Span(&c, 1)); | 
|  | } else { | 
|  | value_string += c; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we have non-printable characters in a TeletexString, we hex encode | 
|  | // since we don't handle Teletex control codes. | 
|  | if (nonprintable && value_tag == CBS_ASN1_T61STRING) { | 
|  | value_string = "#" + bssl::string_util::HexEncode(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | *out = type_string + "=" + value_string; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ReadRdn(der::Parser *parser, RelativeDistinguishedName *out) { | 
|  | while (parser->HasMore()) { | 
|  | der::Parser attr_type_and_value; | 
|  | if (!parser->ReadSequence(&attr_type_and_value)) { | 
|  | return false; | 
|  | } | 
|  | // Read the attribute type, which must be an OBJECT IDENTIFIER. | 
|  | der::Input type; | 
|  | if (!attr_type_and_value.ReadTag(CBS_ASN1_OBJECT, &type)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Read the attribute value. | 
|  | CBS_ASN1_TAG tag; | 
|  | der::Input value; | 
|  | if (!attr_type_and_value.ReadTagAndValue(&tag, &value)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // There should be no more elements in the sequence after reading the | 
|  | // attribute type and value. | 
|  | if (attr_type_and_value.HasMore()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | out->push_back(X509NameAttribute(type, tag, value)); | 
|  | } | 
|  |  | 
|  | // RFC 5280 section 4.1.2.4 | 
|  | // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue | 
|  | return out->size() != 0; | 
|  | } | 
|  |  | 
|  | bool ParseName(der::Input name_tlv, RDNSequence *out) { | 
|  | der::Parser name_parser(name_tlv); | 
|  | der::Input name_value; | 
|  | if (!name_parser.ReadTag(CBS_ASN1_SEQUENCE, &name_value)) { | 
|  | return false; | 
|  | } | 
|  | return ParseNameValue(name_value, out); | 
|  | } | 
|  |  | 
|  | bool ParseNameValue(der::Input name_value, RDNSequence *out) { | 
|  | der::Parser rdn_sequence_parser(name_value); | 
|  | while (rdn_sequence_parser.HasMore()) { | 
|  | der::Parser rdn_parser; | 
|  | if (!rdn_sequence_parser.ReadConstructed(CBS_ASN1_SET, &rdn_parser)) { | 
|  | return false; | 
|  | } | 
|  | RelativeDistinguishedName type_and_values; | 
|  | if (!ReadRdn(&rdn_parser, &type_and_values)) { | 
|  | return false; | 
|  | } | 
|  | out->push_back(type_and_values); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ConvertToRFC2253(const RDNSequence &rdn_sequence, std::string *out) { | 
|  | std::string rdns_string; | 
|  | size_t size = rdn_sequence.size(); | 
|  | for (size_t i = 0; i < size; ++i) { | 
|  | RelativeDistinguishedName rdn = rdn_sequence[size - i - 1]; | 
|  | std::string rdn_string; | 
|  | for (const auto &atv : rdn) { | 
|  | if (!rdn_string.empty()) { | 
|  | rdn_string += "+"; | 
|  | } | 
|  | std::string atv_string; | 
|  | if (!atv.AsRFC2253String(&atv_string)) { | 
|  | return false; | 
|  | } | 
|  | rdn_string += atv_string; | 
|  | } | 
|  | if (!rdns_string.empty()) { | 
|  | rdns_string += ","; | 
|  | } | 
|  | rdns_string += rdn_string; | 
|  | } | 
|  |  | 
|  | *out = rdns_string; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace bssl |