summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/manager/ssl/tests/gtest/OCSPCacheTest.cpp357
1 files changed, 357 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
new file mode 100644
index 0000000000..23d0cefc2b
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CertVerifier.h"
+#include "OCSPCache.h"
+#include "gtest/gtest.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Casting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Sprintf.h"
+#include "nss.h"
+#include "mozpkix/pkixtypes.h"
+#include "mozpkix/test/pkixtestutil.h"
+#include "prerr.h"
+#include "secerr.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+using mozilla::OriginAttributes;
+
+template <size_t N>
+inline Input LiteralInput(const char (&valueString)[N]) {
+ // Ideally we would use mozilla::BitwiseCast() here rather than
+ // reinterpret_cast for better type checking, but the |N - 1| part trips
+ // static asserts.
+ return Input(reinterpret_cast<const uint8_t(&)[N - 1]>(valueString));
+}
+
+const int MaxCacheEntries = 1024;
+
+class psm_OCSPCacheTest : public ::testing::Test {
+ protected:
+ psm_OCSPCacheTest() : now(Now()) {}
+
+ static void SetUpTestCase() { NSS_NoDB_Init(nullptr); }
+
+ const Time now;
+ mozilla::psm::OCSPCache cache;
+};
+
+static void PutAndGet(
+ mozilla::psm::OCSPCache& cache, const CertID& certID, Result result,
+ Time time, const OriginAttributes& originAttributes = OriginAttributes()) {
+ // The first time is thisUpdate. The second is validUntil.
+ // The caller is expecting the validUntil returned with Get
+ // to be equal to the passed-in time. Since these values will
+ // be different in practice, make thisUpdate less than validUntil.
+ Time thisUpdate(time);
+ ASSERT_EQ(Success, thisUpdate.SubtractSeconds(10));
+ Result rv = cache.Put(certID, originAttributes, result, thisUpdate, time);
+ ASSERT_TRUE(rv == Success);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, originAttributes, resultOut, timeOut));
+ ASSERT_EQ(result, resultOut);
+ ASSERT_EQ(time, timeOut);
+}
+
+Input fakeIssuer1(LiteralInput("CN=issuer1"));
+Input fakeKey000(LiteralInput("key000"));
+Input fakeKey001(LiteralInput("key001"));
+Input fakeSerial0000(LiteralInput("0000"));
+
+TEST_F(psm_OCSPCacheTest, TestPutAndGet) {
+ Input fakeSerial000(LiteralInput("000"));
+ Input fakeSerial001(LiteralInput("001"));
+
+ SCOPED_TRACE("");
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001), Success,
+ now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestVariousGets) {
+ SCOPED_TRACE("");
+ for (int i = 0; i < MaxCacheEntries; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), Success,
+ timeIn);
+ }
+
+ Time timeIn(now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+
+ // This will be at the end of the list in the cache
+ CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000);
+ ASSERT_TRUE(cache.Get(cert0000, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+ // Once we access it, it goes to the front
+ ASSERT_TRUE(cache.Get(cert0000, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+
+ // This will be in the middle
+ Time timeInPlus512(now);
+ ASSERT_EQ(Success, timeInPlus512.AddSeconds(512));
+
+ static const Input fakeSerial0512(LiteralInput("0512"));
+ CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512);
+ ASSERT_TRUE(cache.Get(cert0512, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeInPlus512, timeOut);
+ ASSERT_TRUE(cache.Get(cert0512, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeInPlus512, timeOut);
+
+ // We've never seen this certificate
+ static const Input fakeSerial1111(LiteralInput("1111"));
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestEviction) {
+ SCOPED_TRACE("");
+ // By putting more distinct entries in the cache than it can hold,
+ // we cause the least recently used entry to be evicted.
+ for (int i = 0; i < MaxCacheEntries + 1; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), Success,
+ timeIn);
+ }
+
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestNoEvictionForRevokedResponses) {
+ SCOPED_TRACE("");
+ CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000);
+ Time timeIn(now);
+ PutAndGet(cache, notEvicted, Result::ERROR_REVOKED_CERTIFICATE, timeIn);
+ // By putting more distinct entries in the cache than it can hold,
+ // we cause the least recently used entry that isn't revoked to be evicted.
+ for (int i = 1; i < MaxCacheEntries + 1; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), Success,
+ timeIn);
+ }
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(notEvicted, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+
+ Input fakeSerial0001(LiteralInput("0001"));
+ CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001);
+ ASSERT_FALSE(cache.Get(evicted, OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestEverythingIsRevoked) {
+ SCOPED_TRACE("");
+ Time timeIn(now);
+ // Fill up the cache with revoked responses.
+ for (int i = 0; i < MaxCacheEntries; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+ Result::ERROR_REVOKED_CERTIFICATE, timeIn);
+ }
+ static const Input fakeSerial1025(LiteralInput("1025"));
+ CertID good(fakeIssuer1, fakeKey000, fakeSerial1025);
+ // This will "succeed", allowing verification to continue. However,
+ // nothing was actually put in the cache.
+ Time timeInPlus1025(timeIn);
+ ASSERT_EQ(Success, timeInPlus1025.AddSeconds(1025));
+ Time timeInPlus1025Minus50(timeInPlus1025);
+ ASSERT_EQ(Success, timeInPlus1025Minus50.SubtractSeconds(50));
+ Result result = cache.Put(good, OriginAttributes(), Success,
+ timeInPlus1025Minus50, timeInPlus1025);
+ ASSERT_EQ(Success, result);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(good, OriginAttributes(), resultOut, timeOut));
+
+ static const Input fakeSerial1026(LiteralInput("1026"));
+ CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026);
+ // This will fail, causing verification to fail.
+ Time timeInPlus1026(timeIn);
+ ASSERT_EQ(Success, timeInPlus1026.AddSeconds(1026));
+ Time timeInPlus1026Minus50(timeInPlus1026);
+ ASSERT_EQ(Success, timeInPlus1026Minus50.SubtractSeconds(50));
+ result =
+ cache.Put(revoked, OriginAttributes(), Result::ERROR_REVOKED_CERTIFICATE,
+ timeInPlus1026Minus50, timeInPlus1026);
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, result);
+}
+
+TEST_F(psm_OCSPCacheTest, VariousIssuers) {
+ SCOPED_TRACE("");
+ Time timeIn(now);
+ static const Input fakeIssuer2(LiteralInput("CN=issuer2"));
+ static const Input fakeSerial001(LiteralInput("001"));
+ CertID subject(fakeIssuer1, fakeKey000, fakeSerial001);
+ PutAndGet(cache, subject, Success, now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(subject, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+ // Test that we don't match a different issuer DN
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001),
+ OriginAttributes(), resultOut, timeOut));
+ // Test that we don't match a different issuer key
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, Times) {
+ SCOPED_TRACE("");
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+ PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(100));
+ PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
+ // This should not override the more recent entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(100), TimeFromElapsedSecondsAD(100)));
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ // Here we see the more recent time.
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
+
+ // Result::ERROR_REVOKED_CERTIFICATE overrides everything
+ PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+ TimeFromElapsedSecondsAD(50));
+}
+
+TEST_F(psm_OCSPCacheTest, NetworkFailure) {
+ SCOPED_TRACE("");
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+ PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(100));
+ PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
+ // This should not override the already present entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(300), TimeFromElapsedSecondsAD(350)));
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
+
+ PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(400));
+ // This should not override the already present entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(500), TimeFromElapsedSecondsAD(550)));
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(400), timeOut);
+
+ PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+ TimeFromElapsedSecondsAD(600));
+ // This should not override the already present entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(700), TimeFromElapsedSecondsAD(750)));
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(600), timeOut);
+}
+
+TEST_F(psm_OCSPCacheTest, TestOriginAttributes) {
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+
+ // We test two attributes, firstPartyDomain and partitionKey, respectively
+ // because we don't have entries that have both attributes set because the two
+ // features that use these attributes are mutually exclusive.
+
+ // Set pref for OCSP cache network partitioning.
+ mozilla::Preferences::SetBool("privacy.partition.network_state.ocsp_cache",
+ true);
+
+ SCOPED_TRACE("");
+ OriginAttributes attrs;
+ attrs.mFirstPartyDomain.AssignLiteral("foo.com");
+ PutAndGet(cache, certID, Success, now, attrs);
+
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ attrs.mFirstPartyDomain.AssignLiteral("bar.com");
+ ASSERT_FALSE(cache.Get(certID, attrs, resultOut, timeOut));
+
+ // OCSP cache should not be isolated by containers for firstPartyDomain.
+ attrs.mUserContextId = 1;
+ attrs.mFirstPartyDomain.AssignLiteral("foo.com");
+ ASSERT_TRUE(cache.Get(certID, attrs, resultOut, timeOut));
+
+ // Clear originAttributes.
+ attrs.mUserContextId = 0;
+ attrs.mFirstPartyDomain.Truncate();
+
+ // Add OCSP cache for the partitionKey.
+ attrs.mPartitionKey.AssignLiteral("(https,foo.com)");
+ PutAndGet(cache, certID, Success, now, attrs);
+
+ // Check cache entry for the partitionKey.
+ attrs.mPartitionKey.AssignLiteral("(https,foo.com)");
+ ASSERT_TRUE(cache.Get(certID, attrs, resultOut, timeOut));
+
+ // OCSP cache entry should not exist for the other partitionKey.
+ attrs.mPartitionKey.AssignLiteral("(https,bar.com)");
+ ASSERT_FALSE(cache.Get(certID, attrs, resultOut, timeOut));
+
+ // OCSP cache should not be isolated by containers for partitonKey.
+ attrs.mUserContextId = 1;
+ attrs.mPartitionKey.AssignLiteral("(https,foo.com)");
+ ASSERT_TRUE(cache.Get(certID, attrs, resultOut, timeOut));
+
+ // OCSP cache should not exist for the OAs which has both attributes set.
+ attrs.mUserContextId = 0;
+ attrs.mFirstPartyDomain.AssignLiteral("foo.com");
+ attrs.mPartitionKey.AssignLiteral("(https,foo.com)");
+ ASSERT_FALSE(cache.Get(certID, attrs, resultOut, timeOut));
+}