Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/pkix/test/gtest/pkixbuild_tests.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This code is made available to you under your choice of the following sets
4
 * of licensing terms:
5
 */
6
/* This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
 */
10
/* Copyright 2013 Mozilla Contributors
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 *     http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24
25
#if defined(_MSC_VER) && _MSC_VER < 1900
26
// When building with -D_HAS_EXCEPTIONS=0, MSVC's <xtree> header triggers
27
// warning C4702: unreachable code.
28
// https://connect.microsoft.com/VisualStudio/feedback/details/809962
29
#pragma warning(push)
30
#pragma warning(disable: 4702)
31
#endif
32
33
#include <map>
34
#include <vector>
35
36
#if defined(_MSC_VER) && _MSC_VER < 1900
37
#pragma warning(pop)
38
#endif
39
40
#include "pkixder.h"
41
#include "pkixgtest.h"
42
43
using namespace mozilla::pkix;
44
using namespace mozilla::pkix::test;
45
46
static ByteString
47
CreateCert(const char* issuerCN, // null means "empty name"
48
           const char* subjectCN, // null means "empty name"
49
           EndEntityOrCA endEntityOrCA,
50
           /*optional modified*/ std::map<ByteString, ByteString>*
51
             subjectDERToCertDER = nullptr,
52
           /*optional*/ const ByteString* extension = nullptr,
53
           /*optional*/ const TestKeyPair* issuerKeyPair = nullptr,
54
           /*optional*/ const TestKeyPair* subjectKeyPair = nullptr)
55
0
{
56
0
  static long serialNumberValue = 0;
57
0
  ++serialNumberValue;
58
0
  ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
59
0
  EXPECT_FALSE(ENCODING_FAILED(serialNumber));
60
0
61
0
  ByteString issuerDER(issuerCN ? CNToDERName(issuerCN) : Name(ByteString()));
62
0
  ByteString subjectDER(subjectCN ? CNToDERName(subjectCN) : Name(ByteString()));
63
0
64
0
  std::vector<ByteString> extensions;
65
0
  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
66
0
    ByteString basicConstraints =
67
0
      CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
68
0
    EXPECT_FALSE(ENCODING_FAILED(basicConstraints));
69
0
    extensions.push_back(basicConstraints);
70
0
  }
71
0
  if (extension) {
72
0
    extensions.push_back(*extension);
73
0
  }
74
0
  extensions.push_back(ByteString()); // marks the end of the list
75
0
76
0
  ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
77
0
  ByteString certDER(CreateEncodedCertificate(
78
0
                       v3, sha256WithRSAEncryption(), serialNumber, issuerDER,
79
0
                       oneDayBeforeNow, oneDayAfterNow, subjectDER,
80
0
                       subjectKeyPair ? *subjectKeyPair : *reusedKey,
81
0
                       extensions.data(),
82
0
                       issuerKeyPair ? *issuerKeyPair : *reusedKey,
83
0
                       sha256WithRSAEncryption()));
84
0
  EXPECT_FALSE(ENCODING_FAILED(certDER));
85
0
86
0
  if (subjectDERToCertDER) {
87
0
    (*subjectDERToCertDER)[subjectDER] = certDER;
88
0
  }
89
0
90
0
  return certDER;
91
0
}
92
93
class TestTrustDomain final : public DefaultCryptoTrustDomain
94
{
95
public:
96
  // The "cert chain tail" is a longish chain of certificates that is used by
97
  // all of the tests here. We share this chain across all the tests in order
98
  // to speed up the tests (generating keypairs for the certs is very slow).
99
  bool SetUpCertChainTail()
100
0
  {
101
0
    static char const* const names[] = {
102
0
        "CA1 (Root)", "CA2", "CA3", "CA4", "CA5", "CA6", "CA7"
103
0
    };
104
0
105
0
    for (size_t i = 0; i < MOZILLA_PKIX_ARRAY_LENGTH(names); ++i) {
106
0
      const char* issuerName = i == 0 ? names[0] : names[i-1];
107
0
      CreateCACert(issuerName, names[i]);
108
0
      if (i == 0) {
109
0
        rootCACertDER = leafCACertDER;
110
0
      }
111
0
    }
112
0
113
0
    return true;
114
0
  }
115
116
  void CreateCACert(const char* issuerName, const char* subjectName)
117
0
  {
118
0
    leafCACertDER = CreateCert(issuerName, subjectName,
119
0
                               EndEntityOrCA::MustBeCA, &subjectDERToCertDER);
120
0
    assert(!ENCODING_FAILED(leafCACertDER));
121
0
  }
122
123
0
  ByteString GetLeafCACertDER() const { return leafCACertDER; }
124
125
private:
126
  Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
127
                      /*out*/ TrustLevel& trustLevel) override
128
0
  {
129
0
    trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
130
0
               ? TrustLevel::TrustAnchor
131
0
               : TrustLevel::InheritsTrust;
132
0
    return Success;
133
0
  }
134
135
  Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
136
                    override
137
0
  {
138
0
    ByteString subjectDER(InputToByteString(encodedIssuerName));
139
0
    ByteString certDER(subjectDERToCertDER[subjectDER]);
140
0
    Input derCert;
141
0
    Result rv = derCert.Init(certDER.data(), certDER.length());
142
0
    if (rv != Success) {
143
0
      return rv;
144
0
    }
145
0
    bool keepGoing;
146
0
    rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/,
147
0
                       keepGoing);
148
0
    if (rv != Success) {
149
0
      return rv;
150
0
    }
151
0
    return Success;
152
0
  }
153
154
  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
155
                         /*optional*/ const Input*, /*optional*/ const Input*)
156
                         override
157
0
  {
158
0
    return Success;
159
0
  }
160
161
  Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
162
0
  {
163
0
    return Success;
164
0
  }
165
166
  std::map<ByteString, ByteString> subjectDERToCertDER;
167
  ByteString leafCACertDER;
168
  ByteString rootCACertDER;
169
};
170
171
class pkixbuild : public ::testing::Test
172
{
173
public:
174
  static void SetUpTestCase()
175
0
  {
176
0
    if (!trustDomain.SetUpCertChainTail()) {
177
0
      abort();
178
0
    }
179
0
  }
180
181
protected:
182
183
  static TestTrustDomain trustDomain;
184
};
185
186
/*static*/ TestTrustDomain pkixbuild::trustDomain;
187
188
TEST_F(pkixbuild, MaxAcceptableCertChainLength)
189
0
{
190
0
  {
191
0
    ByteString leafCACert(trustDomain.GetLeafCACertDER());
192
0
    Input certDER;
193
0
    ASSERT_EQ(Success, certDER.Init(leafCACert.data(), leafCACert.length()));
194
0
    ASSERT_EQ(Success,
195
0
              BuildCertChain(trustDomain, certDER, Now(),
196
0
                             EndEntityOrCA::MustBeCA,
197
0
                             KeyUsage::noParticularKeyUsageRequired,
198
0
                             KeyPurposeId::id_kp_serverAuth,
199
0
                             CertPolicyId::anyPolicy,
200
0
                             nullptr/*stapledOCSPResponse*/));
201
0
  }
202
0
203
0
  {
204
0
    ByteString certDER(CreateCert("CA7", "Direct End-Entity",
205
0
                                  EndEntityOrCA::MustBeEndEntity));
206
0
    ASSERT_FALSE(ENCODING_FAILED(certDER));
207
0
    Input certDERInput;
208
0
    ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
209
0
    ASSERT_EQ(Success,
210
0
              BuildCertChain(trustDomain, certDERInput, Now(),
211
0
                             EndEntityOrCA::MustBeEndEntity,
212
0
                             KeyUsage::noParticularKeyUsageRequired,
213
0
                             KeyPurposeId::id_kp_serverAuth,
214
0
                             CertPolicyId::anyPolicy,
215
0
                             nullptr/*stapledOCSPResponse*/));
216
0
  }
217
0
}
218
219
TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
220
0
{
221
0
  static char const* const caCertName = "CA Too Far";
222
0
223
0
  trustDomain.CreateCACert("CA7", caCertName);
224
0
225
0
  {
226
0
    ByteString certDER(trustDomain.GetLeafCACertDER());
227
0
    Input certDERInput;
228
0
    ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
229
0
    ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
230
0
              BuildCertChain(trustDomain, certDERInput, Now(),
231
0
                             EndEntityOrCA::MustBeCA,
232
0
                             KeyUsage::noParticularKeyUsageRequired,
233
0
                             KeyPurposeId::id_kp_serverAuth,
234
0
                             CertPolicyId::anyPolicy,
235
0
                             nullptr/*stapledOCSPResponse*/));
236
0
  }
237
0
238
0
  {
239
0
    ByteString certDER(CreateCert(caCertName, "End-Entity Too Far",
240
0
                                  EndEntityOrCA::MustBeEndEntity));
241
0
    ASSERT_FALSE(ENCODING_FAILED(certDER));
242
0
    Input certDERInput;
243
0
    ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
244
0
    ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
245
0
              BuildCertChain(trustDomain, certDERInput, Now(),
246
0
                             EndEntityOrCA::MustBeEndEntity,
247
0
                             KeyUsage::noParticularKeyUsageRequired,
248
0
                             KeyPurposeId::id_kp_serverAuth,
249
0
                             CertPolicyId::anyPolicy,
250
0
                             nullptr/*stapledOCSPResponse*/));
251
0
  }
252
0
}
253
254
// A TrustDomain that checks certificates against a given root certificate.
255
// It is initialized with the DER encoding of a root certificate that
256
// is treated as a trust anchor and is assumed to have issued all certificates
257
// (i.e. FindIssuer always attempts to build the next step in the chain with
258
// it).
259
class SingleRootTrustDomain : public DefaultCryptoTrustDomain
260
{
261
public:
262
  explicit SingleRootTrustDomain(ByteString aRootDER)
263
    : rootDER(aRootDER)
264
0
  {
265
0
  }
266
267
  // The CertPolicyId argument is unused because we don't care about EV.
268
  Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
269
                      /*out*/ TrustLevel& trustLevel) override
270
0
  {
271
0
    Input rootCert;
272
0
    Result rv = rootCert.Init(rootDER.data(), rootDER.length());
273
0
    if (rv != Success) {
274
0
      return rv;
275
0
    }
276
0
    if (InputsAreEqual(candidateCert, rootCert)) {
277
0
      trustLevel = TrustLevel::TrustAnchor;
278
0
    } else {
279
0
      trustLevel = TrustLevel::InheritsTrust;
280
0
    }
281
0
    return Success;
282
0
  }
283
284
  Result FindIssuer(Input, IssuerChecker& checker, Time) override
285
0
  {
286
0
    // keepGoing is an out parameter from IssuerChecker.Check. It would tell us
287
0
    // whether or not to continue attempting other potential issuers. We only
288
0
    // know of one potential issuer, however, so we ignore it.
289
0
    bool keepGoing;
290
0
    Input rootCert;
291
0
    Result rv = rootCert.Init(rootDER.data(), rootDER.length());
292
0
    if (rv != Success) {
293
0
      return rv;
294
0
    }
295
0
    return checker.Check(rootCert, nullptr, keepGoing);
296
0
  }
297
298
  Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
299
0
  {
300
0
    return Success;
301
0
  }
302
303
  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
304
                         /*optional*/ const Input*, /*optional*/ const Input*)
305
                         override
306
0
  {
307
0
    return Success;
308
0
  }
309
310
private:
311
  ByteString rootDER;
312
};
313
314
// A TrustDomain that explicitly fails if CheckRevocation is called.
315
class ExpiredCertTrustDomain final : public SingleRootTrustDomain
316
{
317
public:
318
  explicit ExpiredCertTrustDomain(ByteString aRootDER)
319
    : SingleRootTrustDomain(aRootDER)
320
0
  {
321
0
  }
322
323
  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
324
                         /*optional*/ const Input*, /*optional*/ const Input*)
325
                         override
326
0
  {
327
0
    ADD_FAILURE();
328
0
    return NotReached("CheckRevocation should not be called",
329
0
                      Result::FATAL_ERROR_LIBRARY_FAILURE);
330
0
  }
331
};
332
333
TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
334
0
{
335
0
  const char* rootCN = "Root CA";
336
0
  ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
337
0
                                nullptr));
338
0
  EXPECT_FALSE(ENCODING_FAILED(rootDER));
339
0
  ExpiredCertTrustDomain expiredCertTrustDomain(rootDER);
340
0
341
0
  ByteString serialNumber(CreateEncodedSerialNumber(100));
342
0
  EXPECT_FALSE(ENCODING_FAILED(serialNumber));
343
0
  ByteString issuerDER(CNToDERName(rootCN));
344
0
  ByteString subjectDER(CNToDERName("Expired End-Entity Cert"));
345
0
  ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
346
0
  ByteString certDER(CreateEncodedCertificate(
347
0
                       v3, sha256WithRSAEncryption(),
348
0
                       serialNumber, issuerDER,
349
0
                       twoDaysBeforeNow,
350
0
                       oneDayBeforeNow,
351
0
                       subjectDER, *reusedKey, nullptr, *reusedKey,
352
0
                       sha256WithRSAEncryption()));
353
0
  EXPECT_FALSE(ENCODING_FAILED(certDER));
354
0
355
0
  Input cert;
356
0
  ASSERT_EQ(Success, cert.Init(certDER.data(), certDER.length()));
357
0
  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
358
0
            BuildCertChain(expiredCertTrustDomain, cert, Now(),
359
0
                           EndEntityOrCA::MustBeEndEntity,
360
0
                           KeyUsage::noParticularKeyUsageRequired,
361
0
                           KeyPurposeId::id_kp_serverAuth,
362
0
                           CertPolicyId::anyPolicy,
363
0
                           nullptr));
364
0
}
365
366
class DSSTrustDomain final : public EverythingFailsByDefaultTrustDomain
367
{
368
public:
369
  Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
370
                      Input, /*out*/ TrustLevel& trustLevel) override
371
0
  {
372
0
    trustLevel = TrustLevel::TrustAnchor;
373
0
    return Success;
374
0
  }
375
};
376
377
class pkixbuild_DSS : public ::testing::Test { };
378
379
TEST_F(pkixbuild_DSS, DSSEndEntityKeyNotAccepted)
380
0
{
381
0
  DSSTrustDomain trustDomain;
382
0
383
0
  ByteString serialNumber(CreateEncodedSerialNumber(1));
384
0
  ASSERT_FALSE(ENCODING_FAILED(serialNumber));
385
0
386
0
  ByteString subjectDER(CNToDERName("DSS"));
387
0
  ASSERT_FALSE(ENCODING_FAILED(subjectDER));
388
0
  ScopedTestKeyPair subjectKey(GenerateDSSKeyPair());
389
0
  ASSERT_TRUE(subjectKey.get());
390
0
391
0
  ByteString issuerDER(CNToDERName("RSA"));
392
0
  ASSERT_FALSE(ENCODING_FAILED(issuerDER));
393
0
  ScopedTestKeyPair issuerKey(CloneReusedKeyPair());
394
0
  ASSERT_TRUE(issuerKey.get());
395
0
396
0
  ByteString cert(CreateEncodedCertificate(v3, sha256WithRSAEncryption(),
397
0
                                           serialNumber, issuerDER,
398
0
                                           oneDayBeforeNow, oneDayAfterNow,
399
0
                                           subjectDER, *subjectKey, nullptr,
400
0
                                           *issuerKey, sha256WithRSAEncryption()));
401
0
  ASSERT_FALSE(ENCODING_FAILED(cert));
402
0
  Input certDER;
403
0
  ASSERT_EQ(Success, certDER.Init(cert.data(), cert.length()));
404
0
405
0
  ASSERT_EQ(Result::ERROR_UNSUPPORTED_KEYALG,
406
0
            BuildCertChain(trustDomain, certDER, Now(),
407
0
                           EndEntityOrCA::MustBeEndEntity,
408
0
                           KeyUsage::noParticularKeyUsageRequired,
409
0
                           KeyPurposeId::id_kp_serverAuth,
410
0
                           CertPolicyId::anyPolicy,
411
0
                           nullptr/*stapledOCSPResponse*/));
412
0
}
413
414
class IssuerNameCheckTrustDomain final : public DefaultCryptoTrustDomain
415
{
416
public:
417
  IssuerNameCheckTrustDomain(const ByteString& aIssuer, bool aExpectedKeepGoing)
418
    : issuer(aIssuer)
419
    , expectedKeepGoing(aExpectedKeepGoing)
420
0
  {
421
0
  }
422
423
  Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&, Input,
424
                      /*out*/ TrustLevel& trustLevel) override
425
0
  {
426
0
    trustLevel = endEntityOrCA == EndEntityOrCA::MustBeCA
427
0
               ? TrustLevel::TrustAnchor
428
0
               : TrustLevel::InheritsTrust;
429
0
    return Success;
430
0
  }
431
432
  Result FindIssuer(Input, IssuerChecker& checker, Time) override
433
0
  {
434
0
    Input issuerInput;
435
0
    EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length()));
436
0
    bool keepGoing;
437
0
    EXPECT_EQ(Success,
438
0
              checker.Check(issuerInput, nullptr /*additionalNameConstraints*/,
439
0
                            keepGoing));
440
0
    EXPECT_EQ(expectedKeepGoing, keepGoing);
441
0
    return Success;
442
0
  }
443
444
  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
445
                         /*optional*/ const Input*, /*optional*/ const Input*)
446
                         override
447
0
  {
448
0
    return Success;
449
0
  }
450
451
  Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
452
0
  {
453
0
    return Success;
454
0
  }
455
456
private:
457
  const ByteString issuer;
458
  const bool expectedKeepGoing;
459
};
460
461
struct IssuerNameCheckParams
462
{
463
  const char* subjectIssuerCN; // null means "empty name"
464
  const char* issuerSubjectCN; // null means "empty name"
465
  bool matches;
466
  Result expectedError;
467
};
468
469
static const IssuerNameCheckParams ISSUER_NAME_CHECK_PARAMS[] =
470
{
471
  { "foo", "foo", true, Success },
472
  { "foo", "bar", false, Result::ERROR_UNKNOWN_ISSUER },
473
  { "f", "foo", false, Result::ERROR_UNKNOWN_ISSUER }, // prefix
474
  { "foo", "f", false, Result::ERROR_UNKNOWN_ISSUER }, // prefix
475
  { "foo", "Foo", false, Result::ERROR_UNKNOWN_ISSUER }, // case sensitive
476
  { "", "", true, Success },
477
  { nullptr, nullptr, false, Result::ERROR_EMPTY_ISSUER_NAME }, // empty issuer
478
479
  // check that certificate-related errors are deferred and superseded by
480
  // ERROR_UNKNOWN_ISSUER when a chain can't be built due to name mismatches
481
  { "foo", nullptr, false, Result::ERROR_UNKNOWN_ISSUER },
482
  { nullptr, "foo", false, Result::ERROR_UNKNOWN_ISSUER }
483
};
484
485
class pkixbuild_IssuerNameCheck
486
  : public ::testing::Test
487
  , public ::testing::WithParamInterface<IssuerNameCheckParams>
488
{
489
};
490
491
TEST_P(pkixbuild_IssuerNameCheck, MatchingName)
492
0
{
493
0
  const IssuerNameCheckParams& params(GetParam());
494
0
495
0
  ByteString issuerCertDER(CreateCert(params.issuerSubjectCN,
496
0
                                      params.issuerSubjectCN,
497
0
                                      EndEntityOrCA::MustBeCA, nullptr));
498
0
  ASSERT_FALSE(ENCODING_FAILED(issuerCertDER));
499
0
500
0
  ByteString subjectCertDER(CreateCert(params.subjectIssuerCN, "end-entity",
501
0
                                       EndEntityOrCA::MustBeEndEntity,
502
0
                                       nullptr));
503
0
  ASSERT_FALSE(ENCODING_FAILED(subjectCertDER));
504
0
505
0
  Input subjectCertDERInput;
506
0
  ASSERT_EQ(Success, subjectCertDERInput.Init(subjectCertDER.data(),
507
0
                                              subjectCertDER.length()));
508
0
509
0
  IssuerNameCheckTrustDomain trustDomain(issuerCertDER, !params.matches);
510
0
  ASSERT_EQ(params.expectedError,
511
0
            BuildCertChain(trustDomain, subjectCertDERInput, Now(),
512
0
                           EndEntityOrCA::MustBeEndEntity,
513
0
                           KeyUsage::noParticularKeyUsageRequired,
514
0
                           KeyPurposeId::id_kp_serverAuth,
515
0
                           CertPolicyId::anyPolicy,
516
0
                           nullptr/*stapledOCSPResponse*/));
517
0
}
518
519
INSTANTIATE_TEST_CASE_P(pkixbuild_IssuerNameCheck, pkixbuild_IssuerNameCheck,
520
                        testing::ValuesIn(ISSUER_NAME_CHECK_PARAMS));
521
522
523
// Records the embedded SCT list extension for later examination.
524
class EmbeddedSCTListTestTrustDomain final : public SingleRootTrustDomain
525
{
526
public:
527
  explicit EmbeddedSCTListTestTrustDomain(ByteString aRootDER)
528
    : SingleRootTrustDomain(aRootDER)
529
0
  {
530
0
  }
531
532
  virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
533
                                      Input extensionData) override
534
0
  {
535
0
    if (extension == AuxiliaryExtension::EmbeddedSCTList) {
536
0
      signedCertificateTimestamps = InputToByteString(extensionData);
537
0
    } else {
538
0
      ADD_FAILURE();
539
0
    }
540
0
  }
541
542
  ByteString signedCertificateTimestamps;
543
};
544
545
TEST_F(pkixbuild, CertificateTransparencyExtension)
546
0
{
547
0
  // python security/pkix/tools/DottedOIDToCode.py --tlv
548
0
  //   id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
549
0
  static const uint8_t tlv_id_embeddedSctList[] = {
550
0
    0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
551
0
  };
552
0
  static const uint8_t dummySctList[] = {
553
0
    0x01, 0x02, 0x03, 0x04, 0x05
554
0
  };
555
0
556
0
  ByteString ctExtension = TLV(der::SEQUENCE,
557
0
    BytesToByteString(tlv_id_embeddedSctList) +
558
0
    Boolean(false) +
559
0
    TLV(der::OCTET_STRING,
560
0
      // SignedCertificateTimestampList structure is encoded as an OCTET STRING
561
0
      // within the X.509v3 extension (see RFC 6962 section 3.3).
562
0
      // pkix decodes it internally and returns the actual structure.
563
0
      TLV(der::OCTET_STRING, BytesToByteString(dummySctList))));
564
0
565
0
  const char* rootCN = "Root CA";
566
0
  ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA));
567
0
  ASSERT_FALSE(ENCODING_FAILED(rootDER));
568
0
569
0
  ByteString certDER(CreateCert(rootCN, "Cert with SCT list",
570
0
                                EndEntityOrCA::MustBeEndEntity,
571
0
                                nullptr, /*subjectDERToCertDER*/
572
0
                                &ctExtension));
573
0
  ASSERT_FALSE(ENCODING_FAILED(certDER));
574
0
575
0
  Input certInput;
576
0
  ASSERT_EQ(Success, certInput.Init(certDER.data(), certDER.length()));
577
0
578
0
  EmbeddedSCTListTestTrustDomain extTrustDomain(rootDER);
579
0
  ASSERT_EQ(Success,
580
0
            BuildCertChain(extTrustDomain, certInput, Now(),
581
0
                           EndEntityOrCA::MustBeEndEntity,
582
0
                           KeyUsage::noParticularKeyUsageRequired,
583
0
                           KeyPurposeId::anyExtendedKeyUsage,
584
0
                           CertPolicyId::anyPolicy,
585
0
                           nullptr /*stapledOCSPResponse*/));
586
0
  ASSERT_EQ(BytesToByteString(dummySctList),
587
0
            extTrustDomain.signedCertificateTimestamps);
588
0
}
589
590
// This TrustDomain implements a hierarchy like so:
591
//
592
// A   B
593
// |   |
594
// C   D
595
//  \ /
596
//   E
597
//
598
// where A is a trust anchor, B is not a trust anchor and has no known issuer, C
599
// and D are intermediates with the same subject and subject public key, and E
600
// is an end-entity (in practice, the end-entity will be generated by the test
601
// functions using this trust domain).
602
class MultiplePathTrustDomain: public DefaultCryptoTrustDomain
603
{
604
public:
605
  void SetUpCerts()
606
0
  {
607
0
    ASSERT_FALSE(ENCODING_FAILED(CreateCert("UntrustedRoot", "UntrustedRoot",
608
0
                                            EndEntityOrCA::MustBeCA,
609
0
                                            &subjectDERToCertDER)));
610
0
    // The subject DER -> cert DER mapping would be overwritten for subject
611
0
    // "Intermediate" when we create the second "Intermediate" certificate, so
612
0
    // we keep a copy of this "Intermediate".
613
0
    intermediateSignedByUntrustedRootCertDER =
614
0
      CreateCert("UntrustedRoot", "Intermediate", EndEntityOrCA::MustBeCA);
615
0
    ASSERT_FALSE(ENCODING_FAILED(intermediateSignedByUntrustedRootCertDER));
616
0
    rootCACertDER = CreateCert("TrustedRoot", "TrustedRoot",
617
0
                               EndEntityOrCA::MustBeCA, &subjectDERToCertDER);
618
0
    ASSERT_FALSE(ENCODING_FAILED(rootCACertDER));
619
0
    ASSERT_FALSE(ENCODING_FAILED(CreateCert("TrustedRoot", "Intermediate",
620
0
                                            EndEntityOrCA::MustBeCA,
621
0
                                            &subjectDERToCertDER)));
622
0
  }
623
624
private:
625
  Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
626
                      /*out*/ TrustLevel& trustLevel) override
627
0
  {
628
0
    trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
629
0
               ? TrustLevel::TrustAnchor
630
0
               : TrustLevel::InheritsTrust;
631
0
    return Success;
632
0
  }
633
634
  Result CheckCert(ByteString& certDER, IssuerChecker& checker, bool& keepGoing)
635
0
  {
636
0
    Input derCert;
637
0
    Result rv = derCert.Init(certDER.data(), certDER.length());
638
0
    if (rv != Success) {
639
0
      return rv;
640
0
    }
641
0
    return checker.Check(derCert, nullptr/*additionalNameConstraints*/,
642
0
                         keepGoing);
643
0
  }
644
645
  Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
646
                    override
647
0
  {
648
0
    ByteString subjectDER(InputToByteString(encodedIssuerName));
649
0
    ByteString certDER(subjectDERToCertDER[subjectDER]);
650
0
    assert(!ENCODING_FAILED(certDER));
651
0
    bool keepGoing;
652
0
    Result rv = CheckCert(certDER, checker, keepGoing);
653
0
    if (rv != Success) {
654
0
      return rv;
655
0
    }
656
0
    // Also try the other intermediate.
657
0
    if (keepGoing) {
658
0
      rv = CheckCert(intermediateSignedByUntrustedRootCertDER, checker,
659
0
                     keepGoing);
660
0
      if (rv != Success) {
661
0
        return rv;
662
0
      }
663
0
    }
664
0
    return Success;
665
0
  }
666
667
  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
668
                         /*optional*/ const Input*,
669
                         /*optional*/ const Input*) override
670
0
  {
671
0
    return Success;
672
0
  }
673
674
  Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
675
0
  {
676
0
    return Success;
677
0
  }
678
679
  std::map<ByteString, ByteString> subjectDERToCertDER;
680
  ByteString rootCACertDER;
681
  ByteString intermediateSignedByUntrustedRootCertDER;
682
};
683
684
TEST_F(pkixbuild, BadEmbeddedSCTWithMultiplePaths)
685
0
{
686
0
  MultiplePathTrustDomain trustDomain;
687
0
  trustDomain.SetUpCerts();
688
0
689
0
  // python security/pkix/tools/DottedOIDToCode.py --tlv
690
0
  //   id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
691
0
  static const uint8_t tlv_id_embeddedSctList[] = {
692
0
    0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
693
0
  };
694
0
  static const uint8_t dummySctList[] = {
695
0
    0x01, 0x02, 0x03, 0x04, 0x05
696
0
  };
697
0
  ByteString ctExtension = TLV(der::SEQUENCE,
698
0
    BytesToByteString(tlv_id_embeddedSctList) +
699
0
    Boolean(false) +
700
0
    // The contents of the OCTET STRING are supposed to consist of an OCTET
701
0
    // STRING of useful data. We're testing what happens if it isn't, so shove
702
0
    // some bogus (non-OCTET STRING) data in there.
703
0
    TLV(der::OCTET_STRING, BytesToByteString(dummySctList)));
704
0
  ByteString certDER(CreateCert("Intermediate", "Cert with bogus SCT list",
705
0
                                EndEntityOrCA::MustBeEndEntity,
706
0
                                nullptr, /*subjectDERToCertDER*/
707
0
                                &ctExtension));
708
0
  ASSERT_FALSE(ENCODING_FAILED(certDER));
709
0
  Input certDERInput;
710
0
  ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
711
0
  ASSERT_EQ(Result::ERROR_BAD_DER,
712
0
            BuildCertChain(trustDomain, certDERInput, Now(),
713
0
                           EndEntityOrCA::MustBeEndEntity,
714
0
                           KeyUsage::noParticularKeyUsageRequired,
715
0
                           KeyPurposeId::id_kp_serverAuth,
716
0
                           CertPolicyId::anyPolicy,
717
0
                           nullptr/*stapledOCSPResponse*/));
718
0
}
719
720
// Same as a MultiplePathTrustDomain, but the end-entity is revoked.
721
class RevokedEndEntityTrustDomain final : public MultiplePathTrustDomain
722
{
723
public:
724
  Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&, Time,
725
                         Duration, /*optional*/ const Input*,
726
                         /*optional*/ const Input*) override
727
0
  {
728
0
    if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
729
0
      return Result::ERROR_REVOKED_CERTIFICATE;
730
0
    }
731
0
    return Success;
732
0
  }
733
};
734
735
TEST_F(pkixbuild, RevokedEndEntityWithMultiplePaths)
736
0
{
737
0
  RevokedEndEntityTrustDomain trustDomain;
738
0
  trustDomain.SetUpCerts();
739
0
  ByteString certDER(CreateCert("Intermediate", "RevokedEndEntity",
740
0
                                EndEntityOrCA::MustBeEndEntity));
741
0
  ASSERT_FALSE(ENCODING_FAILED(certDER));
742
0
  Input certDERInput;
743
0
  ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
744
0
  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE,
745
0
            BuildCertChain(trustDomain, certDERInput, Now(),
746
0
                           EndEntityOrCA::MustBeEndEntity,
747
0
                           KeyUsage::noParticularKeyUsageRequired,
748
0
                           KeyPurposeId::id_kp_serverAuth,
749
0
                           CertPolicyId::anyPolicy,
750
0
                           nullptr/*stapledOCSPResponse*/));
751
0
}
752
753
// This represents a collection of different certificates that all have the same
754
// subject and issuer distinguished name.
755
class SelfIssuedCertificatesTrustDomain final : public DefaultCryptoTrustDomain
756
{
757
public:
758
  void SetUpCerts(size_t totalCerts)
759
0
  {
760
0
    ASSERT_TRUE(totalCerts > 0);
761
0
    // First we generate a trust anchor.
762
0
    ScopedTestKeyPair rootKeyPair(GenerateKeyPair());
763
0
    rootCACertDER = CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
764
0
                               nullptr, rootKeyPair.get(), rootKeyPair.get());
765
0
    ASSERT_FALSE(ENCODING_FAILED(rootCACertDER));
766
0
    certs.push_back(rootCACertDER);
767
0
    ScopedTestKeyPair issuerKeyPair(rootKeyPair.release());
768
0
    size_t subCAsGenerated;
769
0
    // Then we generate 6 sub-CAs (given that we were requested to generate at
770
0
    // least that many).
771
0
    for (subCAsGenerated = 0;
772
0
         subCAsGenerated < totalCerts - 1 && subCAsGenerated < 6;
773
0
         subCAsGenerated++) {
774
0
      // Each certificate has to have a unique SPKI (mozilla::pkix does loop
775
0
      // detection and stops searching if it encounters two certificates in a
776
0
      // path with the same subject and SPKI).
777
0
      ScopedTestKeyPair keyPair(GenerateKeyPair());
778
0
      ByteString cert(CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
779
0
                                 nullptr, issuerKeyPair.get(), keyPair.get()));
780
0
      ASSERT_FALSE(ENCODING_FAILED(cert));
781
0
      certs.push_back(cert);
782
0
      issuerKeyPair.reset(keyPair.release());
783
0
    }
784
0
    // We set firstIssuerKey here because we can't end up with a path that has
785
0
    // more than 7 CAs in it (because mozilla::pkix limits the path length).
786
0
    firstIssuerKey.reset(issuerKeyPair.release());
787
0
    // For any more sub CAs we generate, it doesn't matter what their keys are
788
0
    // as long as they're different.
789
0
    for (; subCAsGenerated < totalCerts - 1; subCAsGenerated++) {
790
0
      ScopedTestKeyPair keyPair(GenerateKeyPair());
791
0
      ByteString cert(CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
792
0
                                 nullptr, keyPair.get(), keyPair.get()));
793
0
      ASSERT_FALSE(ENCODING_FAILED(cert));
794
0
      certs.insert(certs.begin(), cert);
795
0
    }
796
0
  }
797
798
  const TestKeyPair* GetFirstIssuerKey()
799
0
  {
800
0
    return firstIssuerKey.get();
801
0
  }
802
803
private:
804
  Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
805
                      /*out*/ TrustLevel& trustLevel) override
806
0
  {
807
0
    trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
808
0
               ? TrustLevel::TrustAnchor
809
0
               : TrustLevel::InheritsTrust;
810
0
    return Success;
811
0
  }
812
813
  Result FindIssuer(Input, IssuerChecker& checker, Time) override
814
0
  {
815
0
    bool keepGoing;
816
0
    for (auto& cert: certs) {
817
0
      Input certInput;
818
0
      Result rv = certInput.Init(cert.data(), cert.length());
819
0
      if (rv != Success) {
820
0
        return rv;
821
0
      }
822
0
      rv = checker.Check(certInput, nullptr, keepGoing);
823
0
      if (rv != Success || !keepGoing) {
824
0
        return rv;
825
0
      }
826
0
    }
827
0
    return Success;
828
0
  }
829
830
  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
831
                         /*optional*/ const Input*, /*optional*/ const Input*)
832
                         override
833
0
  {
834
0
    return Success;
835
0
  }
836
837
  Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
838
0
  {
839
0
    return Success;
840
0
  }
841
842
  std::vector<ByteString> certs;
843
  ByteString rootCACertDER;
844
  ScopedTestKeyPair firstIssuerKey;
845
};
846
847
TEST_F(pkixbuild, AvoidUnboundedPathSearchingFailure)
848
0
{
849
0
  SelfIssuedCertificatesTrustDomain trustDomain;
850
0
  // This creates a few hundred million potential paths of length 8 (end entity
851
0
  // + 6 sub-CAs + root). It would be prohibitively expensive to enumerate all
852
0
  // of these, so we give mozilla::pkix a budget that is spent when searching
853
0
  // paths. If the budget is exhausted, it simply returns an unknown issuer
854
0
  // error. In the future it might be nice to return a specific error that would
855
0
  // give the front-end a hint that maybe it shouldn't have so many certificates
856
0
  // that all have the same subject and issuer DN but different SPKIs.
857
0
  trustDomain.SetUpCerts(18);
858
0
  ByteString certDER(CreateCert("DN", "DN", EndEntityOrCA::MustBeEndEntity,
859
0
                                nullptr, nullptr,
860
0
                                trustDomain.GetFirstIssuerKey()));
861
0
  ASSERT_FALSE(ENCODING_FAILED(certDER));
862
0
  Input certDERInput;
863
0
  ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
864
0
  ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
865
0
            BuildCertChain(trustDomain, certDERInput, Now(),
866
0
                           EndEntityOrCA::MustBeEndEntity,
867
0
                           KeyUsage::noParticularKeyUsageRequired,
868
0
                           KeyPurposeId::id_kp_serverAuth,
869
0
                           CertPolicyId::anyPolicy,
870
0
                           nullptr/*stapledOCSPResponse*/));
871
0
}
872
873
TEST_F(pkixbuild, AvoidUnboundedPathSearchingSuccess)
874
0
{
875
0
  SelfIssuedCertificatesTrustDomain trustDomain;
876
0
  // This creates a few hundred thousand possible potential paths of length 8
877
0
  // (end entity + 6 sub-CAs + root). This will nearly exhaust mozilla::pkix's
878
0
  // search budget, so this should succeed.
879
0
  trustDomain.SetUpCerts(10);
880
0
  ByteString certDER(CreateCert("DN", "DN", EndEntityOrCA::MustBeEndEntity,
881
0
                                nullptr, nullptr,
882
0
                                trustDomain.GetFirstIssuerKey()));
883
0
  ASSERT_FALSE(ENCODING_FAILED(certDER));
884
0
  Input certDERInput;
885
0
  ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
886
0
  ASSERT_EQ(Success,
887
0
            BuildCertChain(trustDomain, certDERInput, Now(),
888
0
                           EndEntityOrCA::MustBeEndEntity,
889
0
                           KeyUsage::noParticularKeyUsageRequired,
890
0
                           KeyPurposeId::id_kp_serverAuth,
891
0
                           CertPolicyId::anyPolicy,
892
0
                           nullptr/*stapledOCSPResponse*/));
893
0
}