#include "net/cert/asn1_util.h"
#include <optional>
#include <string_view>
#include "base/strings/string_view_util.h"
#include "third_party/boringssl/src/pki/input.h"
#include "third_party/boringssl/src/pki/parse_certificate.h"
#include "third_party/boringssl/src/pki/parser.h"
namespace net::asn1 {
namespace {
bool SeekToIssuer(bssl::der::Input in, bssl::der::Parser* tbs_certificate) {
bssl::der::Parser parser(in);
bssl::der::Parser certificate;
if (!parser.ReadSequence(&certificate))
return false;
if (parser.HasMore())
return false;
if (!certificate.ReadSequence(tbs_certificate))
return false;
bool unused;
if (!tbs_certificate->SkipOptionalTag(
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0, &unused)) {
return false;
}
if (!tbs_certificate->SkipTag(CBS_ASN1_INTEGER)) {
return false;
}
if (!tbs_certificate->SkipTag(CBS_ASN1_SEQUENCE)) {
return false;
}
return true;
}
bool SeekToSubject(bssl::der::Input in, bssl::der::Parser* tbs_certificate) {
if (!SeekToIssuer(in, tbs_certificate)) {
return false;
}
if (!tbs_certificate->SkipTag(CBS_ASN1_SEQUENCE)) {
return false;
}
if (!tbs_certificate->SkipTag(CBS_ASN1_SEQUENCE)) {
return false;
}
return true;
}
bool SeekToSPKI(bssl::der::Input in, bssl::der::Parser* tbs_certificate) {
return SeekToSubject(in, tbs_certificate) &&
tbs_certificate->SkipTag(CBS_ASN1_SEQUENCE);
}
bool SeekToExtensions(bssl::der::Input in,
bool* extensions_present,
bssl::der::Parser* extensions_parser) {
bool present;
bssl::der::Parser tbs_cert_parser;
if (!SeekToSPKI(in, &tbs_cert_parser))
return false;
if (!tbs_cert_parser.SkipTag(CBS_ASN1_SEQUENCE)) {
return false;
}
if (!tbs_cert_parser.SkipOptionalTag(CBS_ASN1_CONTEXT_SPECIFIC | 1,
&present)) {
return false;
}
if (!tbs_cert_parser.SkipOptionalTag(CBS_ASN1_CONTEXT_SPECIFIC | 2,
&present)) {
return false;
}
std::optional<bssl::der::Input> extensions;
if (!tbs_cert_parser.ReadOptionalTag(
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3, &extensions)) {
return false;
}
if (!extensions) {
*extensions_present = false;
return true;
}
bssl::der::Parser explicit_extensions_parser(extensions.value());
if (!explicit_extensions_parser.ReadSequence(extensions_parser))
return false;
if (explicit_extensions_parser.HasMore())
return false;
*extensions_present = true;
return true;
}
bool ExtractExtensionWithOID(std::string_view cert,
bssl::der::Input extension_oid,
bool* out_extension_present,
bssl::ParsedExtension* out_extension) {
bssl::der::Parser extensions;
bool extensions_present;
if (!SeekToExtensions(bssl::der::Input(cert), &extensions_present,
&extensions)) {
return false;
}
if (!extensions_present) {
*out_extension_present = false;
return true;
}
while (extensions.HasMore()) {
bssl::der::Input extension_tlv;
if (!extensions.ReadRawTLV(&extension_tlv) ||
!ParseExtension(extension_tlv, out_extension)) {
return false;
}
if (out_extension->oid == extension_oid) {
*out_extension_present = true;
return true;
}
}
*out_extension_present = false;
return true;
}
}
bool ExtractSubjectFromDERCert(std::string_view cert,
std::string_view* subject_out) {
bssl::der::Parser parser;
if (!SeekToSubject(bssl::der::Input(cert), &parser)) {
return false;
}
bssl::der::Input subject;
if (!parser.ReadRawTLV(&subject))
return false;
*subject_out = base::as_string_view(subject);
return true;
}
bool ExtractIssuerAndSubjectFromDERCert(
base::span<const uint8_t> cert,
base::span<const uint8_t>* issuer_out,
base::span<const uint8_t>* subject_out) {
bssl::der::Parser parser;
if (!SeekToIssuer(bssl::der::Input(cert), &parser)) {
return false;
}
bssl::der::Input issuer;
if (!parser.ReadRawTLV(&issuer)) {
return false;
}
*issuer_out = issuer;
if (!parser.SkipTag(CBS_ASN1_SEQUENCE)) {
return false;
}
bssl::der::Input subject;
if (!parser.ReadRawTLV(&subject)) {
return false;
}
*subject_out = subject;
return true;
}
bool ExtractSPKIFromDERCert(std::string_view cert, std::string_view* spki_out) {
bssl::der::Parser parser;
if (!SeekToSPKI(bssl::der::Input(cert), &parser)) {
return false;
}
bssl::der::Input spki;
if (!parser.ReadRawTLV(&spki))
return false;
*spki_out = base::as_string_view(spki);
return true;
}
bool ExtractSubjectPublicKeyFromSPKI(std::string_view spki,
std::string_view* spk_out) {
bssl::der::Parser parser((bssl::der::Input(spki)));
bssl::der::Parser spki_parser;
if (!parser.ReadSequence(&spki_parser))
return false;
if (!spki_parser.SkipTag(CBS_ASN1_SEQUENCE)) {
return false;
}
bssl::der::Input spk;
if (!spki_parser.ReadTag(CBS_ASN1_BITSTRING, &spk)) {
return false;
}
*spk_out = base::as_string_view(spk);
return true;
}
bool HasCanSignHttpExchangesDraftExtension(std::string_view cert) {
static const uint8_t kCanSignHttpExchangesDraftOid[] = {
0x2B, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x16};
bool extension_present;
bssl::ParsedExtension extension;
if (!ExtractExtensionWithOID(cert,
bssl::der::Input(kCanSignHttpExchangesDraftOid),
&extension_present, &extension) ||
!extension_present) {
return false;
}
static const uint8_t kNull[] = {0x05, 0x00};
return extension.value == bssl::der::Input(kNull);
}
bool ExtractSignatureAlgorithmsFromDERCert(
std::string_view cert,
std::string_view* cert_signature_algorithm_sequence,
std::string_view* tbs_signature_algorithm_sequence) {
bssl::der::Parser parser((bssl::der::Input(cert)));
bssl::der::Parser certificate;
if (!parser.ReadSequence(&certificate))
return false;
bssl::der::Parser tbs_certificate;
if (!certificate.ReadSequence(&tbs_certificate))
return false;
bool unused;
if (!tbs_certificate.SkipOptionalTag(
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0, &unused)) {
return false;
}
if (!tbs_certificate.SkipTag(CBS_ASN1_INTEGER)) {
return false;
}
bssl::der::Input tbs_algorithm;
if (!tbs_certificate.ReadRawTLV(&tbs_algorithm))
return false;
bssl::der::Input cert_algorithm;
if (!certificate.ReadRawTLV(&cert_algorithm))
return false;
*cert_signature_algorithm_sequence = base::as_string_view(cert_algorithm);
*tbs_signature_algorithm_sequence = base::as_string_view(tbs_algorithm);
return true;
}
bool ExtractExtensionFromDERCert(std::string_view cert,
std::string_view extension_oid,
bool* out_extension_present,
bool* out_extension_critical,
std::string_view* out_contents) {
*out_extension_present = false;
*out_extension_critical = false;
*out_contents = std::string_view();
bssl::ParsedExtension extension;
if (!ExtractExtensionWithOID(cert, bssl::der::Input(extension_oid),
out_extension_present, &extension)) {
return false;
}
if (!*out_extension_present)
return true;
*out_extension_critical = extension.critical;
*out_contents = base::as_string_view(extension.value);
return true;
}
}