1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/base/asn1_util.h"
11 bool ParseElement(base::StringPiece
* in
,
13 base::StringPiece
* out
,
14 unsigned *out_header_len
) {
15 const uint8
* data
= reinterpret_cast<const uint8
*>(in
->data());
17 // We don't support kAny and kOptional at the same time.
18 if ((tag_value
& kAny
) && (tag_value
& kOptional
))
21 if (in
->empty() && (tag_value
& kOptional
)) {
25 *out
= base::StringPiece();
32 if (tag_value
!= kAny
&&
33 static_cast<unsigned char>(data
[0]) != (tag_value
& 0xff)) {
34 if (tag_value
& kOptional
) {
38 *out
= base::StringPiece();
45 if ((data
[1] & 0x80) == 0) {
49 len
= static_cast<size_t>(data
[1]) + 2;
52 const unsigned num_bytes
= data
[1] & 0x7f;
53 if (num_bytes
== 0 || num_bytes
> 2)
55 if (in
->size() < 2 + num_bytes
)
60 // the length encoding must be minimal.
67 // the length should have been encoded in short form. This distinguishes
68 // DER from BER encoding.
72 *out_header_len
= 2 + num_bytes
;
79 *out
= base::StringPiece(in
->data(), len
);
80 in
->remove_prefix(len
);
84 bool GetElement(base::StringPiece
* in
,
86 base::StringPiece
* out
) {
88 if (!ParseElement(in
, tag_value
, out
, &header_len
))
91 out
->remove_prefix(header_len
);
95 // SeekToSPKI changes |cert| so that it points to a suffix of the
96 // TBSCertificate where the suffix begins at the start of the ASN.1
97 // SubjectPublicKeyInfo value.
98 static bool SeekToSPKI(base::StringPiece
* cert
) {
99 // From RFC 5280, section 4.1
100 // Certificate ::= SEQUENCE {
101 // tbsCertificate TBSCertificate,
102 // signatureAlgorithm AlgorithmIdentifier,
103 // signatureValue BIT STRING }
105 // TBSCertificate ::= SEQUENCE {
106 // version [0] EXPLICIT Version DEFAULT v1,
107 // serialNumber CertificateSerialNumber,
108 // signature AlgorithmIdentifier,
110 // validity Validity,
112 // subjectPublicKeyInfo SubjectPublicKeyInfo,
114 base::StringPiece certificate
;
115 if (!GetElement(cert
, kSEQUENCE
, &certificate
))
118 // We don't allow junk after the certificate.
122 base::StringPiece tbs_certificate
;
123 if (!GetElement(&certificate
, kSEQUENCE
, &tbs_certificate
))
126 if (!GetElement(&tbs_certificate
,
127 kOptional
| kConstructed
| kContextSpecific
| 0,
133 if (!GetElement(&tbs_certificate
, kINTEGER
, NULL
))
136 if (!GetElement(&tbs_certificate
, kSEQUENCE
, NULL
))
139 if (!GetElement(&tbs_certificate
, kSEQUENCE
, NULL
))
142 if (!GetElement(&tbs_certificate
, kSEQUENCE
, NULL
))
145 if (!GetElement(&tbs_certificate
, kSEQUENCE
, NULL
))
147 *cert
= tbs_certificate
;
151 bool ExtractSPKIFromDERCert(base::StringPiece cert
,
152 base::StringPiece
* spki_out
) {
153 if (!SeekToSPKI(&cert
))
155 if (!ParseElement(&cert
, kSEQUENCE
, spki_out
, NULL
))
160 bool ExtractCRLURLsFromDERCert(base::StringPiece cert
,
161 std::vector
<base::StringPiece
>* urls_out
) {
163 std::vector
<base::StringPiece
> tmp_urls_out
;
165 if (!SeekToSPKI(&cert
))
168 // From RFC 5280, section 4.1
169 // TBSCertificate ::= SEQUENCE {
171 // subjectPublicKeyInfo SubjectPublicKeyInfo,
172 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
173 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
174 // extensions [3] EXPLICIT Extensions OPTIONAL
176 // subjectPublicKeyInfo
177 if (!GetElement(&cert
, kSEQUENCE
, NULL
))
180 if (!GetElement(&cert
, kOptional
| kConstructed
| kContextSpecific
| 1, NULL
))
183 if (!GetElement(&cert
, kOptional
| kConstructed
| kContextSpecific
| 2, NULL
))
186 base::StringPiece extensions_seq
;
187 if (!GetElement(&cert
, kOptional
| kConstructed
| kContextSpecific
| 3,
192 if (extensions_seq
.empty())
195 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
196 // Extension ::= SEQUENCE {
197 // extnID OBJECT IDENTIFIER,
198 // critical BOOLEAN DEFAULT FALSE,
199 // extnValue OCTET STRING
201 // |extensions_seq| was EXPLICITly tagged, so we still need to remove the
202 // ASN.1 SEQUENCE header.
203 base::StringPiece extensions
;
204 if (!GetElement(&extensions_seq
, kSEQUENCE
, &extensions
))
207 while (extensions
.size() > 0) {
208 base::StringPiece extension
;
209 if (!GetElement(&extensions
, kSEQUENCE
, &extension
))
212 base::StringPiece oid
;
213 if (!GetElement(&extension
, kOID
, &oid
))
216 // kCRLDistributionPointsOID is the DER encoding of the OID for the X.509
217 // CRL Distribution Points extension.
218 static const uint8 kCRLDistributionPointsOID
[] = {0x55, 0x1d, 0x1f};
220 if (oid
.size() != sizeof(kCRLDistributionPointsOID
) ||
221 memcmp(oid
.data(), kCRLDistributionPointsOID
, oid
.size()) != 0) {
226 GetElement(&extension
, kBOOLEAN
, NULL
);
229 base::StringPiece extension_value
;
230 if (!GetElement(&extension
, kOCTETSTRING
, &extension_value
))
233 // RFC 5280, section 4.2.1.13.
235 // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
237 // DistributionPoint ::= SEQUENCE {
238 // distributionPoint [0] DistributionPointName OPTIONAL,
239 // reasons [1] ReasonFlags OPTIONAL,
240 // cRLIssuer [2] GeneralNames OPTIONAL }
242 base::StringPiece distribution_points
;
243 if (!GetElement(&extension_value
, kSEQUENCE
, &distribution_points
))
246 while (distribution_points
.size() > 0) {
247 base::StringPiece distrib_point
;
248 if (!GetElement(&distribution_points
, kSEQUENCE
, &distrib_point
))
251 base::StringPiece name
;
252 if (!GetElement(&distrib_point
, kContextSpecific
| kConstructed
| 0,
254 // If it doesn't contain a name then we skip it.
258 if (GetElement(&distrib_point
, kContextSpecific
| 1, NULL
)) {
259 // If it contains a subset of reasons then we skip it. We aren't
260 // interested in subsets of CRLs and the RFC states that there MUST be
261 // a CRL that covers all reasons.
265 if (GetElement(&distrib_point
,
266 kContextSpecific
| kConstructed
| 2, NULL
)) {
267 // If it contains a alternative issuer, then we skip it.
271 // DistributionPointName ::= CHOICE {
272 // fullName [0] GeneralNames,
273 // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
274 base::StringPiece general_names
;
275 if (!GetElement(&name
,
276 kContextSpecific
| kConstructed
| 0, &general_names
)) {
280 // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
281 // GeneralName ::= CHOICE {
283 // uniformResourceIdentifier [6] IA5String,
285 while (general_names
.size() > 0) {
286 base::StringPiece url
;
287 if (GetElement(&general_names
, kContextSpecific
| 6, &url
)) {
288 tmp_urls_out
.push_back(url
);
290 if (!GetElement(&general_names
, kAny
, NULL
))
297 urls_out
->swap(tmp_urls_out
);