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

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

Issue 6965015: net: add CRL filter infrastructure. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 6 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
(Empty)
1 // Copyright (c) 2011 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 "base/base64.h"
6 #include "base/json/json_reader.h"
7 #include "base/logging.h"
8 #include "base/values.h"
9 #include "crypto/sha2.h"
10 #include "net/base/crl_filter.h"
11
12 #if defined(USE_SYSTEM_ZLIB)
13 #include <zlib.h>
14 #else
15 #include "third_party/zlib/zlib.h"
16 #endif
17
18 namespace net {
19
20 // Decompress zlib decompressed |in| into |out|. |out_len| is the number of
21 // bytes at |out| and must be exactly equal to the size of the decompressed
22 // data. |dict| optionally contains a pre-shared dictionary.
23 static bool DecompressZlib(char* out, int out_len, base::StringPiece in,
24 base::StringPiece dict) {
25 z_stream z;
26 memset(&z, 0, sizeof(z));
27
28 z.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(in.data()));
29 z.avail_in = in.size();
30 z.next_out = reinterpret_cast<Bytef*>(out);
31 z.avail_out = out_len;
32
33 if (inflateInit(&z) != Z_OK)
34 return false;
35 int r = inflate(&z, Z_FINISH);
36 if (r == Z_NEED_DICT) {
37 r = inflateSetDictionary(&z, reinterpret_cast<const Bytef*>(dict.data()),
38 dict.size());
39 if (r != Z_OK)
40 return false;
41 r = inflate(&z, Z_FINISH);
42 }
43 if (r != Z_STREAM_END)
44 return false;
45 if (z.avail_in || z.avail_out)
46 return false;
47 return true;
Ryan Sleevi 2011/06/02 22:01:54 LEAK: inflateEnd() is never called, particularly a
agl 2011/06/02 22:57:17 Opps, thanks!
48 }
49
50 /* A RangeDecoder is a type of entropy coder. It is superior to a Huffman
51 * encoder because symbols can use fractions of bits.
52 *
53 * Conceptually a number range is split into regions with one region for each
54 * symbol. The size of the region is proportional to the probability of the
55 * symbol:
56 *
57 * +-----+ <- 2**32 - 1
58 * | |
59 * | B |
60 * | |
61 * +-----+ <- 2**30
62 * | A |
63 * +-----+ <- 0
64 *
65 * Here, symbol B is 3 times more probable than A.
66 *
67 * This pattern is recursive: it repeats inside each region:
68 *
69 * +-----+ /+-----+
70 * | | / | |
71 * | B | / | B |
72 * | | / | |
73 * +-----+/ +-----+
74 * | A | | A |
75 * +-----+-----+-----+
76 *
77 * In this implementation, the probabilities are fixed and so are the same at
78 * every level.
79 *
80 * A range coder encodes a series of symbols by specifing a fraction along the
81 * number space such that it hits the correct symbols in order. You have to
82 * know how many symbols to expect from a range coder because it obviously
83 * produces an infinite series of symbols from any input value.
84 *
85 * In order to make the implementation fast on a computer, a high and low point
86 * are maintained that cover the current valid span of the number space.
87 * Whenever the span is small enough to that the most significant 8 bits of the
88 * high and low values are equal, a byte is produced and the current span is
89 * expanded by a factor of 256.
90 *
91 * A decoder reads these bytes and decodes symbols as required. For example,
92 * say that it reads the first byte as 0x80. It knows that the maximum value of
93 * the final span is 0x80fffffff... and the minimum value is 0x8000000...
94 * That's sufficient to figure out that the first symbol is a B.
95 *
96 * In the following, we keep track of these values:
97 * high_, low_: the high and low values of the current span. This is needed
98 * to mirror the state of the encoder so that span expansions occur at
99 * the same point.
100 *
101 * vhigh_, vlow_: the high and low values of the possible final span.
102 * vbits_: the number of bits of |vhigh_| and |vlow_| that are from data.
103 * (The rest of those values is filled with 0xff or 0x00, respectively.)
104 */
105 class RangeDecoder {
106 public:
107 // in: the input bytes
108 // spans: the probabilities of the symbols. The sum of these values must
109 // equal 2**32 - 1.
110 RangeDecoder(base::StringPiece in, const std::vector<uint32> spans)
Ryan Sleevi 2011/06/02 22:01:54 nit: const std::vector<uint32> -> const std::vecto
agl 2011/06/02 22:57:17 Done.
111 : in_(in),
112 spans_(spans),
113 high_(-1),
114 vhigh_(-1),
115 low_(0),
116 vlow_(0),
117 vbits_(0) {
118 }
119
120 bool Decode(unsigned* out_symbol) {
121 // high_ and low_ mirror the state of the encoder so, when they agree on
122 // the first byte, we have to perform span expansion.
123 while (high_ >> 24 == low_ >> 24) {
Ryan Sleevi 2011/06/02 22:01:54 nit: I think parenthesis for the sub-statements he
agl 2011/06/02 22:57:17 Done.
124 vhigh_ <<= 8;
125 vhigh_ |= 0xff;
126 vlow_ <<= 8;
127 vbits_ -= 8;
128
129 high_ <<= 8;
130 high_ |= 0xff;
131 low_ <<= 8;
132 }
133
134 // r is the range of the current span, used as a scaling factor.
135 uint64 r = high_ - low_;
136
137 // We consider each symbol in turn and decide if the final span is such
138 // that it must be the next symbol.
139 for (unsigned i = 0; i < spans_.size(); i++) {
Ryan Sleevi 2011/06/02 22:01:54 nit: unsigned -> size_t (or std::vector<uint32>::s
agl 2011/06/02 22:57:17 Done.
140 const uint32 span = spans_[i];
141 const uint32 scaled = (r * span) >> 32;
142
143 // Since our knowledge of the final span is incremental, |vhigh_| and
144 // |vlow_| might be sufficiently far apart that we can't determine the
145 // next symbol. In this case we have to read more data.
146 while (vhigh_ > low_ + scaled && vlow_ <= low_ + scaled) {
147 // We need more information to disambiguate this. Note that 32-bits of
148 // information is always sufficient to disambiguate.
149 uint32 b = 0;
150 if (!in_.empty())
151 b = static_cast<uint8>(in_[0]);
152 in_.remove_prefix(1);
153 vhigh_ &= ~(static_cast<uint32>(0xff) << (24 - vbits_));
154 vhigh_ |= b << (24 - vbits_);
155 vlow_ |= b << (24 - vbits_);
156 vbits_ += 8;
157 }
158
159 // This symbol covers all the possible values for the final span, so this
160 // must be the next symbol.
161 if (vhigh_ <= low_ + scaled) {
162 high_ = low_ + scaled;
163 *out_symbol = i;
164 return true;
165 }
166
167 low_ += scaled + 1;
168 }
169
170 // Since the sum of |spans_| equals 2**32-1, one of the symbols must cover
171 // the current span.
172 NOTREACHED();
173 return false;
174 }
175
176 private:
177 base::StringPiece in_;
178 const std::vector<uint32> spans_;
Ryan Sleevi 2011/06/02 22:01:54 nit?: const std::vector<uint32>& , to further avoi
agl 2011/06/02 22:57:17 I'd rather reduce the surprise than save copying 1
179
180 uint32 high_, vhigh_, low_, vlow_;
181 unsigned vbits_;
182
183 DISALLOW_COPY_AND_ASSIGN(RangeDecoder);
184 };
185
186 // A GolombCompressedSet is built from a set of random hash values where each
187 // value is less than a pre-agreed limit. Since the hash values are uniform,
188 // their differences are geometrically distributed and golomb encoding is the
189 // optimal encoding for geometrically distributed values.
190 //
191 // Thus the set [1, 10, 15] is turned into delta values ([1, 9, 5]) and each
192 // delta value is Golomb encoded to make a GCS.
193 //
194 // Golomb encoding of a value, v, requires knowledge of the geometric
195 // parameter, M, and consists of (q, r) where v = qM + r. q is unary encoded
196 // and r is binary encoded. In this code M is fixed at 1024.
197 //
198 // A couple of implementation tricks are used to speed things up:
199 //
200 // First, the bits are consumed in blocks of 32 and are little endian encoded,
201 // thus saving a endianness conversion on most systems. Also, the bits inside
202 // each word are ordered such that the first bit is the least-significant bit
203 // and the unary encoding is terminated with a 1 rather than the usual 0.
204 // This allows us to use a DeBruijn sequence to do unary decoding.
205 class GolombCompressedSet {
206 public:
207 class iterator {
208 public:
209 iterator(base::StringPiece data, unsigned num_values)
Ryan Sleevi 2011/06/02 22:01:54 nit?: unsigned -> size_t || uint32 || uint64
210 : full_data_(data),
211 num_values_(num_values) {
212 Reset();
213 }
214
215 void Reset() {
216 data_ = full_data_;
217 pending_ = 0;
218 bits_pending_ = 0;
219 current_ = 0;
220 }
221
222 bool Next(uint64* out) {
223 unsigned q, r;
224 if (!ReadUnary(&q))
225 return false;
226 if (!ReadBinary10(&r))
227 return false;
228
229 uint64 step = static_cast<uint64>(q) << 10;
230 step |= r;
231 current_ += step;
232 *out = current_;
233 return true;
234 }
235
236 bool NextDelta(unsigned* out_delta) {
237 unsigned q, r;
238 if (!ReadUnary(&q))
239 return false;
240 if (!ReadBinary10(&r))
241 return false;
242
243 *out_delta = static_cast<unsigned>(q) << 10;
244 *out_delta |= r;
245 return true;
246 }
247
248 bool Contains(uint64 v) {
249 Reset();
250
251 uint64 value;
252 for (unsigned i = 0; i < num_values_; i++) {
253 if (!Next(&value))
254 return false;
255 if (value == v)
256 return true;
257 if (value > v)
258 return false;
259 }
260
261 return false;
262 }
263
264 private:
265 bool ReadUnary(unsigned* out) {
266 *out = 0;
267
268 uint32 w;
269 if (!CurrentWord(&w))
270 return false;
271
272 while (w == 0) {
273 *out += 32;
274 if (!CurrentWord(&w))
275 return false;
276 }
277
278 // A DeBruijn sequence contains all possible subsequences. kDeBruijn is an
279 // example of a 32-bit word that contains all possible 5-bit subsequences.
280 // When decoding Golomb values, we quickly need to find the number of
281 // consequtive zero bits. (w&-w) results in a word with only the
282 // least-significant true bit set. Since this work has only a single bit
283 // set, its value is a power of two and multiplying by it is the same as a
284 // left shift by the position of that bit.
285 //
286 // Thus we multiply (i.e. left-shift) by the DeBruijn value and check the
287 // top 5 bits. Since each 5-bit subsequence in kDeBruijn is unique, we can
288 // determine by how many bits it has been shifted with a lookup table.
289 static const uint32 kDeBruijn = 0x077CB531;
290 static const uint8 kDeBruijnLookup[32] = {
291 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
292 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9,
293 };
294
295 uint8 r = kDeBruijnLookup[((w&-w) * kDeBruijn) >> 27];
Mike Belshe 2011/06/02 18:11:25 nit: add spacing between operators: (w & -w)
agl 2011/06/02 21:04:39 Done.
296 *out += r;
297 pending_ >>= r + 1;
298 bits_pending_ -= r + 1;
299 return true;
300 }
301
302 bool ReadBinary10(unsigned* out) {
303 uint32 w;
304 if (!CurrentWord(&w))
305 return false;
306 *out = w & 0x3ff;
307 pending_ >>= 10;
308 bits_pending_ -= 10;
309 return true;
310 }
311
312 bool CurrentWord(uint32* out) {
313 if (bits_pending_ < 32) {
314 if (!ReadWord() && bits_pending_ == 0)
315 return false;
316 }
317 *out = static_cast<uint32>(pending_);
318 return true;
319 }
320
321 bool ReadWord() {
322 DCHECK_LE(bits_pending_, 32u);
323
324 uint32 w;
Ryan Sleevi 2011/06/02 22:01:54 nit?: Move down two lines, between line 326 and 32
agl 2011/06/02 22:57:17 Done.
325 if (data_.size() < 4)
326 return false;
327 memcpy(&w, data_.data(), 4);
328 data_.remove_prefix(4);
329
330 uint64 w64 = w;
331 w64 <<= bits_pending_;
332 pending_ |= w64;
333 bits_pending_ += 32;
334 return true;
335 }
336
337 base::StringPiece full_data_;
338 base::StringPiece data_;
339 const unsigned num_values_;
340 uint64 pending_;
341 unsigned bits_pending_;
342 uint32 current_;
343 };
344
345 GolombCompressedSet(base::StringPiece data,
346 unsigned num_values)
347 : full_data_(data),
348 num_values_(num_values) {
349 }
350
351 iterator begin() const {
352 return iterator(full_data_, num_values_);
353 }
354
355 private:
356
Ryan Sleevi 2011/06/02 22:01:54 nit: Delete this blank line
agl 2011/06/02 22:57:17 Done.
357 base::StringPiece full_data_;
358 const unsigned num_values_;
359
360 DISALLOW_COPY_AND_ASSIGN(GolombCompressedSet);
361 };
362
363 // BitWriter buffers a number of bits in a format that matches
364 // GolombCompressedSet's expectations: the bits are packed least-significant
365 // first in little-endian, 32-bit words.
366 class BitWriter {
367 public:
368 BitWriter()
369 : buf_(NULL),
370 buf_len_(0),
371 buf_used_(0),
372 current_(0),
373 num_bits_(0) {
374 }
375
376 void WriteBit(bool b) {
377 current_ >>= 1;
378 if (b)
379 current_ |= 0x80000000u;
380 num_bits_++;
381
382 if (num_bits_ == sizeof(current_) * 8)
383 Flush();
384 }
385
386 // WriteGolomb10 outputs v using Golomb encoding with a geometric parameter
387 // of 1024.
388 void WriteGolomb10(unsigned v) {
389 const unsigned q = v >> 10;
390 unsigned r = v & 0x3ff;
391
392 for (unsigned i = 0; i < q; i++)
393 WriteBit(false);
394 WriteBit(true);
395 for (unsigned i = 0; i < 10; i++) {
396 WriteBit((r&1) == 1);
397 r >>= 1;
398 }
399 }
400
401 void Flush() {
402 if (num_bits_ > 0) {
Ryan Sleevi 2011/06/02 22:01:54 nit: Inconsistent bracing with rest of the file's
agl 2011/06/02 22:57:17 Done.
403 current_ >>= 32 - num_bits_;
404 }
405
406 if (buf_len_ < buf_used_ + sizeof(current_)) {
407 if (buf_) {
408 buf_len_ += sizeof(current_);
409 buf_len_ *= 2;
410 buf_ = reinterpret_cast<uint8*>(realloc(buf_, buf_len_));
411 } else {
412 buf_len_ = 1024;
413 buf_ = reinterpret_cast<uint8*>(malloc(buf_len_));
414 }
415 }
416 // assumes little endian
417 memcpy(buf_ + buf_used_, &current_, sizeof(current_));
418 buf_used_ += sizeof(current_);
419
420 current_ = 0;
421 num_bits_ = 0;
422 }
423
424 std::string as_string() {
425 Flush();
426 return std::string(reinterpret_cast<char*>(buf_), buf_used_);
427 }
428
429 private:
430 uint8* buf_;
Ryan Sleevi 2011/06/02 22:01:54 LEAK: buf_ is leaked (allocated on lines 410 || 41
agl 2011/06/02 22:57:17 Thanks!
431 unsigned buf_len_;
432 unsigned buf_used_;
Ryan Sleevi 2011/06/02 22:01:54 nit: size_t for bytes in memory, http://www.chromi
agl 2011/06/02 22:57:17 Done.
433 uint32 current_;
434 unsigned num_bits_;
Ryan Sleevi 2011/06/02 22:01:54 DISALLOW_COPY_AND_ASSIGN(BitWriter)
agl 2011/06/02 22:57:17 Done.
435 };
436
437 CRLFilter::~CRLFilter() {
438 }
439
440 // CRL filter format:
441 //
442 // uint16le description_len
443 // byte[description_len] description_bytes
444 // byte[] compressed_header
445 // byte[] gcs_bytes
446 //
447 // description_bytes consists of a JSON dictionary with the following keys:
448 // Version (int): currently 0
449 // Contents (string): "CRLFilter" or "CRLFilterDelta" (magic value)
450 // DeltaFrom (int); if this is a delta filter (see below), then this contains
451 // the sequence number of the reference filter.
452 // HeaderZLength (int): the number of bytes of compressed header.
453 // HeaderLength (int): the number of bytes of header after decompression.
454 // RangeLength (int): if this is a delta filter then this is the number of
455 // bytes of range coded data.
456 //
457 // The uncompressed header is also a JSON dictionary with the following keys:
458 // Sequence (int): the sequence number of this filter.
459 // Version (int): currently 0.
460 // NotBefore (int, epoch seconds): the filter is not valid before this time.
461 // NotAfter (int, epoch seconds): the filter is not valid after this time.
462 // MaxRange (int): the limit of the GCS encoded values
463 // NumEntries (int): the number of GCS entries
464 //
465 // CRLsIncluded (array): the covered CRLs. Each element in the array is a
466 // dictionary with the following keys:
467 //
468 // URL (string): the URL of the CRL
469 // ParentSPKISHA256 (string): base64 encoded, SHA256 hash of the CRL
470 // signer's SPKI.
471 //
472 // A delta CRL filter is similar to a CRL filter:
473 //
474 // uint16le description_len
475 // byte[description_len] description_bytes
476 // byte[] delta_compressed_header
477 // uint32le[3] range_probabilities
478 // byte[] range_bytes
479 // byte[] gcs_bytes
480 //
481 // A delta CRL filter applies to a specific CRL filter as given in the
482 // description's "DeltaFrom" value. The compressed header is compressed with
483 // the header bytes of the base CRL filter given as a zlib preshared
484 // dictionary.
485 //
486 // range_probabilities contains the probabilies of the three encoded symbols.
487 // The sum of these values must be 0xffffffff. Next are the range encoded
488 // bytes, the length of which is given in "RangeLength". There's one symbol for
489 // each GCS value in the final filter. (This number is given in the
490 // "NumEntries" value of the header.). Each symbol is either SAME (0), INSERT
491 // (1) or DELETE (2). SAME values are copied into the new filter, INSERTed
492 // values are given as a delta from the last value, GCS encoded in |gcs_bytes|.
493 // DELETEed values are omitted from the final filter.
494
495 // ReadDescription reads the description (including length prefix) from |data|
496 // and updates |data| to remove the description on return. Caller takes
497 // ownership of the returned pointer.
498 static DictionaryValue* ReadDescription(base::StringPiece* data) {
499 if (data->size() < 2)
500 return NULL;
501 uint16 description_len;
502 memcpy(&description_len, data->data(), 2); // assumes little-endian.
503 data->remove_prefix(2);
504
505 if (data->size() < description_len)
506 return NULL;
507
508 const base::StringPiece description_bytes(data->data(), description_len);
509 data->remove_prefix(description_len);
510
511 scoped_ptr<Value> description(base::JSONReader::Read(
512 description_bytes.as_string(), true /* allow trailing comma */));
513 if (description.get() == NULL)
514 return NULL;
515
516 if (!description->IsType(Value::TYPE_DICTIONARY))
517 return NULL;
518 return reinterpret_cast<DictionaryValue*>(description.release());
519 }
520
521 // CRLFilterFromHeader constructs a CRLFilter from the bytes of a header
522 // structures. The header is JSON. See above for details of the keys.
523 //
524 // static
525 CRLFilter* CRLFilter::CRLFilterFromHeader(base::StringPiece header_bytes) {
526 scoped_ptr<Value> header(base::JSONReader::Read(
527 header_bytes.as_string(),
528 true /* allow trailing comma */));
529 if (header.get() == NULL)
530 return NULL;
531
532 if (!header->IsType(Value::TYPE_DICTIONARY))
533 return NULL;
534 DictionaryValue* header_dict =
535 reinterpret_cast<DictionaryValue*>(header.get());
536 int version;
537 if (!header_dict->GetInteger("Version", &version) ||
538 version != 0) {
539 return NULL;
540 }
541
542 double not_before, not_after, max_range, num_entries;
543 if (!header_dict->GetDouble("NotBefore", &not_before) ||
544 !header_dict->GetDouble("NotAfter", &not_after) ||
545 !header_dict->GetDouble("NumEntries", &num_entries) ||
546 !header_dict->GetDouble("MaxRange", &max_range)) {
547 return NULL;
548 }
549
550 if (not_before <= 0 || not_after <= 0 || max_range <= 0 || num_entries <= 0)
551 return NULL;
552
553 int sequence;
554 if (!header_dict->GetInteger("Sequence", &sequence) ||
555 sequence <= 0) {
556 // Sequence is assumed to be zero if omitted.
557 sequence = 0;
558 }
559
560 scoped_ptr<CRLFilter> crl_filter(new CRLFilter);
561 crl_filter->sequence_ = sequence;
562 crl_filter->not_before_ = not_before;
563 crl_filter->not_after_ = not_after;
564 crl_filter->max_range_ = max_range;
565 crl_filter->num_entries_ = num_entries;
566 crl_filter->header_bytes_ = header_bytes.as_string();
567
568 ListValue* crls_included;
569 if (!header_dict->GetList("CRLsIncluded", &crls_included))
570 return NULL;
571
572 for (size_t i = 0; i < crls_included->GetSize(); i++) {
Ryan Sleevi 2011/06/02 22:01:54 world's smallest nit, part 2: ++i
573 DictionaryValue* included_crl_dict;
574 if (!crls_included->GetDictionary(i, &included_crl_dict))
575 return NULL;
576 std::string url, parent_spki_sha256_b64;
577 if (!included_crl_dict->GetString("URL", &url) ||
578 !included_crl_dict->GetString("ParentSPKISHA256",
579 &parent_spki_sha256_b64)) {
580 return NULL;
581 }
582
583 std::string parent_spki_sha256;
584 if (!base::Base64Decode(parent_spki_sha256_b64,
585 &parent_spki_sha256)) {
586 return NULL;
587 }
588 crl_filter->crls_included_.insert(
589 std::make_pair<std::string, std::string>(
590 url,
Ryan Sleevi 2011/06/02 22:01:54 nit: 2 spaces -> 4 spaces
agl 2011/06/02 22:57:17 Done.
591 parent_spki_sha256));
592 }
593
594 return crl_filter.release();
595 }
596
597 // kMaxHeaderLengthBytes contains the sanity limit of the size of a CRL
598 // filter's decompressed header.
599 static const int kMaxHeaderLengthBytes = 1024 * 1024;
600
601 // static
602 CRLFilter* CRLFilter::Parse(base::StringPiece data) {
603 // Other parts of Chrome assume that we're little endian, so we don't lose
604 // anything by doing this.
605 #if defined(__BYTE_ORDER)
606 // Linux check
607 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN,
608 datapack_assumes_little_endian);
609 #elif defined(__BIG_ENDIAN__)
610 // Mac check
611 #error DataPack assumes little endian
612 #endif
613
614 scoped_ptr<DictionaryValue> description_dict(
615 ReadDescription(&data));
616 if (!description_dict.get())
617 return NULL;
618
619 std::string contents;
620 if (!description_dict->GetString("Contents", &contents))
621 return NULL;
622 if (contents != "CRLFilter")
623 return NULL;
624
625 int version;
626 if (!description_dict->GetInteger("Version", &version) ||
627 version != 0) {
628 return NULL;
629 }
630
631 int compressed_header_len;
632 if (!description_dict->GetInteger("HeaderZLength", &compressed_header_len))
633 return NULL;
634
635 if (compressed_header_len <= 0 ||
636 data.size() < static_cast<unsigned>(compressed_header_len)) {
637 return NULL;
638 }
639 const base::StringPiece compressed_header(data.data(), compressed_header_len);
640 data.remove_prefix(compressed_header_len);
641
642 int header_len;
643 if (!description_dict->GetInteger("HeaderLength", &header_len))
644 return NULL;
645 if (header_len < 0 || header_len > kMaxHeaderLengthBytes)
Mike Belshe 2011/06/02 18:11:25 You could use DCHECKs or CHECKs here. If we aren'
agl 2011/06/02 21:04:39 I've added a NOTREACHED. I didn't want to add CHEC
646 return NULL;
647
648 scoped_array<char> header_bytes(new char[header_len]);
649 base::StringPiece no_dict;
650 if (!DecompressZlib(header_bytes.get(), header_len, compressed_header,
651 no_dict)) {
652 return NULL;
653 }
654
655 scoped_refptr<CRLFilter> crl_filter(CRLFilterFromHeader(
656 base::StringPiece(header_bytes.get(), header_len)));
657
658 if (!crl_filter.get())
659 return NULL;
660
661 // The remainder is the Golomb Compressed Set.
662 crl_filter->gcs_bytes_ = data.as_string();
663 crl_filter->gcs_.reset(new GolombCompressedSet(crl_filter->gcs_bytes_,
664 crl_filter->num_entries_));
665 return crl_filter.release();
666 }
667
668 CRLFilter* CRLFilter::ApplyDelta(base::StringPiece data) {
669 scoped_ptr<DictionaryValue> description_dict(
670 ReadDescription(&data));
671 if (!description_dict.get())
672 return NULL;
673
674 int compressed_header_len, header_len, delta_from, version, range_length;
675 std::string contents;
676 if (!description_dict->GetInteger("HeaderZLength", &compressed_header_len) ||
677 !description_dict->GetInteger("HeaderLength", &header_len) ||
678 !description_dict->GetInteger("RangeLength", &range_length) ||
679 !description_dict->GetInteger("DeltaFrom", &delta_from) ||
680 !description_dict->GetInteger("Version", &version) ||
681 !description_dict->GetString("Contents", &contents)) {
682 return NULL;
683 }
684
685 if (version != 0 || contents != "CRLFilterDelta")
686 return NULL;
687
688 if (delta_from < 0 || static_cast<unsigned>(delta_from) != sequence_)
689 return NULL;
690
691 if (compressed_header_len <= 0 ||
692 data.size() < static_cast<unsigned>(compressed_header_len) ||
693 header_len < 0 ||
694 header_len > kMaxHeaderLengthBytes) {
695 return NULL;
696 }
697
698 const base::StringPiece compressed_header(data.data(), compressed_header_len);
699 data.remove_prefix(compressed_header_len);
700
701 scoped_array<char> header_bytes(new char[header_len]);
702 if (!DecompressZlib(header_bytes.get(), header_len, compressed_header,
703 header_bytes_)) {
704 return NULL;
705 }
706
707 scoped_refptr<CRLFilter> crl_filter(CRLFilterFromHeader(
708 base::StringPiece(header_bytes.get(), header_len)));
709
710 if (!crl_filter.get())
711 return NULL;
712
713 // Next are the three span values.
714 static const unsigned num_span_values = 3;
Ryan Sleevi 2011/06/02 22:01:54 nit: constant name http://google-styleguide.googl
agl 2011/06/02 22:57:17 Done.
715 if (data.size() < num_span_values * sizeof(uint32))
Ryan Sleevi 2011/06/02 22:01:54 nit: Use a variable for num_span_values * sizeof(u
agl 2011/06/02 22:57:17 Done.
716 return NULL;
717
718 std::vector<uint32> spans(num_span_values);
719 memcpy(&spans[0], data.data(), num_span_values * sizeof(uint32));
720 data.remove_prefix(num_span_values * sizeof(uint32));
721
722 if (data.size() < static_cast<unsigned>(range_length))
723 return NULL;
724 RangeDecoder decoder(data.substr(0, range_length), spans);
725 data.remove_prefix(range_length);
726
727 GolombCompressedSet gcs(data, 0 /* no values; we don't know that yet. */);
728 GolombCompressedSet::iterator gcs_deltas(gcs.begin());
729 GolombCompressedSet::iterator gcs_prev(gcs_->begin());
730 BitWriter bitwriter;
731
732 uint64 last = 0, v;
733 for (unsigned i = 0; i < crl_filter->num_entries_;) {
734 unsigned symbol, delta;
735 if (!decoder.Decode(&symbol))
736 return NULL;
737 if (symbol == SYMBOL_SAME) {
738 if (!gcs_prev.Next(&v))
739 return NULL;
740 bitwriter.WriteGolomb10(v - last);
741 last = v;
742 i++;
743 } else if (symbol == SYMBOL_INSERT) {
744 if (!gcs_deltas.NextDelta(&delta))
745 return NULL;
746 bitwriter.WriteGolomb10(delta);
747 last += delta;
748 i++;
749 } else if (symbol == SYMBOL_DELETE) {
750 if (!gcs_prev.Next(&v))
751 return NULL;
752 } else {
753 NOTREACHED();
754 return NULL;
755 }
756 }
757
758 crl_filter->gcs_bytes_ = bitwriter.as_string();
759 crl_filter->gcs_.reset(new GolombCompressedSet(crl_filter->gcs_bytes_,
760 crl_filter->num_entries_));
761 return crl_filter.release();
762 }
763
764 bool CRLFilter::CRLIsCovered(
765 std::vector<base::StringPiece> crl_urls,
766 const std::string& parent_spki_sha256) {
767 for (std::vector<base::StringPiece>::const_iterator
768 i = crl_urls.begin(); i != crl_urls.end(); i++) {
769 if (crls_included_.count(std::make_pair<std::string, std::string>(
Ryan Sleevi 2011/06/02 22:01:54 nit: base/stl_util-inl.h has ContainsKey, which is
agl 2011/06/02 22:57:17 Done.
770 i->as_string(), parent_spki_sha256))) {
771 return true;
772 }
773 }
774 return false;
775 }
776
777 // FNV1a64 computes the FNV1a 64-bit hash of the concatenation of |a| and
778 // |b|.
779 static uint64 FNV1a64(const std::string& a, const std::string& b) {
780 uint64 x = 14695981039346656037ul;
781 static const uint64 p = 1099511628211;
782 for (size_t i = 0; i < a.size(); i++) {
783 x ^= static_cast<uint8>(a[i]);
784 x *= p;
785 }
786 for (size_t i = 0; i < b.size(); i++) {
787 x ^= static_cast<uint8>(b[i]);
788 x *= p;
789 }
790 return x;
791 }
792
793 CRLFilter::Result CRLFilter::CheckCertificate(
794 base::StringPiece cert_spki,
795 const std::string& serial_number,
796 std::vector<base::StringPiece> crl_urls,
797 base::StringPiece parent_spki) {
798 const std::string parent_spki_sha256 =
799 crypto::SHA256HashString(parent_spki.as_string());
800
801 if (!CRLIsCovered(crl_urls, parent_spki_sha256))
802 return UNKNOWN;
803
804 uint64 h = FNV1a64(serial_number, parent_spki_sha256);
805 h %= max_range_;
806
807 GolombCompressedSet::iterator it(gcs_->begin());
808 if (it.Contains(h))
809 return PROBABLY_REVOKED;
810 return NOT_REVOKED;
811 }
812
813 int64 CRLFilter::not_before() const {
814 return not_before_;
815 }
816
817 int64 CRLFilter::not_after() const {
818 return not_after_;
819 }
820
821 uint64 CRLFilter::max_range() const {
822 return max_range_;
823 }
824
825 unsigned CRLFilter::num_entries() const {
826 return num_entries_;
827 }
828
829 std::vector<uint64> CRLFilter::DebugValues() {
830 std::vector<uint64> ret;
831 uint64 v;
832
833 GolombCompressedSet::iterator it(gcs_->begin());
834
835 for (unsigned i = 0; i < num_entries_; i++) {
836 if (!it.Next(&v)) {
837 ret.clear();
838 break;
839 }
840 ret.push_back(v);
841 }
842 return ret;
843 }
844
845 std::string CRLFilter::SHA256() const {
846 std::string s = header_bytes_;
847 s += gcs_bytes_;
848 return crypto::SHA256HashString(s);
849 }
850
851 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698