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", &not_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