Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/manager/ssl/tests/gtest/OCSPCacheTest.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 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/. */
6
7
#include "CertVerifier.h"
8
#include "OCSPCache.h"
9
#include "gtest/gtest.h"
10
#include "mozilla/BasePrincipal.h"
11
#include "mozilla/Casting.h"
12
#include "mozilla/Sprintf.h"
13
#include "nss.h"
14
#include "pkix/pkixtypes.h"
15
#include "pkixtestutil.h"
16
#include "prerr.h"
17
#include "secerr.h"
18
19
using namespace mozilla::pkix;
20
using namespace mozilla::pkix::test;
21
22
using mozilla::OriginAttributes;
23
24
template <size_t N>
25
inline Input
26
LiteralInput(const char(&valueString)[N])
27
12
{
28
12
  // Ideally we would use mozilla::BitwiseCast() here rather than
29
12
  // reinterpret_cast for better type checking, but the |N - 1| part trips
30
12
  // static asserts.
31
12
  return Input(reinterpret_cast<const uint8_t(&)[N - 1]>(valueString));
32
12
}
mozilla::pkix::Input LiteralInput<11ul>(char const (&) [11ul])
Line
Count
Source
27
3
{
28
3
  // Ideally we would use mozilla::BitwiseCast() here rather than
29
3
  // reinterpret_cast for better type checking, but the |N - 1| part trips
30
3
  // static asserts.
31
3
  return Input(reinterpret_cast<const uint8_t(&)[N - 1]>(valueString));
32
3
}
mozilla::pkix::Input LiteralInput<7ul>(char const (&) [7ul])
Line
Count
Source
27
6
{
28
6
  // Ideally we would use mozilla::BitwiseCast() here rather than
29
6
  // reinterpret_cast for better type checking, but the |N - 1| part trips
30
6
  // static asserts.
31
6
  return Input(reinterpret_cast<const uint8_t(&)[N - 1]>(valueString));
32
6
}
mozilla::pkix::Input LiteralInput<5ul>(char const (&) [5ul])
Line
Count
Source
27
3
{
28
3
  // Ideally we would use mozilla::BitwiseCast() here rather than
29
3
  // reinterpret_cast for better type checking, but the |N - 1| part trips
30
3
  // static asserts.
31
3
  return Input(reinterpret_cast<const uint8_t(&)[N - 1]>(valueString));
32
3
}
Unexecuted instantiation: mozilla::pkix::Input LiteralInput<4ul>(char const (&) [4ul])
33
34
const int MaxCacheEntries = 1024;
35
36
class psm_OCSPCacheTest : public ::testing::Test
37
{
38
protected:
39
0
  psm_OCSPCacheTest() : now(Now()) { }
40
41
  static void SetUpTestCase()
42
0
  {
43
0
    NSS_NoDB_Init(nullptr);
44
0
  }
45
46
  const Time now;
47
  mozilla::psm::OCSPCache cache;
48
};
49
50
static void
51
PutAndGet(mozilla::psm::OCSPCache& cache, const CertID& certID, Result result,
52
          Time time,
53
          const OriginAttributes& originAttributes = OriginAttributes())
54
0
{
55
0
  // The first time is thisUpdate. The second is validUntil.
56
0
  // The caller is expecting the validUntil returned with Get
57
0
  // to be equal to the passed-in time. Since these values will
58
0
  // be different in practice, make thisUpdate less than validUntil.
59
0
  Time thisUpdate(time);
60
0
  ASSERT_EQ(Success, thisUpdate.SubtractSeconds(10));
61
0
  Result rv = cache.Put(certID, originAttributes, result, thisUpdate, time);
62
0
  ASSERT_TRUE(rv == Success);
63
0
  Result resultOut;
64
0
  Time timeOut(Time::uninitialized);
65
0
  ASSERT_TRUE(cache.Get(certID, originAttributes, resultOut, timeOut));
66
0
  ASSERT_EQ(result, resultOut);
67
0
  ASSERT_EQ(time, timeOut);
68
0
}
69
70
Input fakeIssuer1(LiteralInput("CN=issuer1"));
71
Input fakeKey000(LiteralInput("key000"));
72
Input fakeKey001(LiteralInput("key001"));
73
Input fakeSerial0000(LiteralInput("0000"));
74
75
TEST_F(psm_OCSPCacheTest, TestPutAndGet)
76
0
{
77
0
  Input fakeSerial000(LiteralInput("000"));
78
0
  Input fakeSerial001(LiteralInput("001"));
79
0
80
0
  SCOPED_TRACE("");
81
0
  PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001),
82
0
            Success, now);
83
0
  Result resultOut;
84
0
  Time timeOut(Time::uninitialized);
85
0
  ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000),
86
0
                         OriginAttributes(), resultOut, timeOut));
87
0
}
88
89
TEST_F(psm_OCSPCacheTest, TestVariousGets)
90
0
{
91
0
  SCOPED_TRACE("");
92
0
  for (int i = 0; i < MaxCacheEntries; i++) {
93
0
    uint8_t serialBuf[8];
94
0
    snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
95
0
             "%04d", i);
96
0
    Input fakeSerial;
97
0
    ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
98
0
    Time timeIn(now);
99
0
    ASSERT_EQ(Success, timeIn.AddSeconds(i));
100
0
    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
101
0
              Success, timeIn);
102
0
  }
103
0
104
0
  Time timeIn(now);
105
0
  Result resultOut;
106
0
  Time timeOut(Time::uninitialized);
107
0
108
0
  // This will be at the end of the list in the cache
109
0
  CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000);
110
0
  ASSERT_TRUE(cache.Get(cert0000, OriginAttributes(), resultOut, timeOut));
111
0
  ASSERT_EQ(Success, resultOut);
112
0
  ASSERT_EQ(timeIn, timeOut);
113
0
  // Once we access it, it goes to the front
114
0
  ASSERT_TRUE(cache.Get(cert0000, OriginAttributes(), resultOut, timeOut));
115
0
  ASSERT_EQ(Success, resultOut);
116
0
  ASSERT_EQ(timeIn, timeOut);
117
0
118
0
  // This will be in the middle
119
0
  Time timeInPlus512(now);
120
0
  ASSERT_EQ(Success, timeInPlus512.AddSeconds(512));
121
0
122
0
  static const Input fakeSerial0512(LiteralInput("0512"));
123
0
  CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512);
124
0
  ASSERT_TRUE(cache.Get(cert0512, OriginAttributes(), resultOut, timeOut));
125
0
  ASSERT_EQ(Success, resultOut);
126
0
  ASSERT_EQ(timeInPlus512, timeOut);
127
0
  ASSERT_TRUE(cache.Get(cert0512, OriginAttributes(), resultOut, timeOut));
128
0
  ASSERT_EQ(Success, resultOut);
129
0
  ASSERT_EQ(timeInPlus512, timeOut);
130
0
131
0
  // We've never seen this certificate
132
0
  static const Input fakeSerial1111(LiteralInput("1111"));
133
0
  ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111),
134
0
                         OriginAttributes(), resultOut, timeOut));
135
0
}
136
137
TEST_F(psm_OCSPCacheTest, TestEviction)
138
0
{
139
0
  SCOPED_TRACE("");
140
0
  // By putting more distinct entries in the cache than it can hold,
141
0
  // we cause the least recently used entry to be evicted.
142
0
  for (int i = 0; i < MaxCacheEntries + 1; i++) {
143
0
    uint8_t serialBuf[8];
144
0
    snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
145
0
             "%04d", i);
146
0
    Input fakeSerial;
147
0
    ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
148
0
    Time timeIn(now);
149
0
    ASSERT_EQ(Success, timeIn.AddSeconds(i));
150
0
    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
151
0
              Success, timeIn);
152
0
  }
153
0
154
0
  Result resultOut;
155
0
  Time timeOut(Time::uninitialized);
156
0
  ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000),
157
0
                         OriginAttributes(), resultOut, timeOut));
158
0
}
159
160
TEST_F(psm_OCSPCacheTest, TestNoEvictionForRevokedResponses)
161
0
{
162
0
  SCOPED_TRACE("");
163
0
  CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000);
164
0
  Time timeIn(now);
165
0
  PutAndGet(cache, notEvicted, Result::ERROR_REVOKED_CERTIFICATE, timeIn);
166
0
  // By putting more distinct entries in the cache than it can hold,
167
0
  // we cause the least recently used entry that isn't revoked to be evicted.
168
0
  for (int i = 1; i < MaxCacheEntries + 1; i++) {
169
0
    uint8_t serialBuf[8];
170
0
    snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
171
0
             "%04d", i);
172
0
    Input fakeSerial;
173
0
    ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
174
0
    Time timeIn(now);
175
0
    ASSERT_EQ(Success, timeIn.AddSeconds(i));
176
0
    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
177
0
              Success, timeIn);
178
0
  }
179
0
  Result resultOut;
180
0
  Time timeOut(Time::uninitialized);
181
0
  ASSERT_TRUE(cache.Get(notEvicted, OriginAttributes(), resultOut, timeOut));
182
0
  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
183
0
  ASSERT_EQ(timeIn, timeOut);
184
0
185
0
  Input fakeSerial0001(LiteralInput("0001"));
186
0
  CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001);
187
0
  ASSERT_FALSE(cache.Get(evicted, OriginAttributes(), resultOut, timeOut));
188
0
}
189
190
TEST_F(psm_OCSPCacheTest, TestEverythingIsRevoked)
191
0
{
192
0
  SCOPED_TRACE("");
193
0
  Time timeIn(now);
194
0
  // Fill up the cache with revoked responses.
195
0
  for (int i = 0; i < MaxCacheEntries; i++) {
196
0
    uint8_t serialBuf[8];
197
0
    snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
198
0
             "%04d", i);
199
0
    Input fakeSerial;
200
0
    ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
201
0
    Time timeIn(now);
202
0
    ASSERT_EQ(Success, timeIn.AddSeconds(i));
203
0
    PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
204
0
              Result::ERROR_REVOKED_CERTIFICATE, timeIn);
205
0
  }
206
0
  static const Input fakeSerial1025(LiteralInput("1025"));
207
0
  CertID good(fakeIssuer1, fakeKey000, fakeSerial1025);
208
0
  // This will "succeed", allowing verification to continue. However,
209
0
  // nothing was actually put in the cache.
210
0
  Time timeInPlus1025(timeIn);
211
0
  ASSERT_EQ(Success, timeInPlus1025.AddSeconds(1025));
212
0
  Time timeInPlus1025Minus50(timeInPlus1025);
213
0
  ASSERT_EQ(Success, timeInPlus1025Minus50.SubtractSeconds(50));
214
0
  Result result = cache.Put(good, OriginAttributes(), Success, timeInPlus1025Minus50,
215
0
                            timeInPlus1025);
216
0
  ASSERT_EQ(Success, result);
217
0
  Result resultOut;
218
0
  Time timeOut(Time::uninitialized);
219
0
  ASSERT_FALSE(cache.Get(good, OriginAttributes(), resultOut, timeOut));
220
0
221
0
  static const Input fakeSerial1026(LiteralInput("1026"));
222
0
  CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026);
223
0
  // This will fail, causing verification to fail.
224
0
  Time timeInPlus1026(timeIn);
225
0
  ASSERT_EQ(Success, timeInPlus1026.AddSeconds(1026));
226
0
  Time timeInPlus1026Minus50(timeInPlus1026);
227
0
  ASSERT_EQ(Success, timeInPlus1026Minus50.SubtractSeconds(50));
228
0
  result = cache.Put(revoked, OriginAttributes(), Result::ERROR_REVOKED_CERTIFICATE,
229
0
                     timeInPlus1026Minus50, timeInPlus1026);
230
0
  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, result);
231
0
}
232
233
TEST_F(psm_OCSPCacheTest, VariousIssuers)
234
0
{
235
0
  SCOPED_TRACE("");
236
0
  Time timeIn(now);
237
0
  static const Input fakeIssuer2(LiteralInput("CN=issuer2"));
238
0
  static const Input fakeSerial001(LiteralInput("001"));
239
0
  CertID subject(fakeIssuer1, fakeKey000, fakeSerial001);
240
0
  PutAndGet(cache, subject, Success, now);
241
0
  Result resultOut;
242
0
  Time timeOut(Time::uninitialized);
243
0
  ASSERT_TRUE(cache.Get(subject, OriginAttributes(), resultOut, timeOut));
244
0
  ASSERT_EQ(Success, resultOut);
245
0
  ASSERT_EQ(timeIn, timeOut);
246
0
  // Test that we don't match a different issuer DN
247
0
  ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001),
248
0
                         OriginAttributes(), resultOut, timeOut));
249
0
  // Test that we don't match a different issuer key
250
0
  ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001),
251
0
                         OriginAttributes(), resultOut, timeOut));
252
0
}
253
254
TEST_F(psm_OCSPCacheTest, Times)
255
0
{
256
0
  SCOPED_TRACE("");
257
0
  CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
258
0
  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
259
0
            TimeFromElapsedSecondsAD(100));
260
0
  PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
261
0
  // This should not override the more recent entry.
262
0
  ASSERT_EQ(Success,
263
0
            cache.Put(certID, OriginAttributes(), Result::ERROR_OCSP_UNKNOWN_CERT,
264
0
                      TimeFromElapsedSecondsAD(100),
265
0
                      TimeFromElapsedSecondsAD(100)));
266
0
  Result resultOut;
267
0
  Time timeOut(Time::uninitialized);
268
0
  ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
269
0
  // Here we see the more recent time.
270
0
  ASSERT_EQ(Success, resultOut);
271
0
  ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
272
0
273
0
  // Result::ERROR_REVOKED_CERTIFICATE overrides everything
274
0
  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
275
0
            TimeFromElapsedSecondsAD(50));
276
0
}
277
278
TEST_F(psm_OCSPCacheTest, NetworkFailure)
279
0
{
280
0
  SCOPED_TRACE("");
281
0
  CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
282
0
  PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED,
283
0
            TimeFromElapsedSecondsAD(100));
284
0
  PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
285
0
  // This should not override the already present entry.
286
0
  ASSERT_EQ(Success,
287
0
            cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
288
0
                      TimeFromElapsedSecondsAD(300),
289
0
                      TimeFromElapsedSecondsAD(350)));
290
0
  Result resultOut;
291
0
  Time timeOut(Time::uninitialized);
292
0
  ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
293
0
  ASSERT_EQ(Success, resultOut);
294
0
  ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
295
0
296
0
  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
297
0
            TimeFromElapsedSecondsAD(400));
298
0
  // This should not override the already present entry.
299
0
  ASSERT_EQ(Success,
300
0
            cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
301
0
                      TimeFromElapsedSecondsAD(500),
302
0
                      TimeFromElapsedSecondsAD(550)));
303
0
  ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
304
0
  ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, resultOut);
305
0
  ASSERT_EQ(TimeFromElapsedSecondsAD(400), timeOut);
306
0
307
0
  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
308
0
            TimeFromElapsedSecondsAD(600));
309
0
  // This should not override the already present entry.
310
0
  ASSERT_EQ(Success,
311
0
            cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
312
0
                      TimeFromElapsedSecondsAD(700),
313
0
                      TimeFromElapsedSecondsAD(750)));
314
0
  ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
315
0
  ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
316
0
  ASSERT_EQ(TimeFromElapsedSecondsAD(600), timeOut);
317
0
}
318
319
TEST_F(psm_OCSPCacheTest, TestOriginAttributes)
320
0
{
321
0
  CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
322
0
323
0
  SCOPED_TRACE("");
324
0
  OriginAttributes attrs;
325
0
  attrs.mFirstPartyDomain.AssignLiteral("foo.com");
326
0
  PutAndGet(cache, certID, Success, now, attrs);
327
0
328
0
  Result resultOut;
329
0
  Time timeOut(Time::uninitialized);
330
0
  attrs.mFirstPartyDomain.AssignLiteral("bar.com");
331
0
  ASSERT_FALSE(cache.Get(certID, attrs, resultOut, timeOut));
332
0
333
0
  // OCSP cache should not be isolated by containers.
334
0
  attrs.mUserContextId = 1;
335
0
  attrs.mFirstPartyDomain.AssignLiteral("foo.com");
336
0
  ASSERT_TRUE(cache.Get(certID, attrs, resultOut, timeOut));
337
0
}