// 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 #include #include #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 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(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