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

Side by Side Diff: net/base/crl_set.cc

Issue 9149010: net: allow CRLSets to block specific SPKIs. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: ... Created 8 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/base64.h" 5 #include "base/base64.h"
6 #include "base/format_macros.h" 6 #include "base/format_macros.h"
7 #include "base/json/json_reader.h" 7 #include "base/json/json_reader.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/stl_util.h" 9 #include "base/stl_util.h"
10 #include "base/string_util.h" 10 #include "base/string_util.h"
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
116 // header's "DeltaFrom" value. The delta describes the changes to each CRL 116 // header's "DeltaFrom" value. The delta describes the changes to each CRL
117 // in turn with a zlib compressed array of options: either the CRL is the same, 117 // in turn with a zlib compressed array of options: either the CRL is the same,
118 // a new CRL is inserted, the CRL is deleted or the CRL is updated. In the case 118 // a new CRL is inserted, the CRL is deleted or the CRL is updated. In the case
119 // of an update, the serials in the CRL are considered in the same fashion 119 // of an update, the serials in the CRL are considered in the same fashion
120 // except there is no delta update of a serial number: they are either 120 // except there is no delta update of a serial number: they are either
121 // inserted, deleted or left the same. 121 // inserted, deleted or left the same.
122 122
123 // ReadHeader reads the header (including length prefix) from |data| and 123 // ReadHeader reads the header (including length prefix) from |data| and
124 // updates |data| to remove the header on return. Caller takes ownership of the 124 // updates |data| to remove the header on return. Caller takes ownership of the
125 // returned pointer. 125 // returned pointer.
126 static DictionaryValue* ReadHeader(base::StringPiece* data) { 126 static base::DictionaryValue* ReadHeader(base::StringPiece* data) {
wtc 2012/01/10 00:50:59 Just curious: why didn't we need base:: before?
agl 2012/01/10 16:15:29 There are 'using' statements in the header which m
127 if (data->size() < 2) 127 if (data->size() < 2)
128 return NULL; 128 return NULL;
129 uint16 header_len; 129 uint16 header_len;
130 memcpy(&header_len, data->data(), 2); // assumes little-endian. 130 memcpy(&header_len, data->data(), 2); // assumes little-endian.
131 data->remove_prefix(2); 131 data->remove_prefix(2);
132 132
133 if (data->size() < header_len) 133 if (data->size() < header_len)
134 return NULL; 134 return NULL;
135 135
136 const base::StringPiece header_bytes(data->data(), header_len); 136 const base::StringPiece header_bytes(data->data(), header_len);
137 data->remove_prefix(header_len); 137 data->remove_prefix(header_len);
138 138
139 scoped_ptr<Value> header(base::JSONReader::Read( 139 scoped_ptr<Value> header(base::JSONReader::Read(
140 header_bytes.as_string(), true /* allow trailing comma */)); 140 header_bytes.as_string(), true /* allow trailing comma */));
141 if (header.get() == NULL) 141 if (header.get() == NULL)
142 return NULL; 142 return NULL;
143 143
144 if (!header->IsType(Value::TYPE_DICTIONARY)) 144 if (!header->IsType(Value::TYPE_DICTIONARY))
145 return NULL; 145 return NULL;
146 return reinterpret_cast<DictionaryValue*>(header.release()); 146 return reinterpret_cast<base::DictionaryValue*>(header.release());
147 } 147 }
148 148
149 // kCurrentFileVersion is the version of the CRLSet file format that we 149 // kCurrentFileVersion is the version of the CRLSet file format that we
150 // currently implement. 150 // currently implement.
151 static const int kCurrentFileVersion = 0; 151 static const int kCurrentFileVersion = 0;
152 152
153 static bool ReadCRL(base::StringPiece* data, std::string* out_parent_spki_hash, 153 static bool ReadCRL(base::StringPiece* data, std::string* out_parent_spki_hash,
154 std::vector<std::string>* out_serials) { 154 std::vector<std::string>* out_serials) {
155 if (data->size() < crypto::kSHA256Length) 155 if (data->size() < crypto::kSHA256Length)
156 return false; 156 return false;
(...skipping 16 matching lines...) Expand all
173 if (data->size() < serial_length) 173 if (data->size() < serial_length)
174 return false; 174 return false;
175 std::string serial(data->data(), serial_length); 175 std::string serial(data->data(), serial_length);
176 data->remove_prefix(serial_length); 176 data->remove_prefix(serial_length);
177 out_serials->push_back(serial); 177 out_serials->push_back(serial);
178 } 178 }
179 179
180 return true; 180 return true;
181 } 181 }
182 182
183 bool CRLSet::CopyBlockedSPKIsFromHeader(base::DictionaryValue* header_dict) {
184 ListValue* blocked_spkis_list = NULL;
185 if (!header_dict->GetList("BlockedSPKIs", &blocked_spkis_list)) {
wtc 2012/01/10 00:50:59 Nit: might be better to name this header "RevokedS
agl 2012/01/10 16:15:29 I'd actually like to keep it as blocked. Revoked m
186 // BlockedSPKIs is optional, so it's fine if we don't find it.
187 return true;
188 }
189
190 blocked_spkis_.clear();
wtc 2012/01/10 00:50:59 IMPORTANT: does this blocked_spkis_.clear() call m
agl 2012/01/10 16:15:29 Yes, the blocked SPKIs are included in every CRLSe
wtc 2012/01/11 01:15:12 It would be nice to document this somewhere. This
191
192 for (size_t i = 0; i < blocked_spkis_list->GetSize(); i++) {
wtc 2012/01/10 00:50:59 Use ++i in all the new for loops. It's more impor
agl 2012/01/10 16:15:29 Done, but it's really a waste of time! For example
193 std::string spki_sha256_base64, spki_sha256;
194 if (!blocked_spkis_list->GetString(i, &spki_sha256_base64))
195 return false;
196 if (!base::Base64Decode(spki_sha256_base64, &spki_sha256))
197 return false;
198 blocked_spkis_.push_back(spki_sha256);
199 }
200
201 return true;
202 }
203
183 // static 204 // static
184 bool CRLSet::Parse(base::StringPiece data, scoped_refptr<CRLSet>* out_crl_set) { 205 bool CRLSet::Parse(base::StringPiece data, scoped_refptr<CRLSet>* out_crl_set) {
185 // Other parts of Chrome assume that we're little endian, so we don't lose 206 // Other parts of Chrome assume that we're little endian, so we don't lose
186 // anything by doing this. 207 // anything by doing this.
187 #if defined(__BYTE_ORDER) 208 #if defined(__BYTE_ORDER)
188 // Linux check 209 // Linux check
189 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); 210 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian);
190 #elif defined(__BIG_ENDIAN__) 211 #elif defined(__BIG_ENDIAN__)
191 // Mac check 212 // Mac check
192 #error assumes little endian 213 #error assumes little endian
193 #endif 214 #endif
194 215
195 scoped_ptr<DictionaryValue> header_dict(ReadHeader(&data)); 216 scoped_ptr<base::DictionaryValue> header_dict(ReadHeader(&data));
196 if (!header_dict.get()) 217 if (!header_dict.get())
197 return false; 218 return false;
198 219
199 std::string contents; 220 std::string contents;
200 if (!header_dict->GetString("ContentType", &contents)) 221 if (!header_dict->GetString("ContentType", &contents))
201 return false; 222 return false;
202 if (contents != "CRLSet") 223 if (contents != "CRLSet")
203 return false; 224 return false;
204 225
205 int version; 226 int version;
(...skipping 12 matching lines...) Expand all
218 for (size_t crl_index = 0; !data.empty(); crl_index++) { 239 for (size_t crl_index = 0; !data.empty(); crl_index++) {
219 std::string parent_spki_sha256; 240 std::string parent_spki_sha256;
220 std::vector<std::string> serials; 241 std::vector<std::string> serials;
221 if (!ReadCRL(&data, &parent_spki_sha256, &serials)) 242 if (!ReadCRL(&data, &parent_spki_sha256, &serials))
222 return false; 243 return false;
223 244
224 crl_set->crls_.push_back(std::make_pair(parent_spki_sha256, serials)); 245 crl_set->crls_.push_back(std::make_pair(parent_spki_sha256, serials));
225 crl_set->crls_index_by_issuer_[parent_spki_sha256] = crl_index; 246 crl_set->crls_index_by_issuer_[parent_spki_sha256] = crl_index;
226 } 247 }
227 248
249 if (!crl_set->CopyBlockedSPKIsFromHeader(header_dict.get()))
250 return false;
251
228 *out_crl_set = crl_set; 252 *out_crl_set = crl_set;
229 return true; 253 return true;
230 } 254 }
231 255
232 // kMaxUncompressedChangesLength is the largest changes array that we'll 256 // kMaxUncompressedChangesLength is the largest changes array that we'll
233 // accept. This bounds the number of CRLs in the CRLSet as well as the number 257 // accept. This bounds the number of CRLs in the CRLSet as well as the number
234 // of serial numbers in a given CRL. 258 // of serial numbers in a given CRL.
235 static const unsigned kMaxUncompressedChangesLength = 1024 * 1024; 259 static const unsigned kMaxUncompressedChangesLength = 1024 * 1024;
236 260
237 static bool ReadChanges(base::StringPiece* data, 261 static bool ReadChanges(base::StringPiece* data,
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 } 331 }
308 332
309 if (i != old_serials.size()) 333 if (i != old_serials.size())
310 return false; 334 return false;
311 return true; 335 return true;
312 } 336 }
313 337
314 bool CRLSet::ApplyDelta(const base::StringPiece& in_data, 338 bool CRLSet::ApplyDelta(const base::StringPiece& in_data,
315 scoped_refptr<CRLSet>* out_crl_set) { 339 scoped_refptr<CRLSet>* out_crl_set) {
316 base::StringPiece data(in_data); 340 base::StringPiece data(in_data);
317 scoped_ptr<DictionaryValue> header_dict(ReadHeader(&data)); 341 scoped_ptr<base::DictionaryValue> header_dict(ReadHeader(&data));
318 if (!header_dict.get()) 342 if (!header_dict.get())
319 return false; 343 return false;
320 344
321 std::string contents; 345 std::string contents;
322 if (!header_dict->GetString("ContentType", &contents)) 346 if (!header_dict->GetString("ContentType", &contents))
323 return false; 347 return false;
324 if (contents != "CRLSetDelta") 348 if (contents != "CRLSetDelta")
325 return false; 349 return false;
326 350
327 int version; 351 int version;
328 if (!header_dict->GetInteger("Version", &version) || 352 if (!header_dict->GetInteger("Version", &version) ||
329 version != kCurrentFileVersion) { 353 version != kCurrentFileVersion) {
330 return false; 354 return false;
331 } 355 }
332 356
333 int sequence, delta_from; 357 int sequence, delta_from;
334 if (!header_dict->GetInteger("Sequence", &sequence) || 358 if (!header_dict->GetInteger("Sequence", &sequence) ||
335 !header_dict->GetInteger("DeltaFrom", &delta_from) || 359 !header_dict->GetInteger("DeltaFrom", &delta_from) ||
336 delta_from < 0 || 360 delta_from < 0 ||
337 static_cast<uint32>(delta_from) != sequence_) { 361 static_cast<uint32>(delta_from) != sequence_) {
338 return false; 362 return false;
339 } 363 }
340 364
341 scoped_refptr<CRLSet> crl_set(new CRLSet); 365 scoped_refptr<CRLSet> crl_set(new CRLSet);
342 crl_set->sequence_ = static_cast<uint32>(sequence); 366 crl_set->sequence_ = static_cast<uint32>(sequence);
343 367
368 if (!crl_set->CopyBlockedSPKIsFromHeader(header_dict.get()))
369 return false;
370
344 std::vector<uint8> crl_changes; 371 std::vector<uint8> crl_changes;
345 372
346 if (!ReadChanges(&data, &crl_changes)) 373 if (!ReadChanges(&data, &crl_changes))
347 return false; 374 return false;
348 375
349 size_t i = 0, j = 0; 376 size_t i = 0, j = 0;
350 for (std::vector<uint8>::const_iterator k = crl_changes.begin(); 377 for (std::vector<uint8>::const_iterator k = crl_changes.begin();
351 k != crl_changes.end(); ++k) { 378 k != crl_changes.end(); ++k) {
352 if (*k == SYMBOL_SAME) { 379 if (*k == SYMBOL_SAME) {
353 if (i >= crls_.size()) 380 if (i >= crls_.size())
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 return false; 417 return false;
391 418
392 *out_crl_set = crl_set; 419 *out_crl_set = crl_set;
393 return true; 420 return true;
394 } 421 }
395 422
396 // static 423 // static
397 bool CRLSet::GetIsDeltaUpdate(const base::StringPiece& in_data, 424 bool CRLSet::GetIsDeltaUpdate(const base::StringPiece& in_data,
398 bool *is_delta) { 425 bool *is_delta) {
399 base::StringPiece data(in_data); 426 base::StringPiece data(in_data);
400 scoped_ptr<DictionaryValue> header_dict(ReadHeader(&data)); 427 scoped_ptr<base::DictionaryValue> header_dict(ReadHeader(&data));
401 if (!header_dict.get()) 428 if (!header_dict.get())
402 return false; 429 return false;
403 430
404 std::string contents; 431 std::string contents;
405 if (!header_dict->GetString("ContentType", &contents)) 432 if (!header_dict->GetString("ContentType", &contents))
406 return false; 433 return false;
407 434
408 if (contents == "CRLSet") { 435 if (contents == "CRLSet") {
409 *is_delta = false; 436 *is_delta = false;
410 } else if (contents == "CRLSetDelta") { 437 } else if (contents == "CRLSetDelta") {
411 *is_delta = true; 438 *is_delta = true;
412 } else { 439 } else {
413 return false; 440 return false;
414 } 441 }
415 442
416 return true; 443 return true;
417 } 444 }
418 445
419 std::string CRLSet::Serialize() const { 446 std::string CRLSet::Serialize() const {
420 std::string header = StringPrintf( 447 std::string header = StringPrintf(
421 "{" 448 "{"
422 "\"Version\":0," 449 "\"Version\":0,"
423 "\"ContentType\":\"CRLSet\"," 450 "\"ContentType\":\"CRLSet\","
424 "\"Sequence\":%u," 451 "\"Sequence\":%u,"
425 "\"DeltaFrom\":0," 452 "\"DeltaFrom\":0,"
426 "\"NumParents\":%u" 453 "\"NumParents\":%u,"
427 "}", 454 "\"BlockedSPKIs\":[",
428 static_cast<unsigned>(sequence_), 455 static_cast<unsigned>(sequence_),
429 static_cast<unsigned>(crls_.size())); 456 static_cast<unsigned>(crls_.size()));
430 457
458 for (std::vector<std::string>::const_iterator i = blocked_spkis_.begin();
459 i != blocked_spkis_.end(); i++) {
460 std::string spki_hash_base64;
461 base::Base64Encode(*i, &spki_hash_base64);
462
463 if (i != blocked_spkis_.begin())
464 header += ",";
465 header += "\"" + spki_hash_base64 + "\"";
466 }
467 header += "]}";
468
431 size_t len = 2 /* header len */ + header.size(); 469 size_t len = 2 /* header len */ + header.size();
432 470
433 for (CRLList::const_iterator i = crls_.begin(); i != crls_.end(); ++i) { 471 for (CRLList::const_iterator i = crls_.begin(); i != crls_.end(); ++i) {
434 len += i->first.size() + 4 /* num serials */; 472 len += i->first.size() + 4 /* num serials */;
435 for (std::vector<std::string>::const_iterator j = i->second.begin(); 473 for (std::vector<std::string>::const_iterator j = i->second.begin();
436 j != i->second.end(); j++) { 474 j != i->second.end(); j++) {
437 len += 1 /* serial length */ + j->size(); 475 len += 1 /* serial length */ + j->size();
438 } 476 }
439 } 477 }
440 478
(...skipping 18 matching lines...) Expand all
459 memcpy(out + off, j->data(), j->size()); 497 memcpy(out + off, j->data(), j->size());
460 off += j->size(); 498 off += j->size();
461 } 499 }
462 } 500 }
463 501
464 CHECK_EQ(off, len); 502 CHECK_EQ(off, len);
465 return ret; 503 return ret;
466 } 504 }
467 505
468 CRLSet::Result CRLSet::CheckCertificate( 506 CRLSet::Result CRLSet::CheckCertificate(
507 const base::StringPiece& spki_hash,
469 const base::StringPiece& serial_number, 508 const base::StringPiece& serial_number,
470 const base::StringPiece& parent_spki) const { 509 const base::StringPiece& issuer_spki_hash) const {
510 for (std::vector<std::string>::const_iterator i = blocked_spkis_.begin();
511 i != blocked_spkis_.end(); i++) {
512 if (spki_hash.size() == i->size() &&
513 memcmp(spki_hash.data(), i->data(), i->size()) == 0) {
514 return REVOKED;
515 }
516 }
wtc 2012/01/10 00:50:59 Nit: this function performs two independent checks
agl 2012/01/10 16:15:29 Yes, I think that's a good idea, thanks. I've call
517
518 if (issuer_spki_hash.empty())
519 return GOOD;
wtc 2012/01/10 00:50:59 IMPORTANT: the location of this check means blocke
agl 2012/01/10 16:15:29 Yep.
520
471 base::StringPiece serial(serial_number); 521 base::StringPiece serial(serial_number);
472 522
473 if (!serial.empty() && (serial[0] & 0x80) != 0) { 523 if (!serial.empty() && (serial[0] & 0x80) != 0) {
474 // This serial number is negative but the process which generates CRL sets 524 // This serial number is negative but the process which generates CRL sets
475 // will reject any certificates with negative serial numbers as invalid. 525 // will reject any certificates with negative serial numbers as invalid.
476 return UNKNOWN; 526 return UNKNOWN;
477 } 527 }
478 528
479 // Remove any leading zero bytes. 529 // Remove any leading zero bytes.
480 while (serial.size() > 1 && serial[0] == 0x00) 530 while (serial.size() > 1 && serial[0] == 0x00)
481 serial.remove_prefix(1); 531 serial.remove_prefix(1);
482 532
483 std::map<std::string, size_t>::const_iterator i = 533 std::map<std::string, size_t>::const_iterator i =
484 crls_index_by_issuer_.find(parent_spki.as_string()); 534 crls_index_by_issuer_.find(issuer_spki_hash.as_string());
485 if (i == crls_index_by_issuer_.end()) 535 if (i == crls_index_by_issuer_.end())
486 return UNKNOWN; 536 return UNKNOWN;
487 const std::vector<std::string>& serials = crls_[i->second].second; 537 const std::vector<std::string>& serials = crls_[i->second].second;
488 538
489 for (std::vector<std::string>::const_iterator i = serials.begin(); 539 for (std::vector<std::string>::const_iterator i = serials.begin();
490 i != serials.end(); ++i) { 540 i != serials.end(); ++i) {
491 if (base::StringPiece(*i) == serial) 541 if (base::StringPiece(*i) == serial)
492 return REVOKED; 542 return REVOKED;
493 } 543 }
494 544
495 return GOOD; 545 return GOOD;
496 } 546 }
497 547
498 uint32 CRLSet::sequence() const { 548 uint32 CRLSet::sequence() const {
499 return sequence_; 549 return sequence_;
500 } 550 }
501 551
502 const CRLSet::CRLList& CRLSet::crls() const { 552 const CRLSet::CRLList& CRLSet::crls() const {
503 return crls_; 553 return crls_;
504 } 554 }
505 555
506 } // namespace net 556 } // namespace net
OLDNEW
« net/base/crl_set.h ('K') | « net/base/crl_set.h ('k') | net/base/crl_set_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698