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

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial