| // Copyright 2016 The Chromium Authors | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     https://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "parse_name.h" | 
 |  | 
 | #include <cassert> | 
 |  | 
 | #include <openssl/bytestring.h> | 
 | #include <openssl/mem.h> | 
 |  | 
 | #include "parse_values.h" | 
 | #include "string_util.h" | 
 |  | 
 | BSSL_NAMESPACE_BEGIN | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | BSSL_NAMESPACE_END |