| // Copyright (c) 2012 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/asn1_util.h" |
| |
| #include "net/der/input.h" |
| #include "net/der/parser.h" |
| |
| namespace net { |
| |
| namespace asn1 { |
| |
| namespace { |
| |
| // 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) { |
| // 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; |
| // subject |
| if (!tbs_certificate->SkipTag(der::kSequence)) |
| return false; |
| return true; |
| } |
| |
| // 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::kTagConstructed | der::kTagContextSpecific | 1, &present)) { |
| return false; |
| } |
| // subjectUniqueID |
| if (!tbs_cert_parser.SkipOptionalTag( |
| der::kTagConstructed | der::kTagContextSpecific | 2, &present)) { |
| return false; |
| } |
| |
| der::Input extensions; |
| if (!tbs_cert_parser.ReadOptionalTag( |
| der::kTagConstructed | der::kTagContextSpecific | 3, &extensions, |
| &present)) { |
| return false; |
| } |
| |
| if (!present) { |
| *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); |
| if (!explicit_extensions_parser.ReadSequence(extensions_parser)) |
| return false; |
| |
| if (explicit_extensions_parser.HasMore()) |
| return false; |
| |
| *extensions_present = true; |
| return true; |
| } |
| |
| } // namespace |
| |
| bool ExtractSPKIFromDERCert(base::StringPiece cert, |
| base::StringPiece* 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.AsStringPiece(); |
| return true; |
| } |
| |
| bool ExtractSubjectPublicKeyFromSPKI(base::StringPiece spki, |
| base::StringPiece* 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.AsStringPiece(); |
| return true; |
| } |
| |
| |
| bool ExtractCRLURLsFromDERCert(base::StringPiece cert, |
| std::vector<base::StringPiece>* urls_out) { |
| urls_out->clear(); |
| std::vector<base::StringPiece> tmp_urls_out; |
| bool present; |
| der::Parser extensions_parser; |
| if (!SeekToExtensions(der::Input(cert), &present, &extensions_parser)) |
| return false; |
| |
| if (!present) |
| return true; |
| |
| while (extensions_parser.HasMore()) { |
| der::Parser extension_parser; |
| if (!extensions_parser.ReadSequence(&extension_parser)) |
| return false; |
| |
| der::Input oid; |
| if (!extension_parser.ReadTag(der::kOid, &oid)) |
| return false; |
| |
| // kCRLDistributionPointsOID is the DER encoding of the OID for the X.509 |
| // CRL Distribution Points extension. |
| static const uint8_t kCRLDistributionPointsOID[] = {0x55, 0x1d, 0x1f}; |
| |
| if (oid != der::Input(kCRLDistributionPointsOID)) |
| continue; |
| |
| // critical |
| if (!extension_parser.SkipOptionalTag(der::kBool, &present)) |
| return false; |
| |
| // extnValue |
| der::Input extension_value; |
| if (!extension_parser.ReadTag(der::kOctetString, &extension_value)) |
| return false; |
| |
| // RFC 5280, section 4.2.1.13. |
| // |
| // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint |
| // |
| // DistributionPoint ::= SEQUENCE { |
| // distributionPoint [0] DistributionPointName OPTIONAL, |
| // reasons [1] ReasonFlags OPTIONAL, |
| // cRLIssuer [2] GeneralNames OPTIONAL } |
| |
| 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; |
| |
| while (distribution_points_parser.HasMore()) { |
| der::Parser distrib_point_parser; |
| if (!distribution_points_parser.ReadSequence(&distrib_point_parser)) |
| return false; |
| |
| der::Input name; |
| if (!distrib_point_parser.ReadOptionalTag( |
| der::kTagContextSpecific | der::kTagConstructed | 0, &name, |
| &present)) { |
| return false; |
| } |
| // If it doesn't contain a name then we skip it. |
| if (!present) |
| continue; |
| |
| if (!distrib_point_parser.SkipOptionalTag(der::kTagContextSpecific | 1, |
| &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 (present) |
| continue; |
| |
| if (!distrib_point_parser.SkipOptionalTag( |
| der::kTagContextSpecific | der::kTagConstructed | 2, &present)) { |
| return false; |
| } |
| // If it contains a alternative issuer, then we skip it. |
| if (present) |
| continue; |
| |
| // DistributionPointName ::= CHOICE { |
| // fullName [0] GeneralNames, |
| // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } |
| der::Input general_names; |
| if (!der::Parser(name).ReadOptionalTag( |
| der::kTagContextSpecific | der::kTagConstructed | 0, |
| &general_names, &present)) { |
| return false; |
| } |
| if (!present) |
| continue; |
| |
| // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName |
| // GeneralName ::= CHOICE { |
| // ... |
| // uniformResourceIdentifier [6] IA5String, |
| // ... } |
| der::Parser general_names_parser(general_names); |
| while (general_names_parser.HasMore()) { |
| der::Input url; |
| if (!general_names_parser.ReadOptionalTag(der::kTagContextSpecific | 6, |
| &url, &present)) { |
| return false; |
| } |
| if (present) { |
| // This does not validate that |url| is a valid IA5String. |
| tmp_urls_out.push_back(url.AsStringPiece()); |
| } else { |
| der::Tag unused_tag; |
| der::Input unused_value; |
| if (!general_names_parser.ReadTagAndValue(&unused_tag, |
| &unused_value)) { |
| return false; |
| } |
| } |
| } |
| } |
| } |
| |
| urls_out->swap(tmp_urls_out); |
| return true; |
| } |
| |
| bool HasTLSFeatureExtension(base::StringPiece cert) { |
| bool present; |
| der::Parser extensions_parser; |
| if (!SeekToExtensions(der::Input(cert), &present, &extensions_parser)) |
| return false; |
| if (!present) |
| return false; |
| |
| while (extensions_parser.HasMore()) { |
| der::Parser extension_parser; |
| if (!extensions_parser.ReadSequence(&extension_parser)) |
| return false; |
| |
| der::Input oid; |
| if (!extension_parser.ReadTag(der::kOid, &oid)) |
| return false; |
| |
| // kTLSFeatureExtensionOID is the DER encoding of the OID for the |
| // X.509 TLS Feature Extension. |
| static const uint8_t kTLSFeatureExtensionOID[] = {0x2B, 0x06, 0x01, 0x05, |
| 0x05, 0x07, 0x01, 0x18}; |
| if (oid == der::Input(kTLSFeatureExtensionOID)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace asn1 |
| |
| } // namespace net |