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/cert/crl_set.h"
6
7 #include <algorithm>
8
9 #include "base/base64.h"
10 #include "base/json/json_reader.h"
11 #include "base/sys_byteorder.h"
12 #include "base/time/time.h"
13 #include "base/trace_event/trace_event.h"
14 #include "base/values.h"
15 #include "crypto/sha2.h"
16 #include "net/base/trace_constants.h"
17 #include "third_party/boringssl/src/include/openssl/bytestring.h"
18 #include "third_party/boringssl/src/include/openssl/mem.h"
19
20 namespace net {
21
22 namespace {
23
24 // CRLSet format:
25 //
26 // uint16le header_len
27 // byte[header_len] header_bytes
28 // repeated {
29 // byte[32] parent_spki_sha256
30 // uint32le num_serials
31 // [num_serials] {
32 // uint8_t serial_length;
33 // byte[serial_length] serial;
34 // }
35 //
36 // header_bytes consists of a JSON dictionary with the following keys:
37 // Version (int): currently 0
38 // ContentType (string): "CRLSet" (magic value)
39 // Sequence (int32_t): the monotonic sequence number of this CRL set.
40 // NotAfter (optional) (double/int64_t): The number of seconds since the
41 // Unix epoch, after which, this CRLSet is expired.
42 // BlockedSPKIs (array of string): An array of Base64 encoded, SHA-256 hashed
43 // SubjectPublicKeyInfos that should be blocked.
44 // LimitedSubjects (object/map of string -> array of string): A map between
45 // the Base64-encoded SHA-256 hash of the DER-encoded Subject and the
46 // Base64-encoded SHA-256 hashes of the SubjectPublicKeyInfos that are
47 // allowed for that subject.
48 // KnownInterceptionSPKIs (array of string): An array of Base64-encoded
49 // SHA-256 hashed SubjectPublicKeyInfos known to be used for interception.
50 // BlockedInterceptionSPKIs (array of string): An array of Base64-encoded
51 // SHA-256 hashed SubjectPublicKeyInfos known to be used for interception
52 // and that should be actively blocked.
53 //
54 // ReadHeader reads the header (including length prefix) from |data| and
55 // updates |data| to remove the header on return. Caller takes ownership of the
56 // returned pointer.
ReadHeader(base::StringPiece * data)57 base::DictionaryValue* ReadHeader(base::StringPiece* data) {
58 uint16_t header_len;
59 if (data->size() < sizeof(header_len))
60 return nullptr;
61 memcpy(&header_len, data->data(), sizeof(header_len));
62 data->remove_prefix(sizeof(header_len));
63 header_len = base::ByteSwapToLE16(header_len);
64
65 if (data->size() < header_len)
66 return nullptr;
67
68 const base::StringPiece header_bytes(data->data(), header_len);
69 data->remove_prefix(header_len);
70
71 std::unique_ptr<base::Value> header = base::JSONReader::ReadDeprecated(
72 header_bytes, base::JSON_ALLOW_TRAILING_COMMAS);
73 if (header.get() == nullptr)
74 return nullptr;
75
76 if (!header->is_dict())
77 return nullptr;
78 return static_cast<base::DictionaryValue*>(header.release());
79 }
80
81 // kCurrentFileVersion is the version of the CRLSet file format that we
82 // currently implement.
83 static const int kCurrentFileVersion = 0;
84
ReadCRL(base::StringPiece * data,std::string * out_parent_spki_hash,std::vector<std::string> * out_serials)85 bool ReadCRL(base::StringPiece* data,
86 std::string* out_parent_spki_hash,
87 std::vector<std::string>* out_serials) {
88 if (data->size() < crypto::kSHA256Length)
89 return false;
90 out_parent_spki_hash->assign(data->data(), crypto::kSHA256Length);
91 data->remove_prefix(crypto::kSHA256Length);
92
93 uint32_t num_serials;
94 if (data->size() < sizeof(num_serials))
95 return false;
96 memcpy(&num_serials, data->data(), sizeof(num_serials));
97 data->remove_prefix(sizeof(num_serials));
98 num_serials = base::ByteSwapToLE32(num_serials);
99
100 if (num_serials > 32 * 1024 * 1024) // Sanity check.
101 return false;
102
103 out_serials->reserve(num_serials);
104
105 for (uint32_t i = 0; i < num_serials; ++i) {
106 if (data->size() < sizeof(uint8_t))
107 return false;
108
109 uint8_t serial_length = data->data()[0];
110 data->remove_prefix(sizeof(uint8_t));
111
112 if (data->size() < serial_length)
113 return false;
114
115 out_serials->push_back(std::string());
116 out_serials->back().assign(data->data(), serial_length);
117 data->remove_prefix(serial_length);
118 }
119
120 return true;
121 }
122
123 // CopyHashListFromHeader parses a list of base64-encoded, SHA-256 hashes from
124 // the given |key| in |header_dict| and sets |*out| to the decoded values. It's
125 // not an error if |key| is not found in |header_dict|.
CopyHashListFromHeader(base::DictionaryValue * header_dict,const char * key,std::vector<std::string> * out)126 bool CopyHashListFromHeader(base::DictionaryValue* header_dict,
127 const char* key,
128 std::vector<std::string>* out) {
129 base::ListValue* list = nullptr;
130 if (!header_dict->GetList(key, &list)) {
131 // Hash lists are optional so it's not an error if not present.
132 return true;
133 }
134
135 out->clear();
136 out->reserve(list->GetSize());
137
138 std::string sha256_base64;
139
140 for (size_t i = 0; i < list->GetSize(); ++i) {
141 sha256_base64.clear();
142
143 if (!list->GetString(i, &sha256_base64))
144 return false;
145
146 out->push_back(std::string());
147 if (!base::Base64Decode(sha256_base64, &out->back())) {
148 out->pop_back();
149 return false;
150 }
151 }
152
153 return true;
154 }
155
156 // CopyHashToHashesMapFromHeader parse a map from base64-encoded, SHA-256
157 // hashes to lists of the same, from the given |key| in |header_dict|. It
158 // copies the map data into |out| (after base64-decoding).
CopyHashToHashesMapFromHeader(base::DictionaryValue * header_dict,const char * key,std::unordered_map<std::string,std::vector<std::string>> * out)159 bool CopyHashToHashesMapFromHeader(
160 base::DictionaryValue* header_dict,
161 const char* key,
162 std::unordered_map<std::string, std::vector<std::string>>* out) {
163 out->clear();
164
165 base::Value* const dict =
166 header_dict->FindKeyOfType(key, base::Value::Type::DICTIONARY);
167 if (dict == nullptr) {
168 // Maps are optional so it's not an error if not present.
169 return true;
170 }
171
172 for (const auto& i : dict->DictItems()) {
173 if (!i.second.is_list()) {
174 return false;
175 }
176
177 std::vector<std::string> allowed_spkis;
178 for (const auto& j : i.second.GetList()) {
179 allowed_spkis.push_back(std::string());
180 if (!j.is_string() ||
181 !base::Base64Decode(j.GetString(), &allowed_spkis.back())) {
182 return false;
183 }
184 }
185
186 std::string subject_hash;
187 if (!base::Base64Decode(i.first, &subject_hash)) {
188 return false;
189 }
190
191 (*out)[subject_hash] = allowed_spkis;
192 }
193
194 return true;
195 }
196
197 } // namespace
198
CRLSet()199 CRLSet::CRLSet()
200 : sequence_(0),
201 not_after_(0) {
202 }
203
204 CRLSet::~CRLSet() = default;
205
206 // static
Parse(base::StringPiece data,scoped_refptr<CRLSet> * out_crl_set)207 bool CRLSet::Parse(base::StringPiece data, scoped_refptr<CRLSet>* out_crl_set) {
208 TRACE_EVENT0(NetTracingCategory(), "CRLSet::Parse");
209
210 std::unique_ptr<base::DictionaryValue> header_dict(ReadHeader(&data));
211 if (!header_dict.get())
212 return false;
213
214 std::string contents;
215 if (!header_dict->GetString("ContentType", &contents))
216 return false;
217 if (contents != "CRLSet")
218 return false;
219
220 int version;
221 if (!header_dict->GetInteger("Version", &version) ||
222 version != kCurrentFileVersion) {
223 return false;
224 }
225
226 int sequence;
227 if (!header_dict->GetInteger("Sequence", &sequence))
228 return false;
229
230 double not_after;
231 if (!header_dict->GetDouble("NotAfter", ¬_after)) {
232 // NotAfter is optional for now.
233 not_after = 0;
234 }
235 if (not_after < 0)
236 return false;
237
238 scoped_refptr<CRLSet> crl_set(new CRLSet());
239 crl_set->sequence_ = static_cast<uint32_t>(sequence);
240 crl_set->not_after_ = static_cast<uint64_t>(not_after);
241 crl_set->crls_.reserve(64); // Value observed experimentally.
242
243 for (size_t crl_index = 0; !data.empty(); crl_index++) {
244 std::string spki_hash;
245 std::vector<std::string> blocked_serials;
246
247 if (!ReadCRL(&data, &spki_hash, &blocked_serials)) {
248 return false;
249 }
250 crl_set->crls_[std::move(spki_hash)] = std::move(blocked_serials);
251 }
252
253 std::vector<std::string> blocked_interception_spkis;
254 if (!CopyHashListFromHeader(header_dict.get(), "BlockedSPKIs",
255 &crl_set->blocked_spkis_) ||
256 !CopyHashToHashesMapFromHeader(header_dict.get(), "LimitedSubjects",
257 &crl_set->limited_subjects_) ||
258 !CopyHashListFromHeader(header_dict.get(), "KnownInterceptionSPKIs",
259 &crl_set->known_interception_spkis_) ||
260 !CopyHashListFromHeader(header_dict.get(), "BlockedInterceptionSPKIs",
261 &blocked_interception_spkis)) {
262 return false;
263 }
264
265 // Add the BlockedInterceptionSPKIs to both lists; these are provided as
266 // a separate list to allow less data to be sent over the wire, even though
267 // they are duplicated in-memory.
268 crl_set->blocked_spkis_.insert(crl_set->blocked_spkis_.end(),
269 blocked_interception_spkis.begin(),
270 blocked_interception_spkis.end());
271 crl_set->known_interception_spkis_.insert(
272 crl_set->known_interception_spkis_.end(),
273 blocked_interception_spkis.begin(), blocked_interception_spkis.end());
274
275 // Defines kSPKIBlockList and kKnownInterceptionList
276 #include "net/cert/cert_verify_proc_blocklist.inc"
277 for (const auto& hash : kSPKIBlockList) {
278 crl_set->blocked_spkis_.push_back(std::string(
279 reinterpret_cast<const char*>(hash), crypto::kSHA256Length));
280 }
281
282 for (const auto& hash : kKnownInterceptionList) {
283 crl_set->known_interception_spkis_.push_back(std::string(
284 reinterpret_cast<const char*>(hash), crypto::kSHA256Length));
285 }
286
287 // Sort, as these will be std::binary_search()'d.
288 std::sort(crl_set->blocked_spkis_.begin(), crl_set->blocked_spkis_.end());
289 std::sort(crl_set->known_interception_spkis_.begin(),
290 crl_set->known_interception_spkis_.end());
291
292 *out_crl_set = std::move(crl_set);
293 return true;
294 }
295
CheckSPKI(const base::StringPiece & spki_hash) const296 CRLSet::Result CRLSet::CheckSPKI(const base::StringPiece& spki_hash) const {
297 if (std::binary_search(blocked_spkis_.begin(), blocked_spkis_.end(),
298 spki_hash))
299 return REVOKED;
300 return GOOD;
301 }
302
CheckSubject(const base::StringPiece & encoded_subject,const base::StringPiece & spki_hash) const303 CRLSet::Result CRLSet::CheckSubject(const base::StringPiece& encoded_subject,
304 const base::StringPiece& spki_hash) const {
305 const std::string digest(crypto::SHA256HashString(encoded_subject));
306 const auto i = limited_subjects_.find(digest);
307 if (i == limited_subjects_.end()) {
308 return GOOD;
309 }
310
311 for (const auto& j : i->second) {
312 if (spki_hash == j) {
313 return GOOD;
314 }
315 }
316
317 return REVOKED;
318 }
319
CheckSerial(const base::StringPiece & serial_number,const base::StringPiece & issuer_spki_hash) const320 CRLSet::Result CRLSet::CheckSerial(
321 const base::StringPiece& serial_number,
322 const base::StringPiece& issuer_spki_hash) const {
323 base::StringPiece serial(serial_number);
324
325 if (!serial.empty() && (serial[0] & 0x80) != 0) {
326 // This serial number is negative but the process which generates CRL sets
327 // will reject any certificates with negative serial numbers as invalid.
328 return UNKNOWN;
329 }
330
331 // Remove any leading zero bytes.
332 while (serial.size() > 1 && serial[0] == 0x00)
333 serial.remove_prefix(1);
334
335 auto it = crls_.find(issuer_spki_hash.as_string());
336 if (it == crls_.end())
337 return UNKNOWN;
338
339 for (const auto& issuer_serial : it->second) {
340 if (issuer_serial == serial)
341 return REVOKED;
342 }
343
344 return GOOD;
345 }
346
IsKnownInterceptionKey(base::StringPiece spki_hash) const347 bool CRLSet::IsKnownInterceptionKey(base::StringPiece spki_hash) const {
348 return std::binary_search(known_interception_spkis_.begin(),
349 known_interception_spkis_.end(), spki_hash);
350 }
351
IsExpired() const352 bool CRLSet::IsExpired() const {
353 if (not_after_ == 0)
354 return false;
355
356 uint64_t now = base::Time::Now().ToTimeT();
357 return now > not_after_;
358 }
359
sequence() const360 uint32_t CRLSet::sequence() const {
361 return sequence_;
362 }
363
CrlsForTesting() const364 const CRLSet::CRLList& CRLSet::CrlsForTesting() const {
365 return crls_;
366 }
367
368 // static
BuiltinCRLSet()369 scoped_refptr<CRLSet> CRLSet::BuiltinCRLSet() {
370 constexpr char kCRLSet[] =
371 "\x31\x00{\"ContentType\":\"CRLSet\",\"Sequence\":0,\"Version\":0}";
372 scoped_refptr<CRLSet> ret;
373 bool parsed = CRLSet::Parse({kCRLSet, sizeof(kCRLSet) - 1}, &ret);
374 DCHECK(parsed);
375 return ret;
376 }
377
378 // static
EmptyCRLSetForTesting()379 scoped_refptr<CRLSet> CRLSet::EmptyCRLSetForTesting() {
380 return ForTesting(false, nullptr, "", "", {});
381 }
382
383 // static
ExpiredCRLSetForTesting()384 scoped_refptr<CRLSet> CRLSet::ExpiredCRLSetForTesting() {
385 return ForTesting(true, nullptr, "", "", {});
386 }
387
388 // static
ForTesting(bool is_expired,const SHA256HashValue * issuer_spki,const std::string & serial_number,const std::string common_name,const std::vector<std::string> acceptable_spki_hashes_for_cn)389 scoped_refptr<CRLSet> CRLSet::ForTesting(
390 bool is_expired,
391 const SHA256HashValue* issuer_spki,
392 const std::string& serial_number,
393 const std::string common_name,
394 const std::vector<std::string> acceptable_spki_hashes_for_cn) {
395 std::string subject_hash;
396 if (!common_name.empty()) {
397 CBB cbb, top_level, set, inner_seq, oid, cn;
398 uint8_t* x501_data;
399 size_t x501_len;
400 static const uint8_t kCommonNameOID[] = {0x55, 0x04, 0x03}; // 2.5.4.3
401
402 CBB_zero(&cbb);
403
404 if (!CBB_init(&cbb, 32) ||
405 !CBB_add_asn1(&cbb, &top_level, CBS_ASN1_SEQUENCE) ||
406 !CBB_add_asn1(&top_level, &set, CBS_ASN1_SET) ||
407 !CBB_add_asn1(&set, &inner_seq, CBS_ASN1_SEQUENCE) ||
408 !CBB_add_asn1(&inner_seq, &oid, CBS_ASN1_OBJECT) ||
409 !CBB_add_bytes(&oid, kCommonNameOID, sizeof(kCommonNameOID)) ||
410 !CBB_add_asn1(&inner_seq, &cn, CBS_ASN1_PRINTABLESTRING) ||
411 !CBB_add_bytes(&cn,
412 reinterpret_cast<const uint8_t*>(common_name.data()),
413 common_name.size()) ||
414 !CBB_finish(&cbb, &x501_data, &x501_len)) {
415 CBB_cleanup(&cbb);
416 return nullptr;
417 }
418
419 subject_hash.assign(crypto::SHA256HashString(
420 base::StringPiece(reinterpret_cast<char*>(x501_data), x501_len)));
421 OPENSSL_free(x501_data);
422 }
423
424 scoped_refptr<CRLSet> crl_set(new CRLSet);
425 crl_set->sequence_ = 0;
426 if (is_expired)
427 crl_set->not_after_ = 1;
428
429 if (issuer_spki) {
430 const std::string spki(reinterpret_cast<const char*>(issuer_spki->data),
431 sizeof(issuer_spki->data));
432 std::vector<std::string> serials;
433 if (!serial_number.empty()) {
434 serials.push_back(serial_number);
435 // |serial_number| is in DER-encoded form, which means it may have a
436 // leading 0x00 to indicate it is a positive INTEGER. CRLSets are stored
437 // without these leading 0x00, as handled in CheckSerial(), so remove
438 // that here. As DER-encoding means that any sequences of leading zeroes
439 // should be omitted, except to indicate sign, there should only ever
440 // be one, and the next byte should have the high bit set.
441 DCHECK_EQ(serials[0][0] & 0x80, 0); // Negative serials are not allowed.
442 if (serials[0][0] == 0x00) {
443 serials[0].erase(0, 1);
444 // If there was a leading 0x00, then the high-bit of the next byte
445 // should have been set.
446 DCHECK(!serials[0].empty() && serials[0][0] & 0x80);
447 }
448 }
449
450 crl_set->crls_.emplace(std::move(spki), std::move(serials));
451 }
452
453 if (!subject_hash.empty())
454 crl_set->limited_subjects_[subject_hash] = acceptable_spki_hashes_for_cn;
455
456 return crl_set;
457 }
458
459 } // namespace net
460