OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/base/asn1_util.h" | |
6 | |
7 namespace net { | |
8 | |
9 namespace asn1 { | |
10 | |
11 bool ParseElement(base::StringPiece* in, | |
12 unsigned tag_value, | |
13 base::StringPiece* out, | |
14 unsigned *out_header_len) { | |
15 const uint8* data = reinterpret_cast<const uint8*>(in->data()); | |
16 | |
17 // We don't support kAny and kOptional at the same time. | |
18 if ((tag_value & kAny) && (tag_value & kOptional)) | |
19 return false; | |
20 | |
21 if (in->empty() && (tag_value & kOptional)) { | |
22 if (out_header_len) | |
23 *out_header_len = 0; | |
24 if (out) | |
25 *out = base::StringPiece(); | |
26 return true; | |
27 } | |
28 | |
29 if (in->size() < 2) | |
30 return false; | |
31 | |
32 if (tag_value != kAny && | |
33 static_cast<unsigned char>(data[0]) != (tag_value & 0xff)) { | |
34 if (tag_value & kOptional) { | |
35 if (out_header_len) | |
36 *out_header_len = 0; | |
37 if (out) | |
38 *out = base::StringPiece(); | |
39 return true; | |
40 } | |
41 return false; | |
42 } | |
43 | |
44 size_t len = 0; | |
45 if ((data[1] & 0x80) == 0) { | |
46 // short form length | |
47 if (out_header_len) | |
48 *out_header_len = 2; | |
49 len = static_cast<size_t>(data[1]) + 2; | |
50 } else { | |
51 // long form length | |
52 const unsigned num_bytes = data[1] & 0x7f; | |
53 if (num_bytes == 0 || num_bytes > 2) | |
54 return false; | |
55 if (in->size() < 2 + num_bytes) | |
56 return false; | |
57 len = data[2]; | |
58 if (num_bytes == 2) { | |
59 if (len == 0) { | |
60 // the length encoding must be minimal. | |
61 return false; | |
62 } | |
63 len <<= 8; | |
64 len += data[3]; | |
65 } | |
66 if (len < 128) { | |
67 // the length should have been encoded in short form. This distinguishes | |
68 // DER from BER encoding. | |
69 return false; | |
70 } | |
71 if (out_header_len) | |
72 *out_header_len = 2 + num_bytes; | |
73 len += 2 + num_bytes; | |
74 } | |
75 | |
76 if (in->size() < len) | |
77 return false; | |
78 if (out) | |
79 *out = base::StringPiece(in->data(), len); | |
80 in->remove_prefix(len); | |
81 return true; | |
82 } | |
83 | |
84 bool GetElement(base::StringPiece* in, | |
85 unsigned tag_value, | |
86 base::StringPiece* out) { | |
87 unsigned header_len; | |
88 if (!ParseElement(in, tag_value, out, &header_len)) | |
89 return false; | |
90 if (out) | |
91 out->remove_prefix(header_len); | |
92 return true; | |
93 } | |
94 | |
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 } | |
104 | |
105 // TBSCertificate ::= SEQUENCE { | |
106 // version [0] EXPLICIT Version DEFAULT v1, | |
107 // serialNumber CertificateSerialNumber, | |
108 // signature AlgorithmIdentifier, | |
109 // issuer Name, | |
110 // validity Validity, | |
111 // subject Name, | |
112 // subjectPublicKeyInfo SubjectPublicKeyInfo, | |
113 | |
114 base::StringPiece certificate; | |
115 if (!GetElement(cert, kSEQUENCE, &certificate)) | |
116 return false; | |
117 | |
118 // We don't allow junk after the certificate. | |
119 if (!cert->empty()) | |
120 return false; | |
121 | |
122 base::StringPiece tbs_certificate; | |
123 if (!GetElement(&certificate, kSEQUENCE, &tbs_certificate)) | |
124 return false; | |
125 | |
126 if (!GetElement(&tbs_certificate, | |
127 kOptional | kConstructed | kContextSpecific | 0, | |
128 NULL)) { | |
129 return false; | |
130 } | |
131 | |
132 // serialNumber | |
133 if (!GetElement(&tbs_certificate, kINTEGER, NULL)) | |
134 return false; | |
135 // signature | |
136 if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) | |
137 return false; | |
138 // issuer | |
139 if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) | |
140 return false; | |
141 // validity | |
142 if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) | |
143 return false; | |
144 // subject | |
145 if (!GetElement(&tbs_certificate, kSEQUENCE, NULL)) | |
146 return false; | |
147 *cert = tbs_certificate; | |
148 return true; | |
149 } | |
150 | |
151 bool ExtractSPKIFromDERCert(base::StringPiece cert, | |
152 base::StringPiece* spki_out) { | |
153 if (!SeekToSPKI(&cert)) | |
154 return false; | |
155 if (!ParseElement(&cert, kSEQUENCE, spki_out, NULL)) | |
156 return false; | |
157 return true; | |
158 } | |
159 | |
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 } | |
166 // | |
167 // AlgorithmIdentifier ::= SEQUENCE { | |
168 // algorithm OBJECT IDENTIFIER, | |
169 // parameters ANY DEFINED BY algorithm OPTIONAL } | |
170 | |
171 // Step into SubjectPublicKeyInfo sequence. | |
172 base::StringPiece spki_contents; | |
173 if (!asn1::GetElement(&spki, asn1::kSEQUENCE, &spki_contents)) | |
174 return false; | |
175 | |
176 // Step over algorithm field (a SEQUENCE). | |
177 base::StringPiece algorithm; | |
178 if (!asn1::GetElement(&spki_contents, asn1::kSEQUENCE, &algorithm)) | |
179 return false; | |
180 | |
181 // Extract the subjectPublicKey field. | |
182 if (!asn1::GetElement(&spki_contents, asn1::kBITSTRING, spk_out)) | |
183 return false; | |
184 return true; | |
185 } | |
186 | |
187 | |
188 bool ExtractCRLURLsFromDERCert(base::StringPiece cert, | |
189 std::vector<base::StringPiece>* urls_out) { | |
190 urls_out->clear(); | |
191 std::vector<base::StringPiece> tmp_urls_out; | |
192 | |
193 if (!SeekToSPKI(&cert)) | |
194 return false; | |
195 | |
196 // From RFC 5280, section 4.1 | |
197 // TBSCertificate ::= SEQUENCE { | |
198 // ... | |
199 // subjectPublicKeyInfo SubjectPublicKeyInfo, | |
200 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, | |
201 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, | |
202 // extensions [3] EXPLICIT Extensions OPTIONAL | |
203 | |
204 // subjectPublicKeyInfo | |
205 if (!GetElement(&cert, kSEQUENCE, NULL)) | |
206 return false; | |
207 // issuerUniqueID | |
208 if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 1, NULL)) | |
209 return false; | |
210 // subjectUniqueID | |
211 if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 2, NULL)) | |
212 return false; | |
213 | |
214 base::StringPiece extensions_seq; | |
215 if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 3, | |
216 &extensions_seq)) { | |
217 return false; | |
218 } | |
219 | |
220 if (extensions_seq.empty()) | |
221 return true; | |
222 | |
223 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension | |
224 // Extension ::= SEQUENCE { | |
225 // extnID OBJECT IDENTIFIER, | |
226 // critical BOOLEAN DEFAULT FALSE, | |
227 // extnValue OCTET STRING | |
228 | |
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)) | |
233 return false; | |
234 | |
235 while (extensions.size() > 0) { | |
236 base::StringPiece extension; | |
237 if (!GetElement(&extensions, kSEQUENCE, &extension)) | |
238 return false; | |
239 | |
240 base::StringPiece oid; | |
241 if (!GetElement(&extension, kOID, &oid)) | |
242 return false; | |
243 | |
244 // kCRLDistributionPointsOID is the DER encoding of the OID for the X.509 | |
245 // CRL Distribution Points extension. | |
246 static const uint8 kCRLDistributionPointsOID[] = {0x55, 0x1d, 0x1f}; | |
247 | |
248 if (oid.size() != sizeof(kCRLDistributionPointsOID) || | |
249 memcmp(oid.data(), kCRLDistributionPointsOID, oid.size()) != 0) { | |
250 continue; | |
251 } | |
252 | |
253 // critical | |
254 GetElement(&extension, kBOOLEAN, NULL); | |
255 | |
256 // extnValue | |
257 base::StringPiece extension_value; | |
258 if (!GetElement(&extension, kOCTETSTRING, &extension_value)) | |
259 return false; | |
260 | |
261 // RFC 5280, section 4.2.1.13. | |
262 // | |
263 // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint | |
264 // | |
265 // DistributionPoint ::= SEQUENCE { | |
266 // distributionPoint [0] DistributionPointName OPTIONAL, | |
267 // reasons [1] ReasonFlags OPTIONAL, | |
268 // cRLIssuer [2] GeneralNames OPTIONAL } | |
269 | |
270 base::StringPiece distribution_points; | |
271 if (!GetElement(&extension_value, kSEQUENCE, &distribution_points)) | |
272 return false; | |
273 | |
274 while (distribution_points.size() > 0) { | |
275 base::StringPiece distrib_point; | |
276 if (!GetElement(&distribution_points, kSEQUENCE, &distrib_point)) | |
277 return false; | |
278 | |
279 base::StringPiece name; | |
280 if (!GetElement(&distrib_point, kContextSpecific | kConstructed | 0, | |
281 &name)) { | |
282 // If it doesn't contain a name then we skip it. | |
283 continue; | |
284 } | |
285 | |
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. | |
290 continue; | |
291 } | |
292 | |
293 if (GetElement(&distrib_point, | |
294 kContextSpecific | kConstructed | 2, NULL)) { | |
295 // If it contains a alternative issuer, then we skip it. | |
296 continue; | |
297 } | |
298 | |
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)) { | |
305 continue; | |
306 } | |
307 | |
308 // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName | |
309 // GeneralName ::= CHOICE { | |
310 // ... | |
311 // uniformResourceIdentifier [6] IA5String, | |
312 // ... | |
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); | |
317 } else { | |
318 if (!GetElement(&general_names, kAny, NULL)) | |
319 return false; | |
320 } | |
321 } | |
322 } | |
323 } | |
324 | |
325 urls_out->swap(tmp_urls_out); | |
326 return true; | |
327 } | |
328 | |
329 } // namespace asn1 | |
330 | |
331 } // namespace net | |
OLD | NEW |