1 // Copyright (c) 2012 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/cert/asn1_util.h"
11 bool ParseElement(base::StringPiece
* in
,
13 base::StringPiece
* out
,
14 unsigned *out_header_len
) {
15 const uint8_t* data
= reinterpret_cast<const uint8_t*>(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 ExtractSubjectPublicKeyFromSPKI(base::StringPiece spki
,
161 base::StringPiece
* spk_out
) {
162 // From RFC 5280, Section 4.1
163 // SubjectPublicKeyInfo ::= SEQUENCE {
164 // algorithm AlgorithmIdentifier,
165 // subjectPublicKey BIT STRING }
167 // AlgorithmIdentifier ::= SEQUENCE {
168 // algorithm OBJECT IDENTIFIER,
169 // parameters ANY DEFINED BY algorithm OPTIONAL }
171 // Step into SubjectPublicKeyInfo sequence.
172 base::StringPiece spki_contents
;
173 if (!asn1::GetElement(&spki
, asn1::kSEQUENCE
, &spki_contents
))
176 // Step over algorithm field (a SEQUENCE).
177 base::StringPiece algorithm
;
178 if (!asn1::GetElement(&spki_contents
, asn1::kSEQUENCE
, &algorithm
))
181 // Extract the subjectPublicKey field.
182 if (!asn1::GetElement(&spki_contents
, asn1::kBITSTRING
, spk_out
))
188 bool ExtractCRLURLsFromDERCert(base::StringPiece cert
,
189 std::vector
<base::StringPiece
>* urls_out
) {
191 std::vector
<base::StringPiece
> tmp_urls_out
;
193 if (!SeekToSPKI(&cert
))
196 // From RFC 5280, section 4.1
197 // TBSCertificate ::= SEQUENCE {
199 // subjectPublicKeyInfo SubjectPublicKeyInfo,
200 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
201 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
202 // extensions [3] EXPLICIT Extensions OPTIONAL
204 // subjectPublicKeyInfo
205 if (!GetElement(&cert
, kSEQUENCE
, NULL
))
208 if (!GetElement(&cert
, kOptional
| kConstructed
| kContextSpecific
| 1, NULL
))
211 if (!GetElement(&cert
, kOptional
| kConstructed
| kContextSpecific
| 2, NULL
))
214 base::StringPiece extensions_seq
;
215 if (!GetElement(&cert
, kOptional
| kConstructed
| kContextSpecific
| 3,
220 if (extensions_seq
.empty())
223 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
224 // Extension ::= SEQUENCE {
225 // extnID OBJECT IDENTIFIER,
226 // critical BOOLEAN DEFAULT FALSE,
227 // extnValue OCTET STRING
229 // |extensions_seq| was EXPLICITly tagged, so we still need to remove the
230 // ASN.1 SEQUENCE header.
231 base::StringPiece extensions
;
232 if (!GetElement(&extensions_seq
, kSEQUENCE
, &extensions
))
235 while (extensions
.size() > 0) {
236 base::StringPiece extension
;
237 if (!GetElement(&extensions
, kSEQUENCE
, &extension
))
240 base::StringPiece oid
;
241 if (!GetElement(&extension
, kOID
, &oid
))
244 // kCRLDistributionPointsOID is the DER encoding of the OID for the X.509
245 // CRL Distribution Points extension.
246 static const uint8_t kCRLDistributionPointsOID
[] = {0x55, 0x1d, 0x1f};
248 if (oid
.size() != sizeof(kCRLDistributionPointsOID
) ||
249 memcmp(oid
.data(), kCRLDistributionPointsOID
, oid
.size()) != 0) {
254 GetElement(&extension
, kBOOLEAN
, NULL
);
257 base::StringPiece extension_value
;
258 if (!GetElement(&extension
, kOCTETSTRING
, &extension_value
))
261 // RFC 5280, section 4.2.1.13.
263 // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
265 // DistributionPoint ::= SEQUENCE {
266 // distributionPoint [0] DistributionPointName OPTIONAL,
267 // reasons [1] ReasonFlags OPTIONAL,
268 // cRLIssuer [2] GeneralNames OPTIONAL }
270 base::StringPiece distribution_points
;
271 if (!GetElement(&extension_value
, kSEQUENCE
, &distribution_points
))
274 while (distribution_points
.size() > 0) {
275 base::StringPiece distrib_point
;
276 if (!GetElement(&distribution_points
, kSEQUENCE
, &distrib_point
))
279 base::StringPiece name
;
280 if (!GetElement(&distrib_point
, kContextSpecific
| kConstructed
| 0,
282 // If it doesn't contain a name then we skip it.
286 if (GetElement(&distrib_point
, kContextSpecific
| 1, NULL
)) {
287 // If it contains a subset of reasons then we skip it. We aren't
288 // interested in subsets of CRLs and the RFC states that there MUST be
289 // a CRL that covers all reasons.
293 if (GetElement(&distrib_point
,
294 kContextSpecific
| kConstructed
| 2, NULL
)) {
295 // If it contains a alternative issuer, then we skip it.
299 // DistributionPointName ::= CHOICE {
300 // fullName [0] GeneralNames,
301 // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
302 base::StringPiece general_names
;
303 if (!GetElement(&name
,
304 kContextSpecific
| kConstructed
| 0, &general_names
)) {
308 // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
309 // GeneralName ::= CHOICE {
311 // uniformResourceIdentifier [6] IA5String,
313 while (general_names
.size() > 0) {
314 base::StringPiece url
;
315 if (GetElement(&general_names
, kContextSpecific
| 6, &url
)) {
316 tmp_urls_out
.push_back(url
);
318 if (!GetElement(&general_names
, kAny
, NULL
))
325 urls_out
->swap(tmp_urls_out
);