mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
411 lines
12 KiB
C++
411 lines
12 KiB
C++
// 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::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
|