|
|
|
// 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 <cassert>
|
|
|
|
|
|
|
|
#include <openssl/bytestring.h>
|
|
|
|
#include <openssl/mem.h>
|
|
|
|
#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.UnsafeData(), oid.Length());
|
|
|
|
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 der::kTeletexString:
|
|
|
|
return der::ParseTeletexStringAsLatin1(value, out);
|
|
|
|
case der::kIA5String:
|
|
|
|
return der::ParseIA5String(value, out);
|
|
|
|
case der::kPrintableString:
|
|
|
|
return der::ParsePrintableString(value, out);
|
|
|
|
case der::kUtf8String:
|
|
|
|
*out = value.AsString();
|
|
|
|
return true;
|
|
|
|
case der::kUniversalString:
|
|
|
|
return der::ParseUniversalString(value, out);
|
|
|
|
case der::kBmpString:
|
|
|
|
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 == 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 der::ParseUniversalString(value, out);
|
|
|
|
case der::kBmpString:
|
|
|
|
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.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 +=
|
|
|
|
"\\" + bssl::string_util::HexEncode(
|
|
|
|
reinterpret_cast<const uint8_t *>(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 = "#" + bssl::string_util::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 bssl
|