security/manager/ssl/tests/gtest/OCSPCacheTest.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "CertVerifier.h"
     8 #include "OCSPCache.h"
     9 #include "nss.h"
    10 #include "prerr.h"
    11 #include "prprf.h"
    12 #include "secerr.h"
    14 #include "gtest/gtest.h"
    16 const int MaxCacheEntries = 1024;
    18 class OCSPCacheTest : public ::testing::Test
    19 {
    20   protected:
    21     static void SetUpTestCase()
    22     {
    23       NSS_NoDB_Init(nullptr);
    24       mozilla::psm::InitCertVerifierLog();
    25     }
    27     mozilla::psm::OCSPCache cache;
    28 };
    30 // Makes a fake certificate with just the fields we need for testing here.
    31 // (And those values are almost entirely bogus.)
    32 // stackCert should be stack-allocated memory.
    33 static void
    34 MakeFakeCert(CERTCertificate* stackCert, const char* subjectValue,
    35              const char* issuerValue, const char* serialNumberValue,
    36              const char* publicKeyValue)
    37 {
    38   stackCert->derSubject.data = (unsigned char*)subjectValue;
    39   stackCert->derSubject.len = strlen(subjectValue);
    40   stackCert->derIssuer.data = (unsigned char*)issuerValue;
    41   stackCert->derIssuer.len = strlen(issuerValue);
    42   stackCert->serialNumber.data = (unsigned char*)serialNumberValue;
    43   stackCert->serialNumber.len = strlen(serialNumberValue);
    44   stackCert->derPublicKey.data = (unsigned char*)publicKeyValue;
    45   stackCert->derPublicKey.len = strlen(publicKeyValue);
    46   CERTName *subject = CERT_AsciiToName(subjectValue); // TODO: this will leak...
    47   ASSERT_TRUE(subject);
    48   stackCert->subject.arena = subject->arena;
    49   stackCert->subject.rdns = subject->rdns;
    50 }
    52 static void
    53 PutAndGet(mozilla::psm::OCSPCache& cache, CERTCertificate* subject,
    54           CERTCertificate* issuer,
    55           PRErrorCode error, PRTime time)
    56 {
    57   // The first time is thisUpdate. The second is validUntil.
    58   // The caller is expecting the validUntil returned with Get
    59   // to be equal to the passed-in time. Since these values will
    60   // be different in practice, make thisUpdate less than validUntil.
    61   ASSERT_TRUE(time >= 10);
    62   SECStatus rv = cache.Put(subject, issuer, error, time - 10, time);
    63   ASSERT_TRUE(rv == SECSuccess);
    64   PRErrorCode errorOut;
    65   PRTime timeOut;
    66   ASSERT_TRUE(cache.Get(subject, issuer, errorOut, timeOut));
    67   ASSERT_TRUE(error == errorOut && time == timeOut);
    68 }
    70 TEST_F(OCSPCacheTest, TestPutAndGet)
    71 {
    72   SCOPED_TRACE("");
    73   CERTCertificate subject;
    74   MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
    75   CERTCertificate issuer;
    76   MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
    77   PutAndGet(cache, &subject, &issuer, 0, PR_Now());
    78   PRErrorCode errorOut;
    79   PRTime timeOut;
    80   ASSERT_FALSE(cache.Get(&issuer, &issuer, errorOut, timeOut));
    81 }
    83 TEST_F(OCSPCacheTest, TestVariousGets)
    84 {
    85   SCOPED_TRACE("");
    86   CERTCertificate issuer;
    87   MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
    88   PRTime timeIn = PR_Now();
    89   for (int i = 0; i < MaxCacheEntries; i++) {
    90     CERTCertificate subject;
    91     char subjectBuf[64];
    92     PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
    93     char serialBuf[8];
    94     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
    95     MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
    96     PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
    97   }
    98   CERTCertificate subject;
    99   // This will be at the end of the list in the cache
   100   PRErrorCode errorOut;
   101   PRTime timeOut;
   102   MakeFakeCert(&subject, "CN=subject0000", "CN=issuer1", "0000", "key000");
   103   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   104   ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
   105   // Once we access it, it goes to the front
   106   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   107   ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
   108   MakeFakeCert(&subject, "CN=subject0512", "CN=issuer1", "0512", "key000");
   109   // This will be in the middle
   110   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   111   ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
   112   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   113   ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
   114   // We've never seen this certificate
   115   MakeFakeCert(&subject, "CN=subject1111", "CN=issuer1", "1111", "key000");
   116   ASSERT_FALSE(cache.Get(&subject, &issuer, errorOut, timeOut));
   117 }
   119 TEST_F(OCSPCacheTest, TestEviction)
   120 {
   121   SCOPED_TRACE("");
   122   CERTCertificate issuer;
   123   PRTime timeIn = PR_Now();
   124   MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
   125   // By putting more distinct entries in the cache than it can hold,
   126   // we cause the least recently used entry to be evicted.
   127   for (int i = 0; i < MaxCacheEntries + 1; i++) {
   128     CERTCertificate subject;
   129     char subjectBuf[64];
   130     PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
   131     char serialBuf[8];
   132     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
   133     MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
   134     PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
   135   }
   136   CERTCertificate evictedSubject;
   137   MakeFakeCert(&evictedSubject, "CN=subject0000", "CN=issuer1", "0000", "key000");
   138   PRErrorCode errorOut;
   139   PRTime timeOut;
   140   ASSERT_FALSE(cache.Get(&evictedSubject, &issuer, errorOut, timeOut));
   141 }
   143 TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses)
   144 {
   145   SCOPED_TRACE("");
   146   CERTCertificate issuer;
   147   MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
   148   CERTCertificate notEvictedSubject;
   149   MakeFakeCert(&notEvictedSubject, "CN=subject0000", "CN=issuer1", "0000", "key000");
   150   PRTime timeIn = PR_Now();
   151   PutAndGet(cache, &notEvictedSubject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, timeIn);
   152   // By putting more distinct entries in the cache than it can hold,
   153   // we cause the least recently used entry that isn't revoked to be evicted.
   154   for (int i = 1; i < MaxCacheEntries + 1; i++) {
   155     CERTCertificate subject;
   156     char subjectBuf[64];
   157     PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
   158     char serialBuf[8];
   159     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
   160     MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
   161     PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
   162   }
   163   PRErrorCode errorOut;
   164   PRTime timeOut;
   165   ASSERT_TRUE(cache.Get(&notEvictedSubject, &issuer, errorOut, timeOut));
   166   ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == timeIn);
   167   CERTCertificate evictedSubject;
   168   MakeFakeCert(&evictedSubject, "CN=subject0001", "CN=issuer1", "0001", "key000");
   169   ASSERT_FALSE(cache.Get(&evictedSubject, &issuer, errorOut, timeOut));
   170 }
   172 TEST_F(OCSPCacheTest, TestEverythingIsRevoked)
   173 {
   174   SCOPED_TRACE("");
   175   CERTCertificate issuer;
   176   MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
   177   PRTime timeIn = PR_Now();
   178   // Fill up the cache with revoked responses.
   179   for (int i = 0; i < MaxCacheEntries; i++) {
   180     CERTCertificate subject;
   181     char subjectBuf[64];
   182     PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
   183     char serialBuf[8];
   184     PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
   185     MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
   186     PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, timeIn + i);
   187   }
   188   CERTCertificate goodSubject;
   189   MakeFakeCert(&goodSubject, "CN=subject1025", "CN=issuer1", "1025", "key000");
   190   // This will "succeed", allowing verification to continue. However,
   191   // nothing was actually put in the cache.
   192   SECStatus result = cache.Put(&goodSubject, &issuer, 0, timeIn + 1025 - 50,
   193                                timeIn + 1025);
   194   ASSERT_TRUE(result == SECSuccess);
   195   PRErrorCode errorOut;
   196   PRTime timeOut;
   197   ASSERT_FALSE(cache.Get(&goodSubject, &issuer, errorOut, timeOut));
   199   CERTCertificate revokedSubject;
   200   MakeFakeCert(&revokedSubject, "CN=subject1026", "CN=issuer1", "1026", "key000");
   201   // This will fail, causing verification to fail.
   202   result = cache.Put(&revokedSubject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE,
   203                      timeIn + 1026 - 50, timeIn + 1026);
   204   PRErrorCode error = PR_GetError();
   205   ASSERT_TRUE(result == SECFailure);
   206   ASSERT_TRUE(error == SEC_ERROR_REVOKED_CERTIFICATE);
   207 }
   209 TEST_F(OCSPCacheTest, VariousIssuers)
   210 {
   211   SCOPED_TRACE("");
   212   CERTCertificate issuer1;
   213   MakeFakeCert(&issuer1, "CN=issuer1", "CN=issuer1", "000", "key000");
   214   CERTCertificate issuer2;
   215   MakeFakeCert(&issuer2, "CN=issuer2", "CN=issuer2", "000", "key001");
   216   CERTCertificate issuer3;
   217   // Note: same CN as issuer1
   218   MakeFakeCert(&issuer3, "CN=issuer1", "CN=issuer3", "000", "key003");
   219   CERTCertificate subject;
   220   MakeFakeCert(&subject, "CN=subject", "CN=issuer1", "001", "key002");
   221   PRTime timeIn = PR_Now();
   222   PutAndGet(cache, &subject, &issuer1, 0, timeIn);
   223   PRErrorCode errorOut;
   224   PRTime timeOut;
   225   ASSERT_TRUE(cache.Get(&subject, &issuer1, errorOut, timeOut));
   226   ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
   227   ASSERT_FALSE(cache.Get(&subject, &issuer2, errorOut, timeOut));
   228   ASSERT_FALSE(cache.Get(&subject, &issuer3, errorOut, timeOut));
   229 }
   231 TEST_F(OCSPCacheTest, Times)
   232 {
   233   SCOPED_TRACE("");
   234   CERTCertificate subject;
   235   MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
   236   CERTCertificate issuer;
   237   MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
   238   PutAndGet(cache, &subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 100);
   239   PutAndGet(cache, &subject, &issuer, 0, 200);
   240   // This should not override the more recent entry.
   241   SECStatus rv = cache.Put(&subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 100, 100);
   242   ASSERT_TRUE(rv == SECSuccess);
   243   PRErrorCode errorOut;
   244   PRTime timeOut;
   245   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   246   // Here we see the more recent time.
   247   ASSERT_TRUE(errorOut == 0 && timeOut == 200);
   249   // SEC_ERROR_REVOKED_CERTIFICATE overrides everything
   250   PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, 50);
   251 }
   253 TEST_F(OCSPCacheTest, NetworkFailure)
   254 {
   255   SCOPED_TRACE("");
   256   CERTCertificate subject;
   257   MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
   258   CERTCertificate issuer;
   259   MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
   260   PutAndGet(cache, &subject, &issuer, PR_CONNECT_REFUSED_ERROR, 100);
   261   PutAndGet(cache, &subject, &issuer, 0, 200);
   262   // This should not override the already present entry.
   263   SECStatus rv = cache.Put(&subject, &issuer, PR_CONNECT_REFUSED_ERROR, 300, 350);
   264   ASSERT_TRUE(rv == SECSuccess);
   265   PRErrorCode errorOut;
   266   PRTime timeOut;
   267   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   268   ASSERT_TRUE(errorOut == 0 && timeOut == 200);
   270   PutAndGet(cache, &subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 400);
   271   // This should not override the already present entry.
   272   rv = cache.Put(&subject, &issuer, PR_CONNECT_REFUSED_ERROR, 500, 550);
   273   ASSERT_TRUE(rv == SECSuccess);
   274   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   275   ASSERT_TRUE(errorOut == SEC_ERROR_OCSP_UNKNOWN_CERT && timeOut == 400);
   277   PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, 600);
   278   // This should not override the already present entry.
   279   rv = cache.Put(&subject, &issuer, PR_CONNECT_REFUSED_ERROR, 700, 750);
   280   ASSERT_TRUE(rv == SECSuccess);
   281   ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
   282   ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == 600);
   283 }

mercurial