| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/cert/internal/parse_name.h" |
| |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversion_utils.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/sys_byteorder.h" |
| #include "base/third_party/icu/icu_utf.h" |
| |
| #include "starboard/client_porting/poem/string_poem.h" |
| #include "starboard/memory.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // Converts a BMPString value in Input |in| to UTF-8. |
| // |
| // If the conversion is successful, returns true and stores the result in |
| // |out|. Otherwise it returns false and leaves |out| unmodified. |
| bool ConvertBmpStringValue(const der::Input& in, std::string* out) { |
| if (in.Length() % 2 != 0) |
| return false; |
| |
| base::string16 in_16bit; |
| if (in.Length()) { |
| memcpy(base::WriteInto(&in_16bit, in.Length() / 2 + 1), |
| in.UnsafeData(), in.Length()); |
| } |
| for (base::char16& c : in_16bit) { |
| // BMPString is UCS-2 in big-endian order. |
| c = base::NetToHost16(c); |
| |
| // BMPString only supports codepoints in the Basic Multilingual Plane; |
| // surrogates are not allowed. |
| if (CBU_IS_SURROGATE(c)) |
| return false; |
| } |
| return base::UTF16ToUTF8(in_16bit.data(), in_16bit.size(), out); |
| } |
| |
| // Converts a UniversalString value in Input |in| to UTF-8. |
| // |
| // If the conversion is successful, returns true and stores the result in |
| // |out|. Otherwise it returns false and leaves |out| unmodified. |
| bool ConvertUniversalStringValue(const der::Input& in, std::string* out) { |
| if (in.Length() % 4 != 0) |
| return false; |
| |
| std::vector<uint32_t> in_32bit(in.Length() / 4); |
| if (in.Length()) |
| memcpy(in_32bit.data(), in.UnsafeData(), in.Length()); |
| for (const uint32_t c : in_32bit) { |
| // UniversalString is UCS-4 in big-endian order. |
| uint32_t codepoint = base::NetToHost32(c); |
| if (!CBU_IS_UNICODE_CHAR(codepoint)) |
| return false; |
| |
| base::WriteUnicodeCharacter(codepoint, out); |
| } |
| return true; |
| } |
| |
| std::string OidToString(const uint8_t* data, size_t len) { |
| std::string out; |
| size_t index = 0; |
| while (index < len) { |
| uint64_t value = 0; |
| while ((data[index] & 0x80) == 0x80 && index < len) { |
| value = value << 7 | (data[index] & 0x7F); |
| index += 1; |
| } |
| if (index >= len) |
| return std::string(); |
| value = value << 7 | (data[index] & 0x7F); |
| index += 1; |
| |
| if (out.empty()) { |
| uint8_t first = 0; |
| if (value < 40) { |
| first = 0; |
| } else if (value < 80) { |
| first = 1; |
| value -= 40; |
| } else { |
| first = 2; |
| value -= 80; |
| } |
| out = base::UintToString(first); |
| } |
| out += "." + base::UintToString(value); |
| } |
| |
| return out; |
| } |
| |
| } // namespace |
| |
| der::Input TypeCommonNameOid() { |
| // id-at-commonName: 2.5.4.3 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x03}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeSurnameOid() { |
| // id-at-surname: 2.5.4.4 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x04}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeSerialNumberOid() { |
| // id-at-serialNumber: 2.5.4.5 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x05}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeCountryNameOid() { |
| // id-at-countryName: 2.5.4.6 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x06}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeLocalityNameOid() { |
| // id-at-localityName: 2.5.4.7 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x07}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeStateOrProvinceNameOid() { |
| // id-at-stateOrProvinceName: 2.5.4.8 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x08}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeStreetAddressOid() { |
| // street (streetAddress): 2.5.4.9 (RFC 4519) |
| static const uint8_t oid[] = {0x55, 0x04, 0x09}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeOrganizationNameOid() { |
| // id-at-organizationName: 2.5.4.10 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x0a}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeOrganizationUnitNameOid() { |
| // id-at-organizationalUnitName: 2.5.4.11 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x0b}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeTitleOid() { |
| // id-at-title: 2.5.4.12 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x0c}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeNameOid() { |
| // id-at-name: 2.5.4.41 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x29}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeGivenNameOid() { |
| // id-at-givenName: 2.5.4.42 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x2a}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeInitialsOid() { |
| // id-at-initials: 2.5.4.43 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x2b}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeGenerationQualifierOid() { |
| // id-at-generationQualifier: 2.5.4.44 (RFC 5280) |
| static const uint8_t oid[] = {0x55, 0x04, 0x2c}; |
| return der::Input(oid); |
| } |
| |
| der::Input TypeDomainComponentOid() { |
| // dc (domainComponent): 0.9.2342.19200300.100.1.25 (RFC 4519) |
| static const uint8_t oid[] = {0x09, 0x92, 0x26, 0x89, 0x93, |
| 0xF2, 0x2C, 0x64, 0x01, 0x19}; |
| return der::Input(oid); |
| } |
| |
| bool X509NameAttribute::ValueAsString(std::string* out) const { |
| switch (value_tag) { |
| case der::kTeletexString: { |
| // Convert from Latin-1 to UTF-8. |
| size_t utf8_length = value.Length(); |
| for (size_t i = 0; i < value.Length(); i++) { |
| if (value.UnsafeData()[i] > 0x7f) |
| utf8_length++; |
| } |
| out->reserve(utf8_length); |
| for (size_t i = 0; i < value.Length(); i++) { |
| uint8_t u = value.UnsafeData()[i]; |
| if (u <= 0x7f) { |
| out->push_back(u); |
| } else { |
| out->push_back(0xc0 | (u >> 6)); |
| out->push_back(0x80 | (u & 0x3f)); |
| } |
| } |
| DCHECK_EQ(utf8_length, out->size()); |
| return true; |
| } |
| case der::kIA5String: |
| for (char c : value.AsStringPiece()) { |
| if (static_cast<uint8_t>(c) > 127) |
| return false; |
| } |
| *out = value.AsString(); |
| return true; |
| case der::kPrintableString: |
| for (char c : value.AsStringPiece()) { |
| if (!(base::IsAsciiAlpha(c) || c == ' ' || (c >= '\'' && c <= ':') || |
| c == '=' || c == '?')) { |
| return false; |
| } |
| } |
| *out = value.AsString(); |
| return true; |
| case der::kUtf8String: |
| *out = value.AsString(); |
| return true; |
| case der::kUniversalString: |
| return ConvertUniversalStringValue(value, out); |
| case der::kBmpString: |
| return ConvertBmpStringValue(value, out); |
| default: |
| return false; |
| } |
| } |
| |
| bool X509NameAttribute::ValueAsStringWithUnsafeOptions( |
| PrintableStringHandling printable_string_handling, |
| std::string* out) const { |
| if (printable_string_handling == PrintableStringHandling::kAsUTF8Hack && |
| value_tag == der::kPrintableString) { |
| *out = value.AsString(); |
| return true; |
| } |
| return ValueAsString(out); |
| } |
| |
| bool X509NameAttribute::ValueAsStringUnsafe(std::string* out) const { |
| switch (value_tag) { |
| case der::kIA5String: |
| case der::kPrintableString: |
| case der::kTeletexString: |
| case der::kUtf8String: |
| *out = value.AsString(); |
| return true; |
| case der::kUniversalString: |
| return ConvertUniversalStringValue(value, out); |
| case der::kBmpString: |
| return ConvertBmpStringValue(value, out); |
| default: |
| 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 == TypeCommonNameOid()) { |
| type_string = "CN"; |
| } else if (type == TypeSurnameOid()) { |
| type_string = "SN"; |
| } else if (type == TypeCountryNameOid()) { |
| type_string = "C"; |
| } else if (type == TypeLocalityNameOid()) { |
| type_string = "L"; |
| } else if (type == TypeStateOrProvinceNameOid()) { |
| type_string = "ST"; |
| } else if (type == TypeOrganizationNameOid()) { |
| type_string = "O"; |
| } else if (type == TypeOrganizationUnitNameOid()) { |
| type_string = "OU"; |
| } else if (type == TypeGivenNameOid()) { |
| type_string = "GN"; |
| } else { |
| type_string = OidToString(type.UnsafeData(), type.Length()); |
| if (type_string.empty()) |
| return false; |
| value_string = "#" + base::HexEncode(value.UnsafeData(), value.Length()); |
| } |
| |
| if (value_string.empty()) { |
| std::string unescaped; |
| if (!ValueAsStringUnsafe(&unescaped)) |
| return false; |
| |
| bool nonprintable = false; |
| for (unsigned int i = 0; i < unescaped.length(); ++i) { |
| unsigned char c = static_cast<unsigned char>(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; |
| std::string h; |
| h += c; |
| value_string += "\\" + base::HexEncode(h.data(), h.length()); |
| } 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 == der::kTeletexString) |
| value_string = "#" + base::HexEncode(value.UnsafeData(), value.Length()); |
| } |
| |
| *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(der::kOid, &type)) |
| return false; |
| |
| // Read the attribute value. |
| der::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(const der::Input& name_tlv, RDNSequence* out) { |
| der::Parser name_parser(name_tlv); |
| der::Input name_value; |
| if (!name_parser.ReadTag(der::kSequence, &name_value)) |
| return false; |
| return ParseNameValue(name_value, out); |
| } |
| |
| bool ParseNameValue(const 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(der::kSet, &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 net |