| // 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/der/input.h" |
| #include "net/der/parse_values.h" |
| #include "net/der/parser.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // 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; |
| } |
| |
| } // namespace |
| |
| ParsedTbsCertificate::ParsedTbsCertificate() {} |
| |
| ParsedTbsCertificate::~ParsedTbsCertificate() {} |
| |
| bool VerifySerialNumber(const der::Input& value) { |
| bool unused_negative; |
| if (!der::IsValidInteger(value, &unused_negative)) |
| return false; |
| |
| // Check if the serial number is too long per RFC 5280. |
| if (value.Length() > 20) |
| 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) { |
| der::Parser parser(certificate_tlv); |
| |
| // Certificate ::= SEQUENCE { |
| der::Parser certificate_parser; |
| if (!parser.ReadSequence(&certificate_parser)) |
| return false; |
| |
| // tbsCertificate TBSCertificate, |
| if (!ReadSequenceTLV(&certificate_parser, out_tbs_certificate_tlv)) |
| return false; |
| |
| // signatureAlgorithm AlgorithmIdentifier, |
| if (!ReadSequenceTLV(&certificate_parser, out_signature_algorithm_tlv)) |
| return false; |
| |
| // signatureValue BIT STRING } |
| if (!certificate_parser.ReadBitString(out_signature_value)) |
| return false; |
| |
| // There isn't an extension point at the end of Certificate. |
| if (certificate_parser.HasMore()) |
| return false; |
| |
| // By definition the input was a single Certificate, so there shouldn't be |
| // unconsumed data. |
| if (parser.HasMore()) |
| 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) { |
| der::Parser parser(tbs_tlv); |
| |
| // Certificate ::= SEQUENCE { |
| der::Parser tbs_parser; |
| if (!parser.ReadSequence(&tbs_parser)) |
| return false; |
| |
| // version [0] EXPLICIT Version DEFAULT v1, |
| der::Input version; |
| bool has_version; |
| if (!tbs_parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &version, |
| &has_version)) { |
| return false; |
| } |
| if (has_version) { |
| if (!ParseVersion(version, &out->version)) |
| return false; |
| if (out->version == CertificateVersion::V1) { |
| // 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)) |
| return false; |
| if (!options.allow_invalid_serial_numbers && |
| !VerifySerialNumber(out->serial_number)) { |
| return false; |
| } |
| |
| // signature AlgorithmIdentifier, |
| if (!ReadSequenceTLV(&tbs_parser, &out->signature_algorithm_tlv)) |
| return false; |
| |
| // issuer Name, |
| if (!ReadSequenceTLV(&tbs_parser, &out->issuer_tlv)) |
| return false; |
| |
| // validity Validity, |
| der::Input validity_tlv; |
| if (!tbs_parser.ReadRawTLV(&validity_tlv)) |
| return false; |
| if (!ParseValidity(validity_tlv, &out->validity_not_before, |
| &out->validity_not_after)) { |
| return false; |
| } |
| |
| // subject Name, |
| if (!ReadSequenceTLV(&tbs_parser, &out->subject_tlv)) |
| return false; |
| |
| // subjectPublicKeyInfo SubjectPublicKeyInfo, |
| if (!ReadSequenceTLV(&tbs_parser, &out->spki_tlv)) |
| 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)) { |
| return false; |
| } |
| if (out->has_issuer_unique_id) { |
| if (!der::ParseBitString(issuer_unique_id, &out->issuer_unique_id)) |
| return false; |
| if (out->version != CertificateVersion::V2 && |
| out->version != CertificateVersion::V3) { |
| 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)) { |
| return false; |
| } |
| if (out->has_subject_unique_id) { |
| if (!der::ParseBitString(subject_unique_id, &out->subject_unique_id)) |
| return false; |
| if (out->version != CertificateVersion::V2 && |
| out->version != CertificateVersion::V3) { |
| 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)) { |
| 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)) |
| return false; |
| if (out->version != CertificateVersion::V3) |
| 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()) |
| 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); |
| } |
| |
| 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) { |
| 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; |
| } |
| |
| } // namespace net |