summaryrefslogtreecommitdiff
path: root/chromium/components/cast_certificate/cast_crl.cc
blob: fa95ae33083063606cfcccde01c24ed69efc1e52 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/cast_certificate/cast_crl.h"

#include <unordered_map>
#include <unordered_set>

#include "base/base64.h"
#include "base/memory/ptr_util.h"
#include "base/memory/singleton.h"
#include "components/cast_certificate/proto/revocation.pb.h"
#include "crypto/sha2.h"
#include "net/cert/internal/cert_errors.h"
#include "net/cert/internal/parse_certificate.h"
#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/internal/path_builder.h"
#include "net/cert/internal/signature_algorithm.h"
#include "net/cert/internal/signature_policy.h"
#include "net/cert/internal/trust_store_in_memory.h"
#include "net/cert/internal/verify_certificate_chain.h"
#include "net/cert/internal/verify_signed_data.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/der/encode_values.h"
#include "net/der/input.h"
#include "net/der/parse_values.h"
#include "net/der/parser.h"

namespace cast_certificate {
namespace {

enum CrlVersion {
  // version 0: Spki Hash Algorithm = SHA-256
  //            Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256
  CRL_VERSION_0 = 0,
};

// -------------------------------------------------------------------------
// Cast CRL trust anchors.
// -------------------------------------------------------------------------

// There is one trusted root for Cast CRL certificate chains:
//
//   (1) CN=Cast CRL Root CA    (kCastCRLRootCaDer)
//
// These constants are defined by the file included next:

#include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h"

// Singleton for the Cast CRL trust store.
class CastCRLTrustStore {
 public:
  static CastCRLTrustStore* GetInstance() {
    return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits<
                                                  CastCRLTrustStore>>::get();
  }

  static net::TrustStore& Get() { return GetInstance()->store_; }

 private:
  friend struct base::DefaultSingletonTraits<CastCRLTrustStore>;

  CastCRLTrustStore() {
    // Initialize the trust store with the root certificate.
    net::CertErrors errors;
    scoped_refptr<net::ParsedCertificate> cert =
        net::ParsedCertificate::CreateWithoutCopyingUnsafe(
            kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer), {}, &errors);
    CHECK(cert) << errors.ToDebugString();
    // Enforce pathlen constraints and policies defined on the root certificate.
    scoped_refptr<net::TrustAnchor> anchor =
        net::TrustAnchor::CreateFromCertificateWithConstraints(std::move(cert));
    CHECK(anchor);
    store_.AddTrustAnchor(std::move(anchor));
  }

  net::TrustStoreInMemory store_;
  DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore);
};

// Converts a uint64_t unix timestamp to net::der::GeneralizedTime.
bool ConvertTimeSeconds(uint64_t seconds,
                        net::der::GeneralizedTime* generalized_time) {
  base::Time unix_timestamp =
      base::Time::UnixEpoch() +
      base::TimeDelta::FromSeconds(base::saturated_cast<int64_t>(seconds));
  return net::der::EncodeTimeAsGeneralizedTime(unix_timestamp,
                                               generalized_time);
}

// Specifies the signature verification policy.
// The required algorithms are:
// RSASSA PKCS#1 v1.5 with SHA-256, using RSA keys 2048-bits or longer.
std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
  return base::MakeUnique<net::SimpleSignaturePolicy>(2048);
}

// Verifies the CRL is signed by a trusted CRL authority at the time the CRL
// was issued. Verifies the signature of |tbs_crl| is valid based on the
// certificate and signature in |crl|. The validity of |tbs_crl| is verified
// at |time|. The validity period of the CRL is adjusted to be the earliest
// of the issuer certificate chain's expiration and the CRL's expiration and
// the result is stored in |overall_not_after|.
bool VerifyCRL(const Crl& crl,
               const TbsCrl& tbs_crl,
               const base::Time& time,
               net::TrustStore* trust_store,
               net::der::GeneralizedTime* overall_not_after) {
  // Verify the trust of the CRL authority.
  net::CertErrors parse_errors;
  scoped_refptr<net::ParsedCertificate> parsed_cert =
      net::ParsedCertificate::Create(
          net::x509_util::CreateCryptoBuffer(crl.signer_cert()), {},
          &parse_errors);
  if (parsed_cert == nullptr) {
    VLOG(2) << "CRL - Issuer certificate parsing failed:\n"
            << parse_errors.ToDebugString();
    return false;
  }

  // Wrap the signature in a BitString.
  net::der::BitString signature_value_bit_string = net::der::BitString(
      net::der::Input(base::StringPiece(crl.signature())), 0);

  // Verify the signature.
  auto signature_policy = CreateCastSignaturePolicy();
  std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type =
      net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256);
  net::CertErrors verify_errors;
  if (!VerifySignedData(*signature_algorithm_type,
                        net::der::Input(&crl.tbs_crl()),
                        signature_value_bit_string, parsed_cert->tbs().spki_tlv,
                        signature_policy.get(), &verify_errors)) {
    VLOG(2) << "CRL - Signature verification failed:\n"
            << verify_errors.ToDebugString();
    return false;
  }

  // Verify the issuer certificate.
  net::der::GeneralizedTime verification_time;
  if (!net::der::EncodeTimeAsGeneralizedTime(time, &verification_time)) {
    VLOG(2) << "CRL - Unable to parse verification time.";
    return false;
  }
  net::CertPathBuilder::Result result;
  net::CertPathBuilder path_builder(parsed_cert.get(), trust_store,
                                    signature_policy.get(), verification_time,
                                    &result);
  path_builder.Run();
  if (!result.HasValidPath()) {
    VLOG(2) << "CRL - Issuer certificate verification failed.";
    // TODO(crbug.com/634443): Log the error information.
    return false;
  }
  // There are no requirements placed on the leaf certificate having any
  // particular KeyUsages. Leaf certificate checks are bypassed.

  // Verify the CRL is still valid.
  net::der::GeneralizedTime not_before;
  if (!ConvertTimeSeconds(tbs_crl.not_before_seconds(), &not_before)) {
    VLOG(2) << "CRL - Unable to parse not_before.";
    return false;
  }
  net::der::GeneralizedTime not_after;
  if (!ConvertTimeSeconds(tbs_crl.not_after_seconds(), &not_after)) {
    VLOG(2) << "CRL - Unable to parse not_after.";
    return false;
  }
  if ((verification_time < not_before) || (verification_time > not_after)) {
    VLOG(2) << "CRL - Not time-valid.";
    return false;
  }

  // Set CRL expiry to the earliest of the cert chain expiry and CRL expiry.
  // Note that the trust anchor is not part of this loop.
  // "expiration" of the trust anchor is handled instead by its
  // presence in the trust store.
  *overall_not_after = not_after;
  for (const auto& cert : result.GetBestValidPath()->path.certs) {
    net::der::GeneralizedTime cert_not_after = cert->tbs().validity_not_after;
    if (cert_not_after < *overall_not_after)
      *overall_not_after = cert_not_after;
  }

  // Perform sanity check on serial numbers.
  for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
    uint64_t first_serial_number = range.first_serial_number();
    uint64_t last_serial_number = range.last_serial_number();
    if (last_serial_number < first_serial_number) {
      VLOG(2) << "CRL - Malformed serial number range.";
      return false;
    }
  }
  return true;
}

class CastCRLImpl : public CastCRL {
 public:
  CastCRLImpl(const TbsCrl& tbs_crl,
              const net::der::GeneralizedTime& overall_not_after);
  ~CastCRLImpl() override;

  bool CheckRevocation(const net::CertPath& trusted_chain,
                       const base::Time& time) const override;

 private:
  struct SerialNumberRange {
    uint64_t first_serial;
    uint64_t last_serial;
  };

  net::der::GeneralizedTime not_before_;
  net::der::GeneralizedTime not_after_;

  // Revoked public key hashes.
  // The values consist of the SHA256 hash of the SubjectPublicKeyInfo.
  std::unordered_set<std::string> revoked_hashes_;

  // Revoked serial number ranges indexed by issuer public key hash.
  // The key is the SHA256 hash of issuer's SubjectPublicKeyInfo.
  // The value is a list of revoked serial number ranges.
  std::unordered_map<std::string, std::vector<SerialNumberRange>>
      revoked_serial_numbers_;
  DISALLOW_COPY_AND_ASSIGN(CastCRLImpl);
};

CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl,
                         const net::der::GeneralizedTime& overall_not_after) {
  // Parse the validity information.
  // Assume ConvertTimeSeconds will succeed. Successful call to VerifyCRL
  // means that these calls were successful.
  ConvertTimeSeconds(tbs_crl.not_before_seconds(), &not_before_);
  ConvertTimeSeconds(tbs_crl.not_after_seconds(), &not_after_);
  if (overall_not_after < not_after_)
    not_after_ = overall_not_after;

  // Parse the revoked hashes.
  for (const auto& hash : tbs_crl.revoked_public_key_hashes()) {
    revoked_hashes_.insert(hash);
  }

  // Parse the revoked serial ranges.
  for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
    std::string issuer_hash = range.issuer_public_key_hash();

    uint64_t first_serial_number = range.first_serial_number();
    uint64_t last_serial_number = range.last_serial_number();
    auto& serial_number_range = revoked_serial_numbers_[issuer_hash];
    serial_number_range.push_back({first_serial_number, last_serial_number});
  }
}

CastCRLImpl::~CastCRLImpl() {}

// Verifies the revocation status of the certificate chain, at the specified
// time.
bool CastCRLImpl::CheckRevocation(const net::CertPath& trusted_chain,
                                  const base::Time& time) const {
  if (trusted_chain.IsEmpty())
    return false;

  DCHECK(trusted_chain.trust_anchor);

  // Check the validity of the CRL at the specified time.
  net::der::GeneralizedTime verification_time;
  if (!net::der::EncodeTimeAsGeneralizedTime(time, &verification_time)) {
    VLOG(2) << "CRL verification time malformed.";
    return false;
  }
  if ((verification_time < not_before_) || (verification_time > not_after_)) {
    VLOG(2) << "CRL not time-valid. Perform hard fail.";
    return false;
  }

  // Check revocation. Note that this loop has "+ 1" in order to also loop
  // over the trust anchor (which is treated specially).
  for (size_t i = 0; i < trusted_chain.certs.size() + 1; ++i) {
    // This loop iterates over both certificates AND then the trust
    // anchor after exhausing the certs.
    net::der::Input spki_tlv;
    if (i == trusted_chain.certs.size()) {
      spki_tlv = trusted_chain.trust_anchor->spki();
    } else {
      spki_tlv = trusted_chain.certs[i]->tbs().spki_tlv;
    }

    // Calculate the public key's hash to check for revocation.
    std::string spki_hash = crypto::SHA256HashString(spki_tlv.AsString());
    if (revoked_hashes_.find(spki_hash) != revoked_hashes_.end()) {
      VLOG(2) << "Public key is revoked.";
      return false;
    }

    // Check if the subordinate certificate was revoked by serial number.
    if (i > 0) {
      auto issuer_iter = revoked_serial_numbers_.find(spki_hash);
      if (issuer_iter != revoked_serial_numbers_.end()) {
        const auto& subordinate = trusted_chain.certs[i - 1];
        uint64_t serial_number;
        // Only Google generated device certificates will be revoked by range.
        // These will always be less than 64 bits in length.
        if (!net::der::ParseUint64(subordinate->tbs().serial_number,
                                   &serial_number)) {
          continue;
        }
        for (const auto& revoked_serial : issuer_iter->second) {
          if (revoked_serial.first_serial <= serial_number &&
              revoked_serial.last_serial >= serial_number) {
            VLOG(2) << "Serial number is revoked";
            return false;
          }
        }
      }
    }
  }
  return true;
}

}  // namespace

std::unique_ptr<CastCRL> ParseAndVerifyCRL(const std::string& crl_proto,
                                           const base::Time& time) {
  return ParseAndVerifyCRLUsingCustomTrustStore(crl_proto, time,
                                                &CastCRLTrustStore::Get());
}

std::unique_ptr<CastCRL> ParseAndVerifyCRLUsingCustomTrustStore(
    const std::string& crl_proto,
    const base::Time& time,
    net::TrustStore* trust_store) {
  if (!trust_store)
    return ParseAndVerifyCRL(crl_proto, time);

  CrlBundle crl_bundle;
  if (!crl_bundle.ParseFromString(crl_proto)) {
    LOG(ERROR) << "CRL - Binary could not be parsed.";
    return nullptr;
  }
  for (auto const& crl : crl_bundle.crls()) {
    TbsCrl tbs_crl;
    if (!tbs_crl.ParseFromString(crl.tbs_crl())) {
      LOG(WARNING) << "Binary TBS CRL could not be parsed.";
      continue;
    }
    if (tbs_crl.version() != CRL_VERSION_0) {
      continue;
    }
    net::der::GeneralizedTime overall_not_after;
    if (!VerifyCRL(crl, tbs_crl, time, trust_store, &overall_not_after)) {
      LOG(ERROR) << "CRL - Verification failed.";
      return nullptr;
    }
    return base::MakeUnique<CastCRLImpl>(tbs_crl, overall_not_after);
  }
  LOG(ERROR) << "No supported version of revocation data.";
  return nullptr;
}

}  // namespace cast_certificate