mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 16:26:10 +03:00
980 lines
32 KiB
C++
980 lines
32 KiB
C++
|
// Copyright 2015 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_certificate.h"
|
||
|
|
||
|
#include <utility>
|
||
|
|
||
|
#include "base/strings/string_util.h"
|
||
|
#include "net/cert/internal/cert_error_params.h"
|
||
|
#include "net/cert/internal/cert_errors.h"
|
||
|
#include "net/cert/internal/general_names.h"
|
||
|
#include "net/der/input.h"
|
||
|
#include "net/der/parse_values.h"
|
||
|
#include "net/der/parser.h"
|
||
|
|
||
|
namespace net {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
DEFINE_CERT_ERROR_ID(kCertificateNotSequence,
|
||
|
"Failed parsing Certificate SEQUENCE");
|
||
|
DEFINE_CERT_ERROR_ID(kUnconsumedDataInsideCertificateSequence,
|
||
|
"Unconsumed data inside Certificate SEQUENCE");
|
||
|
DEFINE_CERT_ERROR_ID(kUnconsumedDataAfterCertificateSequence,
|
||
|
"Unconsumed data after Certificate SEQUENCE");
|
||
|
DEFINE_CERT_ERROR_ID(kTbsCertificateNotSequence,
|
||
|
"Couldn't read tbsCertificate as SEQUENCE");
|
||
|
DEFINE_CERT_ERROR_ID(
|
||
|
kSignatureAlgorithmNotSequence,
|
||
|
"Couldn't read Certificate.signatureAlgorithm as SEQUENCE");
|
||
|
DEFINE_CERT_ERROR_ID(kSignatureValueNotBitString,
|
||
|
"Couldn't read Certificate.signatureValue as BIT STRING");
|
||
|
DEFINE_CERT_ERROR_ID(kUnconsumedDataInsideTbsCertificateSequence,
|
||
|
"Unconsumed data inside TBSCertificate");
|
||
|
DEFINE_CERT_ERROR_ID(kTbsNotSequence, "Failed parsing TBSCertificate SEQUENCE");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingVersion, "Failed reading version");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedParsingVersion, "Failed parsing version");
|
||
|
DEFINE_CERT_ERROR_ID(kVersionExplicitlyV1,
|
||
|
"Version explicitly V1 (should be omitted)");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingSerialNumber, "Failed reading serialNumber");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingSignatureValue, "Failed reading signature");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingIssuer, "Failed reading issuer");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingValidity, "Failed reading validity");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedParsingValidity, "Failed parsing validity");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingSubject, "Failed reading subject");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingSpki, "Failed reading subjectPublicKeyInfo");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingIssuerUniqueId,
|
||
|
"Failed reading issuerUniqueId");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedParsingIssuerUniqueId,
|
||
|
"Failed parsing issuerUniqueId");
|
||
|
DEFINE_CERT_ERROR_ID(
|
||
|
kIssuerUniqueIdNotExpected,
|
||
|
"Unexpected issuerUniqueId (must be V2 or V3 certificate)");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingSubjectUniqueId,
|
||
|
"Failed reading subjectUniqueId");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedParsingSubjectUniqueId,
|
||
|
"Failed parsing subjectUniqueId");
|
||
|
DEFINE_CERT_ERROR_ID(
|
||
|
kSubjectUniqueIdNotExpected,
|
||
|
"Unexpected subjectUniqueId (must be V2 or V3 certificate)");
|
||
|
DEFINE_CERT_ERROR_ID(kFailedReadingExtensions,
|
||
|
"Failed reading extensions SEQUENCE");
|
||
|
DEFINE_CERT_ERROR_ID(kUnexpectedExtensions,
|
||
|
"Unexpected extensions (must be V3 certificate)");
|
||
|
DEFINE_CERT_ERROR_ID(kSerialNumberIsNegative, "Serial number is negative");
|
||
|
DEFINE_CERT_ERROR_ID(kSerialNumberIsZero, "Serial number is zero");
|
||
|
DEFINE_CERT_ERROR_ID(kSerialNumberLengthOver20,
|
||
|
"Serial number is longer than 20 octets");
|
||
|
DEFINE_CERT_ERROR_ID(kSerialNumberNotValidInteger,
|
||
|
"Serial number is not a valid INTEGER");
|
||
|
|
||
|
// Returns true if |input| is a SEQUENCE and nothing else.
|
||
|
WARN_UNUSED_RESULT bool IsSequenceTLV(const der::Input& input) {
|
||
|
der::Parser parser(input);
|
||
|
der::Parser unused_sequence_parser;
|
||
|
if (!parser.ReadSequence(&unused_sequence_parser))
|
||
|
return false;
|
||
|
// Should by a single SEQUENCE by definition of the function.
|
||
|
return !parser.HasMore();
|
||
|
}
|
||
|
|
||
|
// Reads a SEQUENCE from |parser| and writes the full tag-length-value into
|
||
|
// |out|. On failure |parser| may or may not have been advanced.
|
||
|
WARN_UNUSED_RESULT bool ReadSequenceTLV(der::Parser* parser, der::Input* out) {
|
||
|
return parser->ReadRawTLV(out) && IsSequenceTLV(*out);
|
||
|
}
|
||
|
|
||
|
// Parses a Version according to RFC 5280:
|
||
|
//
|
||
|
// Version ::= INTEGER { v1(0), v2(1), v3(2) }
|
||
|
//
|
||
|
// No value other that v1, v2, or v3 is allowed (and if given will fail). RFC
|
||
|
// 5280 minimally requires the handling of v3 (and overwhelmingly these are the
|
||
|
// certificate versions in use today):
|
||
|
//
|
||
|
// Implementations SHOULD be prepared to accept any version certificate.
|
||
|
// At a minimum, conforming implementations MUST recognize version 3
|
||
|
// certificates.
|
||
|
WARN_UNUSED_RESULT bool ParseVersion(const der::Input& in,
|
||
|
CertificateVersion* version) {
|
||
|
der::Parser parser(in);
|
||
|
uint64_t version64;
|
||
|
if (!parser.ReadUint64(&version64))
|
||
|
return false;
|
||
|
|
||
|
switch (version64) {
|
||
|
case 0:
|
||
|
*version = CertificateVersion::V1;
|
||
|
break;
|
||
|
case 1:
|
||
|
*version = CertificateVersion::V2;
|
||
|
break;
|
||
|
case 2:
|
||
|
*version = CertificateVersion::V3;
|
||
|
break;
|
||
|
default:
|
||
|
// Don't allow any other version identifier.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// By definition the input to this function was a single INTEGER, so there
|
||
|
// shouldn't be anything else after it.
|
||
|
return !parser.HasMore();
|
||
|
}
|
||
|
|
||
|
// Consumes a "Time" value (as defined by RFC 5280) from |parser|. On success
|
||
|
// writes the result to |*out| and returns true. On failure no guarantees are
|
||
|
// made about the state of |parser|.
|
||
|
//
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// Time ::= CHOICE {
|
||
|
// utcTime UTCTime,
|
||
|
// generalTime GeneralizedTime }
|
||
|
WARN_UNUSED_RESULT bool ReadTime(der::Parser* parser,
|
||
|
der::GeneralizedTime* out) {
|
||
|
der::Input value;
|
||
|
der::Tag tag;
|
||
|
|
||
|
if (!parser->ReadTagAndValue(&tag, &value))
|
||
|
return false;
|
||
|
|
||
|
if (tag == der::kUtcTime)
|
||
|
return der::ParseUTCTime(value, out);
|
||
|
|
||
|
if (tag == der::kGeneralizedTime)
|
||
|
return der::ParseGeneralizedTime(value, out);
|
||
|
|
||
|
// Unrecognized tag.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Parses a DER-encoded "Validity" as specified by RFC 5280. Returns true on
|
||
|
// success and sets the results in |not_before| and |not_after|:
|
||
|
//
|
||
|
// Validity ::= SEQUENCE {
|
||
|
// notBefore Time,
|
||
|
// notAfter Time }
|
||
|
//
|
||
|
// Note that upon success it is NOT guaranteed that |*not_before <= *not_after|.
|
||
|
bool ParseValidity(const der::Input& validity_tlv,
|
||
|
der::GeneralizedTime* not_before,
|
||
|
der::GeneralizedTime* not_after) {
|
||
|
der::Parser parser(validity_tlv);
|
||
|
|
||
|
// Validity ::= SEQUENCE {
|
||
|
der::Parser validity_parser;
|
||
|
if (!parser.ReadSequence(&validity_parser))
|
||
|
return false;
|
||
|
|
||
|
// notBefore Time,
|
||
|
if (!ReadTime(&validity_parser, not_before))
|
||
|
return false;
|
||
|
|
||
|
// notAfter Time }
|
||
|
if (!ReadTime(&validity_parser, not_after))
|
||
|
return false;
|
||
|
|
||
|
// By definition the input was a single Validity sequence, so there shouldn't
|
||
|
// be unconsumed data.
|
||
|
if (parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
// The Validity type does not have an extension point.
|
||
|
if (validity_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
// Note that RFC 5280 doesn't require notBefore to be <=
|
||
|
// notAfter, so that will not be considered a "parsing" error here. Instead it
|
||
|
// will be considered an expired certificate later when testing against the
|
||
|
// current timestamp.
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Returns true if every bit in |bits| is zero (including empty).
|
||
|
WARN_UNUSED_RESULT bool BitStringIsAllZeros(const der::BitString& bits) {
|
||
|
// Note that it is OK to read from the unused bits, since BitString parsing
|
||
|
// guarantees they are all zero.
|
||
|
for (size_t i = 0; i < bits.bytes().Length(); ++i) {
|
||
|
if (bits.bytes().UnsafeData()[i] != 0)
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Parses a DistributionPointName.
|
||
|
//
|
||
|
// Currently this implementation is only concerned with URIs encoded in
|
||
|
// fullName and skips the rest (it does not fully parse the GeneralNames).
|
||
|
//
|
||
|
// URIs found in fullName are appended to |uris|.
|
||
|
//
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// DistributionPointName ::= CHOICE {
|
||
|
// fullName [0] GeneralNames,
|
||
|
// nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
|
||
|
bool ParseDistributionPointName(const der::Input& dp_name,
|
||
|
std::vector<base::StringPiece>* uris) {
|
||
|
bool has_full_name;
|
||
|
der::Input der_full_name;
|
||
|
if (!der::Parser(dp_name).ReadOptionalTag(
|
||
|
der::kTagContextSpecific | der::kTagConstructed | 0, &der_full_name,
|
||
|
&has_full_name)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (!has_full_name) {
|
||
|
// Only process DistributionPoints which provide "fullName".
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// TODO(mattm): surface the CertErrors.
|
||
|
CertErrors errors;
|
||
|
std::unique_ptr<GeneralNames> full_name =
|
||
|
GeneralNames::CreateFromValue(der_full_name, &errors);
|
||
|
if (!full_name)
|
||
|
return false;
|
||
|
|
||
|
// This code is only interested in extracting the URIs from fullName.
|
||
|
*uris = full_name->uniform_resource_identifiers;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// RFC 5280, section 4.2.1.13.
|
||
|
//
|
||
|
// DistributionPoint ::= SEQUENCE {
|
||
|
// distributionPoint [0] DistributionPointName OPTIONAL,
|
||
|
// reasons [1] ReasonFlags OPTIONAL,
|
||
|
// cRLIssuer [2] GeneralNames OPTIONAL }
|
||
|
bool ParseAndAddDistributionPoint(
|
||
|
der::Parser* parser,
|
||
|
std::vector<ParsedDistributionPoint>* distribution_points) {
|
||
|
ParsedDistributionPoint distribution_point;
|
||
|
|
||
|
// DistributionPoint ::= SEQUENCE {
|
||
|
der::Parser distrib_point_parser;
|
||
|
if (!parser->ReadSequence(&distrib_point_parser))
|
||
|
return false;
|
||
|
|
||
|
// distributionPoint [0] DistributionPointName OPTIONAL,
|
||
|
bool distribution_point_present;
|
||
|
der::Input name;
|
||
|
if (!distrib_point_parser.ReadOptionalTag(
|
||
|
der::kTagContextSpecific | der::kTagConstructed | 0, &name,
|
||
|
&distribution_point_present)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!distribution_point_present) {
|
||
|
// Only process DistributionPoints which provide a "distributionPoint".
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// reasons [1] ReasonFlags OPTIONAL,
|
||
|
bool reasons_present;
|
||
|
if (!distrib_point_parser.SkipOptionalTag(der::kTagContextSpecific | 1,
|
||
|
&reasons_present)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// If it contains a subset of reasons then we skip it. We aren't
|
||
|
// interested in subsets of CRLs and the RFC states that there MUST be
|
||
|
// a CRL that covers all reasons.
|
||
|
if (reasons_present) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Extract the URIs from the DistributionPointName.
|
||
|
if (!ParseDistributionPointName(name, &distribution_point.uris))
|
||
|
return false;
|
||
|
|
||
|
// cRLIssuer [2] GeneralNames OPTIONAL }
|
||
|
bool crl_issuer_present;
|
||
|
der::Input crl_issuer;
|
||
|
if (!distrib_point_parser.ReadOptionalTag(
|
||
|
der::kTagContextSpecific | der::kTagConstructed | 2, &crl_issuer,
|
||
|
&crl_issuer_present)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
distribution_point.has_crl_issuer = crl_issuer_present;
|
||
|
// TODO(eroman): Parse "cRLIssuer".
|
||
|
|
||
|
if (distrib_point_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
distribution_points->push_back(std::move(distribution_point));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
ParsedTbsCertificate::ParsedTbsCertificate() = default;
|
||
|
|
||
|
ParsedTbsCertificate::~ParsedTbsCertificate() = default;
|
||
|
|
||
|
bool VerifySerialNumber(const der::Input& value,
|
||
|
bool warnings_only,
|
||
|
CertErrors* errors) {
|
||
|
// If |warnings_only| was set to true, the exact same errors will be logged,
|
||
|
// only they will be logged with a lower severity (warning rather than error).
|
||
|
CertError::Severity error_severity =
|
||
|
warnings_only ? CertError::SEVERITY_WARNING : CertError::SEVERITY_HIGH;
|
||
|
|
||
|
bool negative;
|
||
|
if (!der::IsValidInteger(value, &negative)) {
|
||
|
errors->Add(error_severity, kSerialNumberNotValidInteger, nullptr);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// RFC 5280 section 4.1.2.2:
|
||
|
//
|
||
|
// Note: Non-conforming CAs may issue certificates with serial numbers
|
||
|
// that are negative or zero. Certificate users SHOULD be prepared to
|
||
|
// gracefully handle such certificates.
|
||
|
if (negative)
|
||
|
errors->AddWarning(kSerialNumberIsNegative);
|
||
|
if (value.Length() == 1 && value.UnsafeData()[0] == 0)
|
||
|
errors->AddWarning(kSerialNumberIsZero);
|
||
|
|
||
|
// RFC 5280 section 4.1.2.2:
|
||
|
//
|
||
|
// Certificate users MUST be able to handle serialNumber values up to 20
|
||
|
// octets. Conforming CAs MUST NOT use serialNumber values longer than 20
|
||
|
// octets.
|
||
|
if (value.Length() > 20) {
|
||
|
errors->Add(error_severity, kSerialNumberLengthOver20,
|
||
|
CreateCertErrorParams1SizeT("length", value.Length()));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ParseCertificate(const der::Input& certificate_tlv,
|
||
|
der::Input* out_tbs_certificate_tlv,
|
||
|
der::Input* out_signature_algorithm_tlv,
|
||
|
der::BitString* out_signature_value,
|
||
|
CertErrors* out_errors) {
|
||
|
// |out_errors| is optional. But ensure it is non-null for the remainder of
|
||
|
// this function.
|
||
|
if (!out_errors) {
|
||
|
CertErrors unused_errors;
|
||
|
return ParseCertificate(certificate_tlv, out_tbs_certificate_tlv,
|
||
|
out_signature_algorithm_tlv, out_signature_value,
|
||
|
&unused_errors);
|
||
|
}
|
||
|
|
||
|
der::Parser parser(certificate_tlv);
|
||
|
|
||
|
// Certificate ::= SEQUENCE {
|
||
|
der::Parser certificate_parser;
|
||
|
if (!parser.ReadSequence(&certificate_parser)) {
|
||
|
out_errors->AddError(kCertificateNotSequence);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// tbsCertificate TBSCertificate,
|
||
|
if (!ReadSequenceTLV(&certificate_parser, out_tbs_certificate_tlv)) {
|
||
|
out_errors->AddError(kTbsCertificateNotSequence);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// signatureAlgorithm AlgorithmIdentifier,
|
||
|
if (!ReadSequenceTLV(&certificate_parser, out_signature_algorithm_tlv)) {
|
||
|
out_errors->AddError(kSignatureAlgorithmNotSequence);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// signatureValue BIT STRING }
|
||
|
if (!certificate_parser.ReadBitString(out_signature_value)) {
|
||
|
out_errors->AddError(kSignatureValueNotBitString);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// There isn't an extension point at the end of Certificate.
|
||
|
if (certificate_parser.HasMore()) {
|
||
|
out_errors->AddError(kUnconsumedDataInsideCertificateSequence);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// By definition the input was a single Certificate, so there shouldn't be
|
||
|
// unconsumed data.
|
||
|
if (parser.HasMore()) {
|
||
|
out_errors->AddError(kUnconsumedDataAfterCertificateSequence);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// From RFC 5280 section 4.1:
|
||
|
//
|
||
|
// TBSCertificate ::= SEQUENCE {
|
||
|
// version [0] EXPLICIT Version DEFAULT v1,
|
||
|
// serialNumber CertificateSerialNumber,
|
||
|
// signature AlgorithmIdentifier,
|
||
|
// issuer Name,
|
||
|
// validity Validity,
|
||
|
// subject Name,
|
||
|
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||
|
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
||
|
// -- If present, version MUST be v2 or v3
|
||
|
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
|
||
|
// -- If present, version MUST be v2 or v3
|
||
|
// extensions [3] EXPLICIT Extensions OPTIONAL
|
||
|
// -- If present, version MUST be v3
|
||
|
// }
|
||
|
bool ParseTbsCertificate(const der::Input& tbs_tlv,
|
||
|
const ParseCertificateOptions& options,
|
||
|
ParsedTbsCertificate* out,
|
||
|
CertErrors* errors) {
|
||
|
// The rest of this function assumes that |errors| is non-null.
|
||
|
if (!errors) {
|
||
|
CertErrors unused_errors;
|
||
|
return ParseTbsCertificate(tbs_tlv, options, out, &unused_errors);
|
||
|
}
|
||
|
|
||
|
// TODO(crbug.com/634443): Add useful error information to |errors|.
|
||
|
|
||
|
der::Parser parser(tbs_tlv);
|
||
|
|
||
|
// TBSCertificate ::= SEQUENCE {
|
||
|
der::Parser tbs_parser;
|
||
|
if (!parser.ReadSequence(&tbs_parser)) {
|
||
|
errors->AddError(kTbsNotSequence);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// version [0] EXPLICIT Version DEFAULT v1,
|
||
|
der::Input version;
|
||
|
bool has_version;
|
||
|
if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &version,
|
||
|
&has_version)) {
|
||
|
errors->AddError(kFailedReadingVersion);
|
||
|
return false;
|
||
|
}
|
||
|
if (has_version) {
|
||
|
if (!ParseVersion(version, &out->version)) {
|
||
|
errors->AddError(kFailedParsingVersion);
|
||
|
return false;
|
||
|
}
|
||
|
if (out->version == CertificateVersion::V1) {
|
||
|
errors->AddError(kVersionExplicitlyV1);
|
||
|
// The correct way to specify v1 is to omit the version field since v1 is
|
||
|
// the DEFAULT.
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
out->version = CertificateVersion::V1;
|
||
|
}
|
||
|
|
||
|
// serialNumber CertificateSerialNumber,
|
||
|
if (!tbs_parser.ReadTag(der::kInteger, &out->serial_number)) {
|
||
|
errors->AddError(kFailedReadingSerialNumber);
|
||
|
return false;
|
||
|
}
|
||
|
if (!VerifySerialNumber(out->serial_number,
|
||
|
options.allow_invalid_serial_numbers, errors)) {
|
||
|
// Invalid serial numbers are only considered fatal failures if
|
||
|
// |!allow_invalid_serial_numbers|.
|
||
|
if (!options.allow_invalid_serial_numbers)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// signature AlgorithmIdentifier,
|
||
|
if (!ReadSequenceTLV(&tbs_parser, &out->signature_algorithm_tlv)) {
|
||
|
errors->AddError(kFailedReadingSignatureValue);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// issuer Name,
|
||
|
if (!ReadSequenceTLV(&tbs_parser, &out->issuer_tlv)) {
|
||
|
errors->AddError(kFailedReadingIssuer);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// validity Validity,
|
||
|
der::Input validity_tlv;
|
||
|
if (!tbs_parser.ReadRawTLV(&validity_tlv)) {
|
||
|
errors->AddError(kFailedReadingValidity);
|
||
|
return false;
|
||
|
}
|
||
|
if (!ParseValidity(validity_tlv, &out->validity_not_before,
|
||
|
&out->validity_not_after)) {
|
||
|
errors->AddError(kFailedParsingValidity);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// subject Name,
|
||
|
if (!ReadSequenceTLV(&tbs_parser, &out->subject_tlv)) {
|
||
|
errors->AddError(kFailedReadingSubject);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||
|
if (!ReadSequenceTLV(&tbs_parser, &out->spki_tlv)) {
|
||
|
errors->AddError(kFailedReadingSpki);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
||
|
// -- If present, version MUST be v2 or v3
|
||
|
der::Input issuer_unique_id;
|
||
|
if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1),
|
||
|
&issuer_unique_id,
|
||
|
&out->has_issuer_unique_id)) {
|
||
|
errors->AddError(kFailedReadingIssuerUniqueId);
|
||
|
return false;
|
||
|
}
|
||
|
if (out->has_issuer_unique_id) {
|
||
|
if (!der::ParseBitString(issuer_unique_id, &out->issuer_unique_id)) {
|
||
|
errors->AddError(kFailedParsingIssuerUniqueId);
|
||
|
return false;
|
||
|
}
|
||
|
if (out->version != CertificateVersion::V2 &&
|
||
|
out->version != CertificateVersion::V3) {
|
||
|
errors->AddError(kIssuerUniqueIdNotExpected);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
|
||
|
// -- If present, version MUST be v2 or v3
|
||
|
der::Input subject_unique_id;
|
||
|
if (!tbs_parser.ReadOptionalTag(der::ContextSpecificPrimitive(2),
|
||
|
&subject_unique_id,
|
||
|
&out->has_subject_unique_id)) {
|
||
|
errors->AddError(kFailedReadingSubjectUniqueId);
|
||
|
return false;
|
||
|
}
|
||
|
if (out->has_subject_unique_id) {
|
||
|
if (!der::ParseBitString(subject_unique_id, &out->subject_unique_id)) {
|
||
|
errors->AddError(kFailedParsingSubjectUniqueId);
|
||
|
return false;
|
||
|
}
|
||
|
if (out->version != CertificateVersion::V2 &&
|
||
|
out->version != CertificateVersion::V3) {
|
||
|
errors->AddError(kSubjectUniqueIdNotExpected);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// extensions [3] EXPLICIT Extensions OPTIONAL
|
||
|
// -- If present, version MUST be v3
|
||
|
if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(3),
|
||
|
&out->extensions_tlv, &out->has_extensions)) {
|
||
|
errors->AddError(kFailedReadingExtensions);
|
||
|
return false;
|
||
|
}
|
||
|
if (out->has_extensions) {
|
||
|
// extensions_tlv must be a single element. Also check that it is a
|
||
|
// SEQUENCE.
|
||
|
if (!IsSequenceTLV(out->extensions_tlv)) {
|
||
|
errors->AddError(kFailedReadingExtensions);
|
||
|
return false;
|
||
|
}
|
||
|
if (out->version != CertificateVersion::V3) {
|
||
|
errors->AddError(kUnexpectedExtensions);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Note that there IS an extension point at the end of TBSCertificate
|
||
|
// (according to RFC 5912), so from that interpretation, unconsumed data would
|
||
|
// be allowed in |tbs_parser|.
|
||
|
//
|
||
|
// However because only v1, v2, and v3 certificates are supported by the
|
||
|
// parsing, there shouldn't be any subsequent data in those versions, so
|
||
|
// reject.
|
||
|
if (tbs_parser.HasMore()) {
|
||
|
errors->AddError(kUnconsumedDataInsideTbsCertificateSequence);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// By definition the input was a single TBSCertificate, so there shouldn't be
|
||
|
// unconsumed data.
|
||
|
if (parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// Extension ::= SEQUENCE {
|
||
|
// extnID OBJECT IDENTIFIER,
|
||
|
// critical BOOLEAN DEFAULT FALSE,
|
||
|
// extnValue OCTET STRING
|
||
|
// -- contains the DER encoding of an ASN.1 value
|
||
|
// -- corresponding to the extension type identified
|
||
|
// -- by extnID
|
||
|
// }
|
||
|
bool ParseExtension(const der::Input& extension_tlv, ParsedExtension* out) {
|
||
|
der::Parser parser(extension_tlv);
|
||
|
|
||
|
// Extension ::= SEQUENCE {
|
||
|
der::Parser extension_parser;
|
||
|
if (!parser.ReadSequence(&extension_parser))
|
||
|
return false;
|
||
|
|
||
|
// extnID OBJECT IDENTIFIER,
|
||
|
if (!extension_parser.ReadTag(der::kOid, &out->oid))
|
||
|
return false;
|
||
|
|
||
|
// critical BOOLEAN DEFAULT FALSE,
|
||
|
out->critical = false;
|
||
|
bool has_critical;
|
||
|
der::Input critical;
|
||
|
if (!extension_parser.ReadOptionalTag(der::kBool, &critical, &has_critical))
|
||
|
return false;
|
||
|
if (has_critical) {
|
||
|
if (!der::ParseBool(critical, &out->critical))
|
||
|
return false;
|
||
|
if (!out->critical)
|
||
|
return false; // DER-encoding requires DEFAULT values be omitted.
|
||
|
}
|
||
|
|
||
|
// extnValue OCTET STRING
|
||
|
if (!extension_parser.ReadTag(der::kOctetString, &out->value))
|
||
|
return false;
|
||
|
|
||
|
// The Extension type does not have an extension point (everything goes in
|
||
|
// extnValue).
|
||
|
if (extension_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
// By definition the input was a single Extension sequence, so there shouldn't
|
||
|
// be unconsumed data.
|
||
|
if (parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
der::Input KeyUsageOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.15
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x0f};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input SubjectAltNameOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.17
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x11};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input BasicConstraintsOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.19
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x13};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input NameConstraintsOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.30
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x1e};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input CertificatePoliciesOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.32
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x20};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input PolicyConstraintsOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.36
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x24};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input ExtKeyUsageOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.37
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x25};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input AuthorityInfoAccessOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
|
||
|
//
|
||
|
// In dotted notation: 1.3.6.1.5.5.7.1.1
|
||
|
static const uint8_t oid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input AdCaIssuersOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
|
||
|
//
|
||
|
// In dotted notation: 1.3.6.1.5.5.7.48.2
|
||
|
static const uint8_t oid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input AdOcspOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
|
||
|
//
|
||
|
// In dotted notation: 1.3.6.1.5.5.7.48.1
|
||
|
static const uint8_t oid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
der::Input CrlDistributionPointsOid() {
|
||
|
// From RFC 5280:
|
||
|
//
|
||
|
// id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
|
||
|
//
|
||
|
// In dotted notation: 2.5.29.31
|
||
|
static const uint8_t oid[] = {0x55, 0x1d, 0x1f};
|
||
|
return der::Input(oid);
|
||
|
}
|
||
|
|
||
|
NET_EXPORT bool ParseExtensions(
|
||
|
const der::Input& extensions_tlv,
|
||
|
std::map<der::Input, ParsedExtension>* extensions) {
|
||
|
der::Parser parser(extensions_tlv);
|
||
|
|
||
|
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
|
||
|
der::Parser extensions_parser;
|
||
|
if (!parser.ReadSequence(&extensions_parser))
|
||
|
return false;
|
||
|
|
||
|
// The Extensions SEQUENCE must contains at least 1 element (otherwise it
|
||
|
// should have been omitted).
|
||
|
if (!extensions_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
extensions->clear();
|
||
|
|
||
|
while (extensions_parser.HasMore()) {
|
||
|
ParsedExtension extension;
|
||
|
|
||
|
der::Input extension_tlv;
|
||
|
if (!extensions_parser.ReadRawTLV(&extension_tlv))
|
||
|
return false;
|
||
|
|
||
|
if (!ParseExtension(extension_tlv, &extension))
|
||
|
return false;
|
||
|
|
||
|
bool is_duplicate =
|
||
|
!extensions->insert(std::make_pair(extension.oid, extension)).second;
|
||
|
|
||
|
// RFC 5280 says that an extension should not appear more than once.
|
||
|
if (is_duplicate)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// By definition the input was a single Extensions sequence, so there
|
||
|
// shouldn't be unconsumed data.
|
||
|
if (parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
NET_EXPORT bool ConsumeExtension(
|
||
|
const der::Input& oid,
|
||
|
std::map<der::Input, ParsedExtension>* unconsumed_extensions,
|
||
|
ParsedExtension* extension) {
|
||
|
auto it = unconsumed_extensions->find(oid);
|
||
|
if (it == unconsumed_extensions->end())
|
||
|
return false;
|
||
|
|
||
|
*extension = it->second;
|
||
|
unconsumed_extensions->erase(it);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ParseBasicConstraints(const der::Input& basic_constraints_tlv,
|
||
|
ParsedBasicConstraints* out) {
|
||
|
der::Parser parser(basic_constraints_tlv);
|
||
|
|
||
|
// BasicConstraints ::= SEQUENCE {
|
||
|
der::Parser sequence_parser;
|
||
|
if (!parser.ReadSequence(&sequence_parser))
|
||
|
return false;
|
||
|
|
||
|
// cA BOOLEAN DEFAULT FALSE,
|
||
|
out->is_ca = false;
|
||
|
bool has_ca;
|
||
|
der::Input ca;
|
||
|
if (!sequence_parser.ReadOptionalTag(der::kBool, &ca, &has_ca))
|
||
|
return false;
|
||
|
if (has_ca) {
|
||
|
if (!der::ParseBool(ca, &out->is_ca))
|
||
|
return false;
|
||
|
// TODO(eroman): Should reject if CA was set to false, since
|
||
|
// DER-encoding requires DEFAULT values be omitted. In
|
||
|
// practice however there are a lot of certificates that use
|
||
|
// the broken encoding.
|
||
|
}
|
||
|
|
||
|
// pathLenConstraint INTEGER (0..MAX) OPTIONAL }
|
||
|
der::Input encoded_path_len;
|
||
|
if (!sequence_parser.ReadOptionalTag(der::kInteger, &encoded_path_len,
|
||
|
&out->has_path_len)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (out->has_path_len) {
|
||
|
// TODO(eroman): Surface reason for failure if length was longer than uint8.
|
||
|
if (!der::ParseUint8(encoded_path_len, &out->path_len))
|
||
|
return false;
|
||
|
} else {
|
||
|
// Default initialize to 0 as a precaution.
|
||
|
out->path_len = 0;
|
||
|
}
|
||
|
|
||
|
// There shouldn't be any unconsumed data in the extension.
|
||
|
if (sequence_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
// By definition the input was a single BasicConstraints sequence, so there
|
||
|
// shouldn't be unconsumed data.
|
||
|
if (parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ParseKeyUsage(const der::Input& key_usage_tlv, der::BitString* key_usage) {
|
||
|
der::Parser parser(key_usage_tlv);
|
||
|
if (!parser.ReadBitString(key_usage))
|
||
|
return false;
|
||
|
|
||
|
// By definition the input was a single BIT STRING.
|
||
|
if (parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
// RFC 5280 section 4.2.1.3:
|
||
|
//
|
||
|
// When the keyUsage extension appears in a certificate, at least
|
||
|
// one of the bits MUST be set to 1.
|
||
|
if (BitStringIsAllZeros(*key_usage))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ParseAuthorityInfoAccess(
|
||
|
const der::Input& authority_info_access_tlv,
|
||
|
std::vector<base::StringPiece>* out_ca_issuers_uris,
|
||
|
std::vector<base::StringPiece>* out_ocsp_uris) {
|
||
|
der::Parser parser(authority_info_access_tlv);
|
||
|
|
||
|
out_ca_issuers_uris->clear();
|
||
|
out_ocsp_uris->clear();
|
||
|
|
||
|
// AuthorityInfoAccessSyntax ::=
|
||
|
// SEQUENCE SIZE (1..MAX) OF AccessDescription
|
||
|
der::Parser sequence_parser;
|
||
|
if (!parser.ReadSequence(&sequence_parser))
|
||
|
return false;
|
||
|
if (!sequence_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
while (sequence_parser.HasMore()) {
|
||
|
// AccessDescription ::= SEQUENCE {
|
||
|
der::Parser access_description_sequence_parser;
|
||
|
if (!sequence_parser.ReadSequence(&access_description_sequence_parser))
|
||
|
return false;
|
||
|
|
||
|
// accessMethod OBJECT IDENTIFIER,
|
||
|
der::Input access_method_oid;
|
||
|
if (!access_description_sequence_parser.ReadTag(der::kOid,
|
||
|
&access_method_oid))
|
||
|
return false;
|
||
|
|
||
|
// accessLocation GeneralName }
|
||
|
der::Tag access_location_tag;
|
||
|
der::Input access_location_value;
|
||
|
if (!access_description_sequence_parser.ReadTagAndValue(
|
||
|
&access_location_tag, &access_location_value))
|
||
|
return false;
|
||
|
|
||
|
// GeneralName ::= CHOICE {
|
||
|
if (access_location_tag == der::ContextSpecificPrimitive(6)) {
|
||
|
// uniformResourceIdentifier [6] IA5String,
|
||
|
base::StringPiece uri = access_location_value.AsStringPiece();
|
||
|
if (!base::IsStringASCII(uri))
|
||
|
return false;
|
||
|
|
||
|
if (access_method_oid == AdCaIssuersOid())
|
||
|
out_ca_issuers_uris->push_back(uri);
|
||
|
else if (access_method_oid == AdOcspOid())
|
||
|
out_ocsp_uris->push_back(uri);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ParsedDistributionPoint::ParsedDistributionPoint() = default;
|
||
|
ParsedDistributionPoint::ParsedDistributionPoint(
|
||
|
ParsedDistributionPoint&& other) = default;
|
||
|
ParsedDistributionPoint::~ParsedDistributionPoint() = default;
|
||
|
|
||
|
bool ParseCrlDistributionPoints(
|
||
|
const der::Input& extension_value,
|
||
|
std::vector<ParsedDistributionPoint>* distribution_points) {
|
||
|
distribution_points->clear();
|
||
|
|
||
|
// RFC 5280, section 4.2.1.13.
|
||
|
//
|
||
|
// CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
|
||
|
der::Parser extension_value_parser(extension_value);
|
||
|
der::Parser distribution_points_parser;
|
||
|
if (!extension_value_parser.ReadSequence(&distribution_points_parser))
|
||
|
return false;
|
||
|
if (extension_value_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
// Sequence must have a minimum of 1 item.
|
||
|
if (!distribution_points_parser.HasMore())
|
||
|
return false;
|
||
|
|
||
|
while (distribution_points_parser.HasMore()) {
|
||
|
if (!ParseAndAddDistributionPoint(&distribution_points_parser,
|
||
|
distribution_points))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace net
|