|
|
|
// Copyright 2012 The Chromium Authors
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "asn1_util.h"
|
|
|
|
|
|
|
|
#include <optional>
|
|
|
|
#include "input.h"
|
|
|
|
#include "parse_certificate.h"
|
|
|
|
#include "parser.h"
|
|
|
|
|
|
|
|
namespace bssl::asn1 {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Parses input |in| which should point to the beginning of a Certificate, and
|
|
|
|
// sets |*tbs_certificate| ready to parse the Subject. If parsing
|
|
|
|
// fails, this function returns false and |*tbs_certificate| is left in an
|
|
|
|
// undefined state.
|
|
|
|
bool SeekToSubject(der::Input in, der::Parser *tbs_certificate) {
|
|
|
|
// From RFC 5280, section 4.1
|
|
|
|
// Certificate ::= SEQUENCE {
|
|
|
|
// tbsCertificate TBSCertificate,
|
|
|
|
// signatureAlgorithm AlgorithmIdentifier,
|
|
|
|
// signatureValue BIT STRING }
|
|
|
|
|
|
|
|
// TBSCertificate ::= SEQUENCE {
|
|
|
|
// version [0] EXPLICIT Version DEFAULT v1,
|
|
|
|
// serialNumber CertificateSerialNumber,
|
|
|
|
// signature AlgorithmIdentifier,
|
|
|
|
// issuer Name,
|
|
|
|
// validity Validity,
|
|
|
|
// subject Name,
|
|
|
|
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
|
|
|
// ... }
|
|
|
|
|
|
|
|
der::Parser parser(in);
|
|
|
|
der::Parser certificate;
|
|
|
|
if (!parser.ReadSequence(&certificate)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't allow junk after the certificate.
|
|
|
|
if (parser.HasMore()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!certificate.ReadSequence(tbs_certificate)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool unused;
|
|
|
|
if (!tbs_certificate->SkipOptionalTag(
|
|
|
|
der::kTagConstructed | der::kTagContextSpecific | 0, &unused)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// serialNumber
|
|
|
|
if (!tbs_certificate->SkipTag(der::kInteger)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// signature
|
|
|
|
if (!tbs_certificate->SkipTag(der::kSequence)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// issuer
|
|
|
|
if (!tbs_certificate->SkipTag(der::kSequence)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// validity
|
|
|
|
if (!tbs_certificate->SkipTag(der::kSequence)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parses input |in| which should point to the beginning of a Certificate, and
|
|
|
|
// sets |*tbs_certificate| ready to parse the SubjectPublicKeyInfo. If parsing
|
|
|
|
// fails, this function returns false and |*tbs_certificate| is left in an
|
|
|
|
// undefined state.
|
|
|
|
bool SeekToSPKI(der::Input in, der::Parser *tbs_certificate) {
|
|
|
|
return SeekToSubject(in, tbs_certificate) &&
|
|
|
|
// Skip over Subject.
|
|
|
|
tbs_certificate->SkipTag(der::kSequence);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parses input |in| which should point to the beginning of a
|
|
|
|
// Certificate. If parsing fails, this function returns false, with
|
|
|
|
// |*extensions_present| and |*extensions_parser| left in an undefined
|
|
|
|
// state. If parsing succeeds and extensions are present, this function
|
|
|
|
// sets |*extensions_present| to true and sets |*extensions_parser|
|
|
|
|
// ready to parse the Extensions. If extensions are not present, it sets
|
|
|
|
// |*extensions_present| to false and |*extensions_parser| is left in an
|
|
|
|
// undefined state.
|
|
|
|
bool SeekToExtensions(der::Input in, bool *extensions_present,
|
|
|
|
der::Parser *extensions_parser) {
|
|
|
|
bool present;
|
|
|
|
der::Parser tbs_cert_parser;
|
|
|
|
if (!SeekToSPKI(in, &tbs_cert_parser)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// From RFC 5280, section 4.1
|
|
|
|
// TBSCertificate ::= SEQUENCE {
|
|
|
|
// ...
|
|
|
|
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
|
|
|
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
|
|
|
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
|
|
|
|
// extensions [3] EXPLICIT Extensions OPTIONAL }
|
|
|
|
|
|
|
|
// subjectPublicKeyInfo
|
|
|
|
if (!tbs_cert_parser.SkipTag(der::kSequence)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// issuerUniqueID
|
|
|
|
if (!tbs_cert_parser.SkipOptionalTag(der::kTagContextSpecific | 1,
|
|
|
|
&present)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// subjectUniqueID
|
|
|
|
if (!tbs_cert_parser.SkipOptionalTag(der::kTagContextSpecific | 2,
|
|
|
|
&present)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<der::Input> extensions;
|
|
|
|
if (!tbs_cert_parser.ReadOptionalTag(
|
|
|
|
der::kTagConstructed | der::kTagContextSpecific | 3, &extensions)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!extensions) {
|
|
|
|
*extensions_present = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
|
|
|
|
// Extension ::= SEQUENCE {
|
|
|
|
// extnID OBJECT IDENTIFIER,
|
|
|
|
// critical BOOLEAN DEFAULT FALSE,
|
|
|
|
// extnValue OCTET STRING }
|
|
|
|
|
|
|
|
// |extensions| was EXPLICITly tagged, so we still need to remove the
|
|
|
|
// ASN.1 SEQUENCE header.
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a DER-encoded, X.509 certificate in |cert| and find an extension with
|
|
|
|
// the given OID. Returns false on parse error or true if the parse was
|
|
|
|
// successful. |*out_extension_present| will be true iff the extension was
|
|
|
|
// found. In the case where it was found, |*out_extension| will describe the
|
|
|
|
// extension, or is undefined on parse error or if the extension is missing.
|
|
|
|
bool ExtractExtensionWithOID(std::string_view cert, der::Input extension_oid,
|
|
|
|
bool *out_extension_present,
|
|
|
|
ParsedExtension *out_extension) {
|
|
|
|
der::Parser extensions;
|
|
|
|
bool extensions_present;
|
|
|
|
if (!SeekToExtensions(der::Input(cert), &extensions_present, &extensions)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!extensions_present) {
|
|
|
|
*out_extension_present = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (extensions.HasMore()) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
bool ExtractSubjectFromDERCert(std::string_view cert,
|
|
|
|
std::string_view *subject_out) {
|
|
|
|
der::Parser parser;
|
|
|
|
if (!SeekToSubject(der::Input(cert), &parser)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
der::Input subject;
|
|
|
|
if (!parser.ReadRawTLV(&subject)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*subject_out = subject.AsStringView();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExtractSPKIFromDERCert(std::string_view cert, std::string_view *spki_out) {
|
|
|
|
der::Parser parser;
|
|
|
|
if (!SeekToSPKI(der::Input(cert), &parser)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
der::Input spki;
|
|
|
|
if (!parser.ReadRawTLV(&spki)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*spki_out = spki.AsStringView();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExtractSubjectPublicKeyFromSPKI(std::string_view spki,
|
|
|
|
std::string_view *spk_out) {
|
|
|
|
// From RFC 5280, Section 4.1
|
|
|
|
// SubjectPublicKeyInfo ::= SEQUENCE {
|
|
|
|
// algorithm AlgorithmIdentifier,
|
|
|
|
// subjectPublicKey BIT STRING }
|
|
|
|
//
|
|
|
|
// AlgorithmIdentifier ::= SEQUENCE {
|
|
|
|
// algorithm OBJECT IDENTIFIER,
|
|
|
|
// parameters ANY DEFINED BY algorithm OPTIONAL }
|
|
|
|
|
|
|
|
// Step into SubjectPublicKeyInfo sequence.
|
|
|
|
der::Parser parser((der::Input(spki)));
|
|
|
|
der::Parser spki_parser;
|
|
|
|
if (!parser.ReadSequence(&spki_parser)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step over algorithm field (a SEQUENCE).
|
|
|
|
if (!spki_parser.SkipTag(der::kSequence)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract the subjectPublicKey field.
|
|
|
|
der::Input spk;
|
|
|
|
if (!spki_parser.ReadTag(der::kBitString, &spk)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*spk_out = spk.AsStringView();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasCanSignHttpExchangesDraftExtension(std::string_view cert) {
|
|
|
|
// kCanSignHttpExchangesDraftOid is the DER encoding of the OID for
|
|
|
|
// canSignHttpExchangesDraft defined in:
|
|
|
|
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html
|
|
|
|
static const uint8_t kCanSignHttpExchangesDraftOid[] = {
|
|
|
|
0x2B, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x16};
|
|
|
|
|
|
|
|
bool extension_present;
|
|
|
|
ParsedExtension extension;
|
|
|
|
if (!ExtractExtensionWithOID(cert, der::Input(kCanSignHttpExchangesDraftOid),
|
|
|
|
&extension_present, &extension) ||
|
|
|
|
!extension_present) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The extension should have contents NULL.
|
|
|
|
static const uint8_t kNull[] = {0x05, 0x00};
|
|
|
|
return extension.value == der::Input(kNull);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ExtractSignatureAlgorithmsFromDERCert(
|
|
|
|
std::string_view cert, std::string_view *cert_signature_algorithm_sequence,
|
|
|
|
std::string_view *tbs_signature_algorithm_sequence) {
|
|
|
|
// From RFC 5280, section 4.1
|
|
|
|
// Certificate ::= SEQUENCE {
|
|
|
|
// tbsCertificate TBSCertificate,
|
|
|
|
// signatureAlgorithm AlgorithmIdentifier,
|
|
|
|
// signatureValue BIT STRING }
|
|
|
|
|
|
|
|
// TBSCertificate ::= SEQUENCE {
|
|
|
|
// version [0] EXPLICIT Version DEFAULT v1,
|
|
|
|
// serialNumber CertificateSerialNumber,
|
|
|
|
// signature AlgorithmIdentifier,
|
|
|
|
// issuer Name,
|
|
|
|
// validity Validity,
|
|
|
|
// subject Name,
|
|
|
|
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
|
|
|
// ... }
|
|
|
|
|
|
|
|
der::Parser parser((der::Input(cert)));
|
|
|
|
der::Parser certificate;
|
|
|
|
if (!parser.ReadSequence(&certificate)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
der::Parser tbs_certificate;
|
|
|
|
if (!certificate.ReadSequence(&tbs_certificate)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool unused;
|
|
|
|
if (!tbs_certificate.SkipOptionalTag(
|
|
|
|
der::kTagConstructed | der::kTagContextSpecific | 0, &unused)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// serialNumber
|
|
|
|
if (!tbs_certificate.SkipTag(der::kInteger)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// signature
|
|
|
|
der::Input tbs_algorithm;
|
|
|
|
if (!tbs_certificate.ReadRawTLV(&tbs_algorithm)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
der::Input cert_algorithm;
|
|
|
|
if (!certificate.ReadRawTLV(&cert_algorithm)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
*cert_signature_algorithm_sequence = cert_algorithm.AsStringView();
|
|
|
|
*tbs_signature_algorithm_sequence = tbs_algorithm.AsStringView();
|
|
|
|
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();
|
|
|
|
|
|
|
|
ParsedExtension extension;
|
|
|
|
if (!ExtractExtensionWithOID(cert, der::Input(extension_oid),
|
|
|
|
out_extension_present, &extension)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!*out_extension_present) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_extension_critical = extension.critical;
|
|
|
|
*out_contents = extension.value.AsStringView();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace bssl::asn1
|