blob: 94b82a43715bcd86604c5e6cf3be61a58136a8bc [file] [log] [blame]
// 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"
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::NumberToString(first);
}
out += "." + base::NumberToString(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