Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |