Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(316)

Side by Side Diff: net/cert/ocsp_parser.cc

Issue 1541213002: Adding OCSP Parser (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix break condition. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
eroman 2016/02/03 22:54:44 can change to 2016
svaldez 2016/02/04 19:03:24 Done.
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 <algorithm>
6
7 #include "base/base64.h"
8 #include "base/sha1.h"
9 #include "net/cert/internal/extended_key_usage.h"
10 #include "net/cert/internal/signature_policy.h"
11 #include "net/cert/internal/verify_name_match.h"
12 #include "net/cert/internal/verify_signed_data.h"
13 #include "net/cert/ocsp_parser.h"
14
15 namespace net {
16
17 namespace cert {
18
19 OCSPSingleResponse::OCSPSingleResponse() {}
20 OCSPSingleResponse::~OCSPSingleResponse() {}
21
22 OCSPResponseData::OCSPResponseData() {}
23 OCSPResponseData::~OCSPResponseData() {}
24
25 OCSPResponse::OCSPResponse() {}
26 OCSPResponse::~OCSPResponse() {}
27
28 der::Input BasicOCSPResponseOid() {
29 // From RFC 6960:
30 //
31 // id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
32 // id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
33 //
34 // In dotted notation: 1.3.6.1.5.5.7.48.1.1
35 static const uint8_t oid[] = {0x2b, 0x06, 0x01, 0x05, 0x05,
36 0x07, 0x30, 0x01, 0x01};
37 return der::Input(oid);
38 }
39
40 bool ParseOCSPSingleResponse(der::Input raw_tlv, OCSPSingleResponse* out) {
eroman 2016/02/03 22:54:45 Please provide comments somewhere that reflect the
svaldez 2016/02/04 19:03:24 Should this be both here and in the header file, o
41 der::Parser response_parser(raw_tlv);
42 der::Parser parser;
43 if (!response_parser.ReadSequence(&parser))
eroman 2016/02/03 22:54:45 It doesn't look like unconsumed data is being chec
svaldez 2016/02/04 19:03:24 Done.
44 return false;
45
46 der::Input cert_id;
47 if (!parser.ReadTag(der::kSequence, &cert_id))
eroman 2016/02/03 22:54:45 The status parsing is a good candidate to move to
svaldez 2016/02/04 19:03:24 Done.
48 return false;
49 out->cert_id = cert_id.AsString();
50 der::Tag status_tag;
51 der::Input status;
52 if (!parser.ReadTagAndValue(&status_tag, &status))
53 return false;
54 if (status_tag == der::ContextSpecificPrimitive(0)) {
55 out->cert_status = OCSPSingleResponse::CERT_GOOD;
56 } else if (status_tag == der::ContextSpecificPrimitive(1)) {
57 out->cert_status = OCSPSingleResponse::CERT_REVOKED;
58 der::Parser revoked_info_parser(status);
eroman 2016/02/03 22:54:45 Same issue here, what if there is unconsumed data
svaldez 2016/02/04 19:03:24 Done.
59 der::Input revocation_time;
60 der::Input revocation_reason;
61 if (!revoked_info_parser.ReadGeneralizedTime(&(out->revocation_time)))
62 return false;
63 if (!revoked_info_parser.ReadTag(der::kEnumerated, &revocation_reason))
64 return false;
65 uint8_t revocation_reason_value;
66 if (!der::ParseUint8(revocation_reason, &revocation_reason_value))
67 return false;
68 out->revocation_reason = static_cast<OCSPSingleResponse::RevocationReason>(
eroman 2016/02/03 22:54:45 This needs to ensure that the number is in range,
svaldez 2016/02/04 19:03:24 Done.
69 revocation_reason_value);
70 } else if (status_tag == der::ContextSpecificPrimitive(2)) {
71 out->cert_status = OCSPSingleResponse::CERT_UNKNOWN;
eroman 2016/02/03 22:54:44 In the case of !CERT_REVOKED would be a good idea
svaldez 2016/02/04 19:03:24 Done.
72 } else {
73 return false;
74 }
75
76 der::Input this_update;
77 if (!parser.ReadGeneralizedTime(&(out->this_update)))
78 return false;
79 der::Input next_update_input;
80 bool next_update_present;
81 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
82 &next_update_input, &next_update_present)) {
83 return false;
84 }
85
86 if (next_update_present) {
87 der::Parser next_update_parser(next_update_input);
eroman 2016/02/03 22:54:45 Same issue here, this should fail if there was unc
svaldez 2016/02/04 19:03:24 Done.
88 der::Input next_update;
89 if (!next_update_parser.ReadGeneralizedTime(&(out->next_update)))
eroman 2016/02/03 22:54:45 How does a caller distinguish when it was present?
svaldez 2016/02/04 19:03:24 Done. Adding has_... for all optional fields.
90 return false;
91 }
92 bool extensions_present;
93 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
94 &(out->extensions), &extensions_present)) {
eroman 2016/02/03 22:54:45 Same question here: how do callers of the function
svaldez 2016/02/04 19:03:24 Done.
95 return false;
96 }
97
98 return true;
99 }
100
101 bool ParseOCSPResponseData(der::Input raw_tlv, OCSPResponseData* out) {
eroman 2016/02/03 22:54:45 Document the structure being parsed, referencing t
svaldez 2016/02/04 19:03:24 Acknowledged.
102 der::Parser parser;
103 if (!der::Parser(raw_tlv).ReadSequence(&parser))
eroman 2016/02/03 22:54:44 Fail on unconsumed data?
svaldez 2016/02/04 19:03:24 Done.
104 return false;
105
106 der::Input version_input;
107 bool version_present;
108 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
109 &version_input, &version_present)) {
110 return false;
111 }
112
113 if (version_present) {
114 der::Parser version_parser(version_input);
115 der::Input version;
116 if (!version_parser.ReadTag(der::kInteger, &version))
117 return false;
118 if (!der::ParseUint8(version, &(out->version)))
eroman 2016/02/03 22:54:44 Because the version is DEFAULT v1, a strict DER pa
svaldez 2016/02/04 19:03:24 Done.
119 return false;
120 } else {
121 out->version = 0;
122 }
123
124 der::Tag id_tag;
125 der::Input responder_id_input;
126 OCSPResponseData::ResponderID responder_id;
127 if (!parser.ReadTagAndValue(&id_tag, &responder_id_input))
128 return false;
129 if (id_tag == der::ContextSpecificConstructed(1)) {
130 responder_id.type = OCSPResponseData::NAME;
131 responder_id.name = responder_id_input;
132 } else if (id_tag == der::ContextSpecificConstructed(2)) {
133 der::Parser key_parser(responder_id_input);
134 der::Input responder_key;
135 if (!key_parser.ReadTag(der::kOctetString, &responder_key))
136 return false;
137
138 if (responder_key.Length() != base::kSHA1Length)
eroman 2016/02/03 22:54:44 given the memcpy below, I would say compare the le
svaldez 2016/02/04 19:03:24 Done.
139 return false;
140 SHA1HashValue key_hash;
141 memcpy(key_hash.data, responder_key.AsString().data(), base::kSHA1Length);
eroman 2016/02/03 22:54:44 Unnecessary to call AsString() that will construct
svaldez 2016/02/04 19:03:24 Done.
142 responder_id.type = OCSPResponseData::KEY_HASH;
143 responder_id.key_hash = HashValue(key_hash);
144 } else {
145 return false;
146 }
147 out->responder_id = responder_id;
148
149 der::Input produced_at;
150 if (!parser.ReadGeneralizedTime(&(out->produced_at)))
151 return false;
152
153 der::Parser responses_parser;
154 if (!parser.ReadSequence(&responses_parser))
155 return false;
156 while (responses_parser.HasMore()) {
157 der::Input single_response;
158 if (!responses_parser.ReadRawTLV(&single_response))
159 return false;
160 out->responses.push_back(single_response);
eroman 2016/02/03 22:54:44 Should out->responses not be cleared before starti
svaldez 2016/02/04 19:03:24 Done.
161 }
162
163 bool extensions_present;
164 if (!parser.ReadOptionalTag(der::ContextSpecificConstructed(1),
165 &(out->extensions), &extensions_present)) {
eroman 2016/02/03 22:54:45 Same question here about disambiguating present an
svaldez 2016/02/04 19:03:24 Done.
166 return false;
167 }
168 return true;
169 }
170
171 bool ParseOCSPResponse(der::Input ocsp_response, OCSPResponse* out) {
eroman 2016/02/03 22:54:45 document the structure being parsed and reference
svaldez 2016/02/04 19:03:24 Acknowledged.
172 der::Parser parser(ocsp_response);
eroman 2016/02/03 22:54:45 What about unconsumed data?
svaldez 2016/02/04 19:03:24 Done.
173 der::Input response_status;
174 der::Parser ocsp_response_parser;
175 if (!parser.ReadSequence(&ocsp_response_parser))
176 return false;
177 if (!ocsp_response_parser.ReadTag(der::kEnumerated, &response_status))
178 return false;
179 uint8_t response_status_value;
180 if (!der::ParseUint8(response_status, &response_status_value))
181 return false;
182 out->status =
183 static_cast<OCSPResponse::ResponseStatus>(response_status_value);
184 if (out->status != OCSPResponse::SUCCESSFUL)
eroman 2016/02/03 22:54:44 Why is this a failure?
svaldez 2016/02/04 19:03:24 It isn't. "return true" since we don't have to par
185 return true;
186
187 der::Input response_type_oid;
188 der::Input response_string;
189 der::Parser response_bytes_input_parser;
190 der::Parser response_bytes_parser;
191 if (!ocsp_response_parser.ReadConstructed(der::ContextSpecificConstructed(0),
192 &response_bytes_input_parser)) {
eroman 2016/02/03 22:54:44 What about unconsumed data?
svaldez 2016/02/04 19:03:24 Done.
193 return false;
194 }
195 if (!response_bytes_input_parser.ReadSequence(&response_bytes_parser))
196 return false;
197 if (!response_bytes_parser.ReadTag(der::kOid, &response_type_oid))
198 return false;
199 if (!response_type_oid.Equals(BasicOCSPResponseOid()))
200 return false;
201 if (!response_bytes_parser.ReadTag(der::kOctetString, &response_string))
202 return false;
203
204 der::Parser response_parser(response_string);
eroman 2016/02/03 22:54:45 What about unconsumed data?
svaldez 2016/02/04 19:03:24 Done.
205 der::Parser basic_response_parser;
206 der::Parser response_data_parser;
207 der::Input sigalg_tlv;
eroman 2016/02/03 22:54:44 This function is big. It needs some documentation
208 if (!response_parser.ReadSequence(&basic_response_parser))
209 return false;
210 if (!basic_response_parser.ReadRawTLV(&(out->data)))
211 return false;
212 if (!basic_response_parser.ReadRawTLV(&sigalg_tlv))
213 return false;
214 out->signature_algorithm = SignatureAlgorithm::CreateFromDer(sigalg_tlv);
215
216 if (!basic_response_parser.ReadBitString(&(out->signature)))
217 return false;
218 der::Input certs_input;
219 bool certs_present;
220 if (!basic_response_parser.ReadOptionalTag(der::ContextSpecificConstructed(0),
221 &certs_input, &certs_present)) {
222 return false;
223 }
224
225 if (!certs_present)
226 return true;
227 der::Parser certs_input_parser(certs_input);
eroman 2016/02/03 22:54:44 Unconsumed data?
eroman 2016/02/03 22:54:45 Same comment as previously. (It is hard to follow
svaldez 2016/02/04 19:03:24 Done.
228 der::Parser certs_parser;
229 if (!certs_input_parser.ReadSequence(&certs_parser))
230 return false;
231 while (certs_parser.HasMore()) {
232 ParsedCertificate parsed_cert;
233 der::Input cert_tlv;
234 if (!certs_parser.ReadRawTLV(&cert_tlv))
235 return false;
236 if (!ParseCertificate(cert_tlv, &parsed_cert))
eroman 2016/02/03 22:54:44 I am not convinced that parsing the certificates s
237 return false;
238 out->certs.push_back(parsed_cert);
239 }
240 return true;
241 }
242
243 namespace {
244
245 bool CheckResponder(OCSPResponseData::ResponderID id,
246 ParsedTbsCertificate cert) {
247 if (id.type == OCSPResponseData::NAME) {
248 return VerifyNameMatch(id.name, cert.issuer_tlv);
249 } else {
250 der::Parser parser(cert.spki_tlv);
251 der::Parser spki_parser;
252 der::BitString key_bits;
253 if (!parser.ReadSequence(&spki_parser))
254 return false;
255 if (!spki_parser.SkipTag(der::kSequence))
256 return false;
257 if (!spki_parser.ReadBitString(&key_bits))
258 return false;
259
260 der::Input key = key_bits.bytes();
261 HashValue key_hash(HASH_VALUE_SHA1);
262 base::SHA1HashBytes(key.UnsafeData(), key.Length(), key_hash.data());
263 return key_hash.Equals(id.key_hash);
264 }
265 }
266
267 } // namespace
268
269 bool VerifyOCSPResponse(OCSPResponse* response, ParsedCertificate* cert) {
eroman 2016/02/03 22:54:44 I didn't review this function yet, but I am skepti
svaldez 2016/02/04 19:03:24 Unfortunately the certificate "chain" used for OCS
270 SimpleSignaturePolicy signature_policy(1024);
271
272 OCSPResponseData response_data;
273 if (!ParseOCSPResponseData(response->data, &response_data))
274 return false;
275
276 ParsedTbsCertificate issuer;
277 ParsedTbsCertificate responder;
278 if (!cert || !ParseTbsCertificate(cert->tbs_certificate_tlv, &issuer))
279 return false;
280
281 if (CheckResponder(response_data.responder_id, issuer)) {
282 responder = issuer;
283 } else {
284 bool found = false;
285 for (const auto& responder_cert : response->certs) {
286 ParsedTbsCertificate tbs_cert;
287 if (!ParseTbsCertificate(responder_cert.tbs_certificate_tlv, &tbs_cert))
288 return false;
289
290 if (CheckResponder(response_data.responder_id, tbs_cert)) {
291 found = true;
292 responder = tbs_cert;
293
294 scoped_ptr<SignatureAlgorithm> signature_algorithm =
295 SignatureAlgorithm::CreateFromDer(
296 responder_cert.signature_algorithm_tlv);
297 der::Input issuer_spki = issuer.spki_tlv;
298 if (!VerifySignedData(*(signature_algorithm.get()),
299 responder_cert.tbs_certificate_tlv,
300 responder_cert.signature_value, issuer_spki,
301 &signature_policy)) {
302 return false;
303 }
304
305 std::map<der::Input, ParsedExtension> extensions;
306 std::vector<der::Input> eku;
307 if (!ParseExtensions(responder.extensions_tlv, &extensions))
308 return false;
309 if (!ParseEKUExtension(extensions[ExtKeyUsageOid()].value, &eku))
310 return false;
311 if (std::find(eku.begin(), eku.end(), OCSPSigning()) == eku.end())
312 return false;
313 break;
314 }
315 }
316 if (!found)
317 return false;
318 }
319
320 return VerifySignedData(*(response->signature_algorithm.get()),
321 response->data, response->signature,
322 responder.spki_tlv, &signature_policy);
323 }
324
325 } // namespace cert
326
327 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698