// Copyright (c) 2011 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/base/asn1_util.h" namespace net { namespace asn1 { bool ParseElement(base::StringPiece* in, unsigned tag_value, base::StringPiece* out, unsigned *out_header_len) { const uint8* data = reinterpret_cast(in->data()); // We don't support kAny and kOptional at the same time. if ((tag_value & kAny) && (tag_value & kOptional)) return false; if (in->empty() && (tag_value & kOptional)) { if (out_header_len) *out_header_len = 0; if (out) *out = base::StringPiece(); return true; } if (in->size() < 2) return false; if (tag_value != kAny && static_cast(data[0]) != (tag_value & 0xff)) { if (tag_value & kOptional) { if (out_header_len) *out_header_len = 0; if (out) *out = base::StringPiece(); return true; } return false; } size_t len = 0; if ((data[1] & 0x80) == 0) { // short form length if (out_header_len) *out_header_len = 2; len = static_cast(data[1]) + 2; } else { // long form length const unsigned num_bytes = data[1] & 0x7f; if (num_bytes == 0 || num_bytes > 2) return false; if (in->size() < 2 + num_bytes) return false; len = data[2]; if (num_bytes == 2) { if (len == 0) { // the length encoding must be minimal. return false; } len <<= 8; len += data[3]; } if (len < 128) { // the length should have been encoded in short form. This distinguishes // DER from BER encoding. return false; } if (out_header_len) *out_header_len = 2 + num_bytes; len += 2 + num_bytes; } if (in->size() < len) return false; if (out) *out = base::StringPiece(in->data(), len); in->remove_prefix(len); return true; } bool GetElement(base::StringPiece* in, unsigned tag_value, base::StringPiece* out) { unsigned header_len; if (!ParseElement(in, tag_value, out, &header_len)) return false; if (out) out->remove_prefix(header_len); return true; } // SeekToSPKI changes |cert| so that it points to a suffix of the // TBSCertificate where the suffix begins at the start of the ASN.1 // SubjectPublicKeyInfo value. static bool SeekToSPKI(base::StringPiece* cert) { // 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, base::StringPiece certificate; if (!GetElement(cert, kSEQUENCE, &certificate)) return false; // We don't allow junk after the certificate. if (!cert->empty()) return false; base::StringPiece tbs_certificate; if (!GetElement(&certificate, kSEQUENCE, &tbs_certificate)) return false; if (!GetElement(&tbs_certificate, kOptional | kConstructed | kContextSpecific | 0, NULL)) { return false; } // serialNumber if (!GetElement(&tbs_certificate, kINTEGER, NULL)) return false; // signature if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) return false; // issuer if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) return false; // validity if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) return false; // subject if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) return false; *cert = tbs_certificate; return true; } bool ExtractSPKIFromDERCert(base::StringPiece cert, base::StringPiece* spki_out) { if (!SeekToSPKI(&cert)) return false; if (!ParseElement(&cert, kSEQUENCE, spki_out, NULL)) return false; return true; } bool ExtractCRLURLsFromDERCert(base::StringPiece cert, std::vector* urls_out) { urls_out->clear(); std::vector tmp_urls_out; if (!SeekToSPKI(&cert)) 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 (!GetElement(&cert, kSEQUENCE, NULL)) return false; // issuerUniqueID if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 1, NULL)) return false; // subjectUniqueID if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 2, NULL)) return false; base::StringPiece extensions_seq; if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 3, &extensions_seq)) { return false; } if (extensions_seq.empty()) return true; // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension // Extension ::= SEQUENCE { // extnID OBJECT IDENTIFIER, // critical BOOLEAN DEFAULT FALSE, // extnValue OCTET STRING // |extensions_seq| was EXPLICITly tagged, so we still need to remove the // ASN.1 SEQUENCE header. base::StringPiece extensions; if (!GetElement(&extensions_seq, kSEQUENCE, &extensions)) return false; while (extensions.size() > 0) { base::StringPiece extension; if (!GetElement(&extensions, kSEQUENCE, &extension)) return false; base::StringPiece oid; if (!GetElement(&extension, kOID, &oid)) return false; // kCRLDistributionPointsOID is the DER encoding of the OID for the X.509 // CRL Distribution Points extension. static const uint8 kCRLDistributionPointsOID[] = {0x55, 0x1d, 0x1f}; if (oid.size() != sizeof(kCRLDistributionPointsOID) || memcmp(oid.data(), kCRLDistributionPointsOID, oid.size()) != 0) { continue; } // critical GetElement(&extension, kBOOLEAN, NULL); // extnValue base::StringPiece extension_value; if (!GetElement(&extension, 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 } base::StringPiece distribution_points; if (!GetElement(&extension_value, kSEQUENCE, &distribution_points)) return false; while (distribution_points.size() > 0) { base::StringPiece distrib_point; if (!GetElement(&distribution_points, kSEQUENCE, &distrib_point)) return false; base::StringPiece name; if (!GetElement(&distrib_point, kContextSpecific | kConstructed | 0, &name)) { // If it doesn't contain a name then we skip it. continue; } if (GetElement(&distrib_point, kContextSpecific | 1, NULL)) { // 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. continue; } if (GetElement(&distrib_point, kContextSpecific | kConstructed | 2, NULL)) { // If it contains a alternative issuer, then we skip it. continue; } // DistributionPointName ::= CHOICE { // fullName [0] GeneralNames, // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } base::StringPiece general_names; if (!GetElement(&name, kContextSpecific | kConstructed | 0, &general_names)) { continue; } // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName // GeneralName ::= CHOICE { // ... // uniformResourceIdentifier [6] IA5String, // ... while (general_names.size() > 0) { base::StringPiece url; if (GetElement(&general_names, kContextSpecific | 6, &url)) { tmp_urls_out.push_back(url); } else { if (!GetElement(&general_names, kAny, NULL)) return false; } } } } urls_out->swap(tmp_urls_out); return true; } } // namespace asn1 } // namespace net