/src/mozilla-central/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.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 2014 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 | | #include "pkixder.h" |
26 | | #include "pkixgtest.h" |
27 | | |
28 | | using namespace mozilla::pkix; |
29 | | using namespace mozilla::pkix::test; |
30 | | |
31 | | const uint16_t END_ENTITY_MAX_LIFETIME_IN_DAYS = 10; |
32 | | |
33 | | // Note that CheckRevocation is never called for OCSP signing certificates. |
34 | | class OCSPTestTrustDomain : public DefaultCryptoTrustDomain |
35 | | { |
36 | | public: |
37 | 0 | OCSPTestTrustDomain() { } |
38 | | |
39 | | Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&, |
40 | | Input, /*out*/ TrustLevel& trustLevel) |
41 | | /*non-final*/ override |
42 | 0 | { |
43 | 0 | EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity); |
44 | 0 | trustLevel = TrustLevel::InheritsTrust; |
45 | 0 | return Success; |
46 | 0 | } |
47 | | |
48 | | virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension, |
49 | | Input extensionData) override |
50 | 0 | { |
51 | 0 | if (extension == AuxiliaryExtension::SCTListFromOCSPResponse) { |
52 | 0 | signedCertificateTimestamps = InputToByteString(extensionData); |
53 | 0 | } else { |
54 | 0 | // We do not currently expect to receive any other extension here. |
55 | 0 | ADD_FAILURE(); |
56 | 0 | } |
57 | 0 | } |
58 | | |
59 | | ByteString signedCertificateTimestamps; |
60 | | }; |
61 | | |
62 | | namespace { |
63 | | char const* const rootName = "Test CA 1"; |
64 | 0 | void deleteCertID(CertID* certID) { delete certID; } |
65 | | } // namespace |
66 | | |
67 | | class pkixocsp_VerifyEncodedResponse : public ::testing::Test |
68 | | { |
69 | | public: |
70 | | static void SetUpTestCase() |
71 | 0 | { |
72 | 0 | rootKeyPair.reset(GenerateKeyPair()); |
73 | 0 | if (!rootKeyPair) { |
74 | 0 | abort(); |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | void SetUp() |
79 | 0 | { |
80 | 0 | rootNameDER = CNToDERName(rootName); |
81 | 0 | if (ENCODING_FAILED(rootNameDER)) { |
82 | 0 | abort(); |
83 | 0 | } |
84 | 0 | Input rootNameDERInput; |
85 | 0 | if (rootNameDERInput.Init(rootNameDER.data(), rootNameDER.length()) |
86 | 0 | != Success) { |
87 | 0 | abort(); |
88 | 0 | } |
89 | 0 | |
90 | 0 | serialNumberDER = |
91 | 0 | CreateEncodedSerialNumber(static_cast<long>(++rootIssuedCount)); |
92 | 0 | if (ENCODING_FAILED(serialNumberDER)) { |
93 | 0 | abort(); |
94 | 0 | } |
95 | 0 | Input serialNumberDERInput; |
96 | 0 | if (serialNumberDERInput.Init(serialNumberDER.data(), |
97 | 0 | serialNumberDER.length()) != Success) { |
98 | 0 | abort(); |
99 | 0 | } |
100 | 0 | |
101 | 0 | Input rootSPKIDER; |
102 | 0 | if (rootSPKIDER.Init(rootKeyPair->subjectPublicKeyInfo.data(), |
103 | 0 | rootKeyPair->subjectPublicKeyInfo.length()) |
104 | 0 | != Success) { |
105 | 0 | abort(); |
106 | 0 | } |
107 | 0 | endEntityCertID.reset(new (std::nothrow) CertID(rootNameDERInput, rootSPKIDER, |
108 | 0 | serialNumberDERInput)); |
109 | 0 | if (!endEntityCertID) { |
110 | 0 | abort(); |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | static ScopedTestKeyPair rootKeyPair; |
115 | | static uint32_t rootIssuedCount; |
116 | | OCSPTestTrustDomain trustDomain; |
117 | | |
118 | | // endEntityCertID references rootKeyPair, rootNameDER, and serialNumberDER. |
119 | | ByteString rootNameDER; |
120 | | ByteString serialNumberDER; |
121 | | // endEntityCertID references rootKeyPair, rootNameDER, and serialNumberDER. |
122 | | ScopedPtr<CertID, deleteCertID> endEntityCertID; |
123 | | }; |
124 | | |
125 | | /*static*/ ScopedTestKeyPair pkixocsp_VerifyEncodedResponse::rootKeyPair; |
126 | | /*static*/ uint32_t pkixocsp_VerifyEncodedResponse::rootIssuedCount = 0; |
127 | | |
128 | | /////////////////////////////////////////////////////////////////////////////// |
129 | | // responseStatus |
130 | | |
131 | | struct WithoutResponseBytes |
132 | | { |
133 | | uint8_t responseStatus; |
134 | | Result expectedError; |
135 | | }; |
136 | | |
137 | | static const WithoutResponseBytes WITHOUT_RESPONSEBYTES[] = { |
138 | | { OCSPResponseContext::successful, Result::ERROR_OCSP_MALFORMED_RESPONSE }, |
139 | | { OCSPResponseContext::malformedRequest, Result::ERROR_OCSP_MALFORMED_REQUEST }, |
140 | | { OCSPResponseContext::internalError, Result::ERROR_OCSP_SERVER_ERROR }, |
141 | | { OCSPResponseContext::tryLater, Result::ERROR_OCSP_TRY_SERVER_LATER }, |
142 | | { 4/*unused*/, Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS }, |
143 | | { OCSPResponseContext::sigRequired, Result::ERROR_OCSP_REQUEST_NEEDS_SIG }, |
144 | | { OCSPResponseContext::unauthorized, Result::ERROR_OCSP_UNAUTHORIZED_REQUEST }, |
145 | | { OCSPResponseContext::unauthorized + 1, |
146 | | Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS |
147 | | }, |
148 | | }; |
149 | | |
150 | | class pkixocsp_VerifyEncodedResponse_WithoutResponseBytes |
151 | | : public pkixocsp_VerifyEncodedResponse |
152 | | , public ::testing::WithParamInterface<WithoutResponseBytes> |
153 | | { |
154 | | protected: |
155 | | ByteString CreateEncodedOCSPErrorResponse(uint8_t status) |
156 | 0 | { |
157 | 0 | static const Input EMPTY; |
158 | 0 | OCSPResponseContext context(CertID(EMPTY, EMPTY, EMPTY), |
159 | 0 | oneDayBeforeNow); |
160 | 0 | context.responseStatus = status; |
161 | 0 | context.skipResponseBytes = true; |
162 | 0 | return CreateEncodedOCSPResponse(context); |
163 | 0 | } |
164 | | }; |
165 | | |
166 | | TEST_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, CorrectErrorCode) |
167 | 0 | { |
168 | 0 | ByteString |
169 | 0 | responseString(CreateEncodedOCSPErrorResponse(GetParam().responseStatus)); |
170 | 0 | Input response; |
171 | 0 | ASSERT_EQ(Success, |
172 | 0 | response.Init(responseString.data(), responseString.length())); |
173 | 0 | bool expired; |
174 | 0 | ASSERT_EQ(GetParam().expectedError, |
175 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
176 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
177 | 0 | response, expired)); |
178 | 0 | } |
179 | | |
180 | | INSTANTIATE_TEST_CASE_P(pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, |
181 | | pkixocsp_VerifyEncodedResponse_WithoutResponseBytes, |
182 | | testing::ValuesIn(WITHOUT_RESPONSEBYTES)); |
183 | | |
184 | | /////////////////////////////////////////////////////////////////////////////// |
185 | | // "successful" responses |
186 | | |
187 | | namespace { |
188 | | |
189 | | // Alias for nullptr to aid readability in the code below. |
190 | | static const char* byKey = nullptr; |
191 | | |
192 | | } // namespace |
193 | | |
194 | | class pkixocsp_VerifyEncodedResponse_successful |
195 | | : public pkixocsp_VerifyEncodedResponse |
196 | | { |
197 | | public: |
198 | | void SetUp() |
199 | 0 | { |
200 | 0 | pkixocsp_VerifyEncodedResponse::SetUp(); |
201 | 0 | } |
202 | | |
203 | | static void SetUpTestCase() |
204 | 0 | { |
205 | 0 | pkixocsp_VerifyEncodedResponse::SetUpTestCase(); |
206 | 0 | } |
207 | | |
208 | | ByteString CreateEncodedOCSPSuccessfulResponse( |
209 | | OCSPResponseContext::CertStatus certStatus, |
210 | | const CertID& certID, |
211 | | /*optional*/ const char* signerName, |
212 | | const TestKeyPair& signerKeyPair, |
213 | | time_t producedAt, time_t thisUpdate, |
214 | | /*optional*/ const time_t* nextUpdate, |
215 | | const TestSignatureAlgorithm& signatureAlgorithm, |
216 | | /*optional*/ const ByteString* certs = nullptr, |
217 | | /*optional*/ OCSPResponseExtension* singleExtensions = nullptr, |
218 | | /*optional*/ OCSPResponseExtension* responseExtensions = nullptr) |
219 | 0 | { |
220 | 0 | OCSPResponseContext context(certID, producedAt); |
221 | 0 | if (signerName) { |
222 | 0 | context.signerNameDER = CNToDERName(signerName); |
223 | 0 | EXPECT_FALSE(ENCODING_FAILED(context.signerNameDER)); |
224 | 0 | } |
225 | 0 | context.signerKeyPair.reset(signerKeyPair.Clone()); |
226 | 0 | EXPECT_TRUE(context.signerKeyPair.get()); |
227 | 0 | context.responseStatus = OCSPResponseContext::successful; |
228 | 0 | context.producedAt = producedAt; |
229 | 0 | context.signatureAlgorithm = signatureAlgorithm; |
230 | 0 | context.certs = certs; |
231 | 0 | context.singleExtensions = singleExtensions; |
232 | 0 | context.responseExtensions = responseExtensions; |
233 | 0 |
|
234 | 0 | context.certStatus = static_cast<uint8_t>(certStatus); |
235 | 0 | context.thisUpdate = thisUpdate; |
236 | 0 | context.nextUpdate = nextUpdate ? *nextUpdate : 0; |
237 | 0 | context.includeNextUpdate = nextUpdate != nullptr; |
238 | 0 |
|
239 | 0 | return CreateEncodedOCSPResponse(context); |
240 | 0 | } |
241 | | }; |
242 | | |
243 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey) |
244 | 0 | { |
245 | 0 | ByteString responseString( |
246 | 0 | CreateEncodedOCSPSuccessfulResponse( |
247 | 0 | OCSPResponseContext::good, *endEntityCertID, byKey, |
248 | 0 | *rootKeyPair, oneDayBeforeNow, |
249 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
250 | 0 | sha256WithRSAEncryption())); |
251 | 0 | Input response; |
252 | 0 | ASSERT_EQ(Success, |
253 | 0 | response.Init(responseString.data(), responseString.length())); |
254 | 0 | bool expired; |
255 | 0 | ASSERT_EQ(Success, |
256 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, |
257 | 0 | Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS, |
258 | 0 | response, expired)); |
259 | 0 | ASSERT_FALSE(expired); |
260 | 0 | } |
261 | | |
262 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byName) |
263 | 0 | { |
264 | 0 | ByteString responseString( |
265 | 0 | CreateEncodedOCSPSuccessfulResponse( |
266 | 0 | OCSPResponseContext::good, *endEntityCertID, rootName, |
267 | 0 | *rootKeyPair, oneDayBeforeNow, |
268 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
269 | 0 | sha256WithRSAEncryption())); |
270 | 0 | Input response; |
271 | 0 | ASSERT_EQ(Success, |
272 | 0 | response.Init(responseString.data(), responseString.length())); |
273 | 0 | bool expired; |
274 | 0 | ASSERT_EQ(Success, |
275 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
276 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
277 | 0 | response, expired)); |
278 | 0 | ASSERT_FALSE(expired); |
279 | 0 | } |
280 | | |
281 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey_without_nextUpdate) |
282 | 0 | { |
283 | 0 | ByteString responseString( |
284 | 0 | CreateEncodedOCSPSuccessfulResponse( |
285 | 0 | OCSPResponseContext::good, *endEntityCertID, byKey, |
286 | 0 | *rootKeyPair, oneDayBeforeNow, |
287 | 0 | oneDayBeforeNow, nullptr, |
288 | 0 | sha256WithRSAEncryption())); |
289 | 0 | Input response; |
290 | 0 | ASSERT_EQ(Success, |
291 | 0 | response.Init(responseString.data(), responseString.length())); |
292 | 0 | bool expired; |
293 | 0 | ASSERT_EQ(Success, |
294 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
295 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
296 | 0 | response, expired)); |
297 | 0 | ASSERT_FALSE(expired); |
298 | 0 | } |
299 | | |
300 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, revoked) |
301 | 0 | { |
302 | 0 | ByteString responseString( |
303 | 0 | CreateEncodedOCSPSuccessfulResponse( |
304 | 0 | OCSPResponseContext::revoked, *endEntityCertID, byKey, |
305 | 0 | *rootKeyPair, oneDayBeforeNow, |
306 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
307 | 0 | sha256WithRSAEncryption())); |
308 | 0 | Input response; |
309 | 0 | ASSERT_EQ(Success, |
310 | 0 | response.Init(responseString.data(), responseString.length())); |
311 | 0 | bool expired; |
312 | 0 | ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, |
313 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
314 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
315 | 0 | response, expired)); |
316 | 0 | ASSERT_FALSE(expired); |
317 | 0 | } |
318 | | |
319 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, unknown) |
320 | 0 | { |
321 | 0 | ByteString responseString( |
322 | 0 | CreateEncodedOCSPSuccessfulResponse( |
323 | 0 | OCSPResponseContext::unknown, *endEntityCertID, byKey, |
324 | 0 | *rootKeyPair, oneDayBeforeNow, |
325 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
326 | 0 | sha256WithRSAEncryption())); |
327 | 0 | Input response; |
328 | 0 | ASSERT_EQ(Success, |
329 | 0 | response.Init(responseString.data(), responseString.length())); |
330 | 0 | bool expired; |
331 | 0 | ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, |
332 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
333 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
334 | 0 | response, expired)); |
335 | 0 | ASSERT_FALSE(expired); |
336 | 0 | } |
337 | | |
338 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, |
339 | | good_unsupportedSignatureAlgorithm) |
340 | 0 | { |
341 | 0 | ByteString responseString( |
342 | 0 | CreateEncodedOCSPSuccessfulResponse( |
343 | 0 | OCSPResponseContext::good, *endEntityCertID, byKey, |
344 | 0 | *rootKeyPair, oneDayBeforeNow, |
345 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
346 | 0 | md5WithRSAEncryption())); |
347 | 0 | Input response; |
348 | 0 | ASSERT_EQ(Success, |
349 | 0 | response.Init(responseString.data(), responseString.length())); |
350 | 0 | bool expired; |
351 | 0 | ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, |
352 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, |
353 | 0 | Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS, |
354 | 0 | response, expired)); |
355 | 0 | ASSERT_FALSE(expired); |
356 | 0 | } |
357 | | |
358 | | // Added for bug 1079436. The output variable validThrough represents the |
359 | | // latest time for which VerifyEncodedOCSPResponse will succeed, which is |
360 | | // different from the nextUpdate time in the OCSP response due to the slop we |
361 | | // add for time comparisons to deal with clock skew. |
362 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, check_validThrough) |
363 | 0 | { |
364 | 0 | ByteString responseString( |
365 | 0 | CreateEncodedOCSPSuccessfulResponse( |
366 | 0 | OCSPResponseContext::good, *endEntityCertID, byKey, |
367 | 0 | *rootKeyPair, oneDayBeforeNow, |
368 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
369 | 0 | sha256WithRSAEncryption())); |
370 | 0 | Time validThrough(Time::uninitialized); |
371 | 0 | { |
372 | 0 | Input response; |
373 | 0 | ASSERT_EQ(Success, |
374 | 0 | response.Init(responseString.data(), responseString.length())); |
375 | 0 | bool expired; |
376 | 0 | ASSERT_EQ(Success, |
377 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, |
378 | 0 | Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS, |
379 | 0 | response, expired, nullptr, |
380 | 0 | &validThrough)); |
381 | 0 | ASSERT_FALSE(expired); |
382 | 0 | // The response was created to be valid until one day after now, so the |
383 | 0 | // value we got for validThrough should be after that. |
384 | 0 | Time oneDayAfterNowAsPKIXTime( |
385 | 0 | TimeFromEpochInSeconds(static_cast<uint64_t>(oneDayAfterNow))); |
386 | 0 | ASSERT_TRUE(validThrough > oneDayAfterNowAsPKIXTime); |
387 | 0 | } |
388 | 0 | { |
389 | 0 | Input response; |
390 | 0 | ASSERT_EQ(Success, |
391 | 0 | response.Init(responseString.data(), responseString.length())); |
392 | 0 | bool expired; |
393 | 0 | // Given validThrough from a previous verification, this response should be |
394 | 0 | // valid through that time. |
395 | 0 | ASSERT_EQ(Success, |
396 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, |
397 | 0 | validThrough, END_ENTITY_MAX_LIFETIME_IN_DAYS, |
398 | 0 | response, expired)); |
399 | 0 | ASSERT_FALSE(expired); |
400 | 0 | } |
401 | 0 | { |
402 | 0 | Time noLongerValid(validThrough); |
403 | 0 | ASSERT_EQ(Success, noLongerValid.AddSeconds(1)); |
404 | 0 | Input response; |
405 | 0 | ASSERT_EQ(Success, |
406 | 0 | response.Init(responseString.data(), responseString.length())); |
407 | 0 | bool expired; |
408 | 0 | // The verification time is now after when the response will be considered |
409 | 0 | // valid. |
410 | 0 | ASSERT_EQ(Result::ERROR_OCSP_OLD_RESPONSE, |
411 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, |
412 | 0 | noLongerValid, END_ENTITY_MAX_LIFETIME_IN_DAYS, |
413 | 0 | response, expired)); |
414 | 0 | ASSERT_TRUE(expired); |
415 | 0 | } |
416 | 0 | } |
417 | | |
418 | | TEST_F(pkixocsp_VerifyEncodedResponse_successful, ct_extension) |
419 | 0 | { |
420 | 0 | // python DottedOIDToCode.py --tlv |
421 | 0 | // id_ocsp_singleExtensionSctList 1.3.6.1.4.1.11129.2.4.5 |
422 | 0 | static const uint8_t tlv_id_ocsp_singleExtensionSctList[] = { |
423 | 0 | 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05 |
424 | 0 | }; |
425 | 0 | static const uint8_t dummySctList[] = { |
426 | 0 | 0x01, 0x02, 0x03, 0x04, 0x05 |
427 | 0 | }; |
428 | 0 |
|
429 | 0 | OCSPResponseExtension ctExtension; |
430 | 0 | ctExtension.id = BytesToByteString(tlv_id_ocsp_singleExtensionSctList); |
431 | 0 | // SignedCertificateTimestampList structure is encoded as an OCTET STRING |
432 | 0 | // within the extension value (see RFC 6962 section 3.3). |
433 | 0 | // pkix decodes it internally and returns the actual structure. |
434 | 0 | ctExtension.value = TLV(der::OCTET_STRING, BytesToByteString(dummySctList)); |
435 | 0 |
|
436 | 0 | ByteString responseString( |
437 | 0 | CreateEncodedOCSPSuccessfulResponse( |
438 | 0 | OCSPResponseContext::good, *endEntityCertID, byKey, |
439 | 0 | *rootKeyPair, oneDayBeforeNow, |
440 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
441 | 0 | sha256WithRSAEncryption(), |
442 | 0 | /*certs*/ nullptr, |
443 | 0 | &ctExtension)); |
444 | 0 | Input response; |
445 | 0 | ASSERT_EQ(Success, |
446 | 0 | response.Init(responseString.data(), responseString.length())); |
447 | 0 |
|
448 | 0 | bool expired; |
449 | 0 | ASSERT_EQ(Success, |
450 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, |
451 | 0 | Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS, |
452 | 0 | response, expired)); |
453 | 0 | ASSERT_FALSE(expired); |
454 | 0 | ASSERT_EQ(BytesToByteString(dummySctList), |
455 | 0 | trustDomain.signedCertificateTimestamps); |
456 | 0 | } |
457 | | |
458 | | /////////////////////////////////////////////////////////////////////////////// |
459 | | // indirect responses (signed by a delegated OCSP responder cert) |
460 | | |
461 | | class pkixocsp_VerifyEncodedResponse_DelegatedResponder |
462 | | : public pkixocsp_VerifyEncodedResponse_successful |
463 | | { |
464 | | protected: |
465 | | // certSubjectName should be unique for each call. This way, we avoid any |
466 | | // issues with NSS caching the certificates internally. For the same reason, |
467 | | // we generate a new keypair on each call. Either one of these should be |
468 | | // sufficient to avoid issues with the NSS cache, but we do both to be |
469 | | // cautious. |
470 | | // |
471 | | // signerName should be byKey to use the byKey ResponderID construction, or |
472 | | // another value (usually equal to certSubjectName) to use the byName |
473 | | // ResponderID construction. |
474 | | // |
475 | | // certSignatureAlgorithm specifies the signature algorithm that the |
476 | | // certificate will be signed with, not the OCSP response. |
477 | | // |
478 | | // If signerEKU is omitted, then the certificate will have the |
479 | | // id-kp-OCSPSigning EKU. If signerEKU is SEC_OID_UNKNOWN then it will not |
480 | | // have any EKU extension. Otherwise, the certificate will have the given |
481 | | // EKU. |
482 | | ByteString CreateEncodedIndirectOCSPSuccessfulResponse( |
483 | | const char* certSubjectName, |
484 | | OCSPResponseContext::CertStatus certStatus, |
485 | | const char* signerName, |
486 | | const TestSignatureAlgorithm& certSignatureAlgorithm, |
487 | | /*optional*/ const Input* signerEKUDER = &OCSPSigningEKUDER, |
488 | | /*optional, out*/ ByteString* signerDEROut = nullptr) |
489 | 0 | { |
490 | 0 | assert(certSubjectName); |
491 | 0 |
|
492 | 0 | const ByteString extensions[] = { |
493 | 0 | signerEKUDER |
494 | 0 | ? CreateEncodedEKUExtension(*signerEKUDER, Critical::No) |
495 | 0 | : ByteString(), |
496 | 0 | ByteString() |
497 | 0 | }; |
498 | 0 | ScopedTestKeyPair signerKeyPair(GenerateKeyPair()); |
499 | 0 | ByteString signerDER(CreateEncodedCertificate( |
500 | 0 | ++rootIssuedCount, certSignatureAlgorithm, |
501 | 0 | rootName, oneDayBeforeNow, oneDayAfterNow, |
502 | 0 | certSubjectName, *signerKeyPair, |
503 | 0 | signerEKUDER ? extensions : nullptr, |
504 | 0 | *rootKeyPair)); |
505 | 0 | EXPECT_FALSE(ENCODING_FAILED(signerDER)); |
506 | 0 | if (signerDEROut) { |
507 | 0 | *signerDEROut = signerDER; |
508 | 0 | } |
509 | 0 |
|
510 | 0 | ByteString signerNameDER; |
511 | 0 | if (signerName) { |
512 | 0 | signerNameDER = CNToDERName(signerName); |
513 | 0 | EXPECT_FALSE(ENCODING_FAILED(signerNameDER)); |
514 | 0 | } |
515 | 0 | ByteString certs[] = { signerDER, ByteString() }; |
516 | 0 | return CreateEncodedOCSPSuccessfulResponse(certStatus, *endEntityCertID, |
517 | 0 | signerName, *signerKeyPair, |
518 | 0 | oneDayBeforeNow, |
519 | 0 | oneDayBeforeNow, |
520 | 0 | &oneDayAfterNow, |
521 | 0 | sha256WithRSAEncryption(), |
522 | 0 | certs); |
523 | 0 | } |
524 | | |
525 | | static ByteString CreateEncodedCertificate(uint32_t serialNumber, |
526 | | const TestSignatureAlgorithm& signatureAlg, |
527 | | const char* issuer, |
528 | | time_t notBefore, |
529 | | time_t notAfter, |
530 | | const char* subject, |
531 | | const TestKeyPair& subjectKeyPair, |
532 | | /*optional*/ const ByteString* extensions, |
533 | | const TestKeyPair& signerKeyPair) |
534 | 0 | { |
535 | 0 | ByteString serialNumberDER(CreateEncodedSerialNumber( |
536 | 0 | static_cast<long>(serialNumber))); |
537 | 0 | if (ENCODING_FAILED(serialNumberDER)) { |
538 | 0 | return ByteString(); |
539 | 0 | } |
540 | 0 | ByteString issuerDER(CNToDERName(issuer)); |
541 | 0 | if (ENCODING_FAILED(issuerDER)) { |
542 | 0 | return ByteString(); |
543 | 0 | } |
544 | 0 | ByteString subjectDER(CNToDERName(subject)); |
545 | 0 | if (ENCODING_FAILED(subjectDER)) { |
546 | 0 | return ByteString(); |
547 | 0 | } |
548 | 0 | return ::mozilla::pkix::test::CreateEncodedCertificate( |
549 | 0 | v3, signatureAlg, serialNumberDER, |
550 | 0 | issuerDER, notBefore, notAfter, |
551 | 0 | subjectDER, subjectKeyPair, extensions, |
552 | 0 | signerKeyPair, signatureAlg); |
553 | 0 | } |
554 | | |
555 | | static const Input OCSPSigningEKUDER; |
556 | | }; |
557 | | |
558 | | /*static*/ const Input pkixocsp_VerifyEncodedResponse_DelegatedResponder:: |
559 | | OCSPSigningEKUDER(tlv_id_kp_OCSPSigning); |
560 | | |
561 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byKey) |
562 | 0 | { |
563 | 0 | ByteString responseString( |
564 | 0 | CreateEncodedIndirectOCSPSuccessfulResponse( |
565 | 0 | "good_indirect_byKey", OCSPResponseContext::good, |
566 | 0 | byKey, sha256WithRSAEncryption())); |
567 | 0 | Input response; |
568 | 0 | ASSERT_EQ(Success, |
569 | 0 | response.Init(responseString.data(), responseString.length())); |
570 | 0 | bool expired; |
571 | 0 | ASSERT_EQ(Success, |
572 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
573 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
574 | 0 | response, expired)); |
575 | 0 | ASSERT_FALSE(expired); |
576 | 0 | } |
577 | | |
578 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byName) |
579 | 0 | { |
580 | 0 | ByteString responseString( |
581 | 0 | CreateEncodedIndirectOCSPSuccessfulResponse( |
582 | 0 | "good_indirect_byName", OCSPResponseContext::good, |
583 | 0 | "good_indirect_byName", sha256WithRSAEncryption())); |
584 | 0 | Input response; |
585 | 0 | ASSERT_EQ(Success, |
586 | 0 | response.Init(responseString.data(), responseString.length())); |
587 | 0 | bool expired; |
588 | 0 | ASSERT_EQ(Success, |
589 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
590 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
591 | 0 | response, expired)); |
592 | 0 | ASSERT_FALSE(expired); |
593 | 0 | } |
594 | | |
595 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, |
596 | | good_byKey_missing_signer) |
597 | 0 | { |
598 | 0 | ScopedTestKeyPair missingSignerKeyPair(GenerateKeyPair()); |
599 | 0 | ASSERT_TRUE(missingSignerKeyPair.get()); |
600 | 0 |
|
601 | 0 | ByteString responseString( |
602 | 0 | CreateEncodedOCSPSuccessfulResponse( |
603 | 0 | OCSPResponseContext::good, *endEntityCertID, byKey, |
604 | 0 | *missingSignerKeyPair, oneDayBeforeNow, |
605 | 0 | oneDayBeforeNow, nullptr, |
606 | 0 | sha256WithRSAEncryption())); |
607 | 0 | Input response; |
608 | 0 | ASSERT_EQ(Success, |
609 | 0 | response.Init(responseString.data(), responseString.length())); |
610 | 0 | bool expired; |
611 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
612 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
613 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
614 | 0 | response, expired)); |
615 | 0 | ASSERT_FALSE(expired); |
616 | 0 | } |
617 | | |
618 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, |
619 | | good_byName_missing_signer) |
620 | 0 | { |
621 | 0 | ScopedTestKeyPair missingSignerKeyPair(GenerateKeyPair()); |
622 | 0 | ASSERT_TRUE(missingSignerKeyPair.get()); |
623 | 0 | ByteString responseString( |
624 | 0 | CreateEncodedOCSPSuccessfulResponse( |
625 | 0 | OCSPResponseContext::good, *endEntityCertID, |
626 | 0 | "missing", *missingSignerKeyPair, |
627 | 0 | oneDayBeforeNow, oneDayBeforeNow, nullptr, |
628 | 0 | sha256WithRSAEncryption())); |
629 | 0 | Input response; |
630 | 0 | ASSERT_EQ(Success, |
631 | 0 | response.Init(responseString.data(), responseString.length())); |
632 | 0 | bool expired; |
633 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
634 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
635 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
636 | 0 | response, expired)); |
637 | 0 | ASSERT_FALSE(expired); |
638 | 0 | } |
639 | | |
640 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_expired) |
641 | 0 | { |
642 | 0 | static const char* signerName = "good_indirect_expired"; |
643 | 0 |
|
644 | 0 | const ByteString extensions[] = { |
645 | 0 | CreateEncodedEKUExtension(OCSPSigningEKUDER, Critical::No), |
646 | 0 | ByteString() |
647 | 0 | }; |
648 | 0 |
|
649 | 0 | ScopedTestKeyPair signerKeyPair(GenerateKeyPair()); |
650 | 0 | ByteString signerDER(CreateEncodedCertificate( |
651 | 0 | ++rootIssuedCount, sha256WithRSAEncryption(), |
652 | 0 | rootName, |
653 | 0 | tenDaysBeforeNow, |
654 | 0 | twoDaysBeforeNow, |
655 | 0 | signerName, *signerKeyPair, extensions, |
656 | 0 | *rootKeyPair)); |
657 | 0 | ASSERT_FALSE(ENCODING_FAILED(signerDER)); |
658 | 0 |
|
659 | 0 | ByteString certs[] = { signerDER, ByteString() }; |
660 | 0 | ByteString responseString( |
661 | 0 | CreateEncodedOCSPSuccessfulResponse( |
662 | 0 | OCSPResponseContext::good, *endEntityCertID, |
663 | 0 | signerName, *signerKeyPair, oneDayBeforeNow, |
664 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
665 | 0 | sha256WithRSAEncryption(), certs)); |
666 | 0 | Input response; |
667 | 0 | ASSERT_EQ(Success, |
668 | 0 | response.Init(responseString.data(), responseString.length())); |
669 | 0 | bool expired; |
670 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
671 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
672 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
673 | 0 | response, expired)); |
674 | 0 | } |
675 | | |
676 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_future) |
677 | 0 | { |
678 | 0 | static const char* signerName = "good_indirect_future"; |
679 | 0 |
|
680 | 0 | const ByteString extensions[] = { |
681 | 0 | CreateEncodedEKUExtension(OCSPSigningEKUDER, Critical::No), |
682 | 0 | ByteString() |
683 | 0 | }; |
684 | 0 |
|
685 | 0 | ScopedTestKeyPair signerKeyPair(GenerateKeyPair()); |
686 | 0 | ByteString signerDER(CreateEncodedCertificate( |
687 | 0 | ++rootIssuedCount, sha256WithRSAEncryption(), |
688 | 0 | rootName, |
689 | 0 | twoDaysAfterNow, |
690 | 0 | tenDaysAfterNow, |
691 | 0 | signerName, *signerKeyPair, extensions, |
692 | 0 | *rootKeyPair)); |
693 | 0 | ASSERT_FALSE(ENCODING_FAILED(signerDER)); |
694 | 0 |
|
695 | 0 | ByteString certs[] = { signerDER, ByteString() }; |
696 | 0 | ByteString responseString( |
697 | 0 | CreateEncodedOCSPSuccessfulResponse( |
698 | 0 | OCSPResponseContext::good, *endEntityCertID, |
699 | 0 | signerName, *signerKeyPair, oneDayBeforeNow, |
700 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
701 | 0 | sha256WithRSAEncryption(), certs)); |
702 | 0 | Input response; |
703 | 0 | ASSERT_EQ(Success, |
704 | 0 | response.Init(responseString.data(), responseString.length())); |
705 | 0 | bool expired; |
706 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
707 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
708 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
709 | 0 | response, expired)); |
710 | 0 | ASSERT_FALSE(expired); |
711 | 0 | } |
712 | | |
713 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_no_eku) |
714 | 0 | { |
715 | 0 | ByteString responseString( |
716 | 0 | CreateEncodedIndirectOCSPSuccessfulResponse( |
717 | 0 | "good_indirect_wrong_eku", |
718 | 0 | OCSPResponseContext::good, byKey, |
719 | 0 | sha256WithRSAEncryption(), nullptr)); |
720 | 0 | Input response; |
721 | 0 | ASSERT_EQ(Success, |
722 | 0 | response.Init(responseString.data(), responseString.length())); |
723 | 0 | bool expired; |
724 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
725 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
726 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
727 | 0 | response, expired)); |
728 | 0 | ASSERT_FALSE(expired); |
729 | 0 | } |
730 | | |
731 | | static const Input serverAuthEKUDER(tlv_id_kp_serverAuth); |
732 | | |
733 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, |
734 | | good_indirect_wrong_eku) |
735 | 0 | { |
736 | 0 | ByteString responseString( |
737 | 0 | CreateEncodedIndirectOCSPSuccessfulResponse( |
738 | 0 | "good_indirect_wrong_eku", |
739 | 0 | OCSPResponseContext::good, byKey, |
740 | 0 | sha256WithRSAEncryption(), &serverAuthEKUDER)); |
741 | 0 | Input response; |
742 | 0 | ASSERT_EQ(Success, |
743 | 0 | response.Init(responseString.data(), responseString.length())); |
744 | 0 | bool expired; |
745 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
746 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
747 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
748 | 0 | response, expired)); |
749 | 0 | ASSERT_FALSE(expired); |
750 | 0 | } |
751 | | |
752 | | // Test that signature of OCSP response signer cert is verified |
753 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_tampered_eku) |
754 | 0 | { |
755 | 0 | ByteString tamperedResponse( |
756 | 0 | CreateEncodedIndirectOCSPSuccessfulResponse( |
757 | 0 | "good_indirect_tampered_eku", |
758 | 0 | OCSPResponseContext::good, byKey, |
759 | 0 | sha256WithRSAEncryption(), &serverAuthEKUDER)); |
760 | 0 | ASSERT_EQ(Success, |
761 | 0 | TamperOnce(tamperedResponse, |
762 | 0 | ByteString(tlv_id_kp_serverAuth, |
763 | 0 | sizeof(tlv_id_kp_serverAuth)), |
764 | 0 | ByteString(tlv_id_kp_OCSPSigning, |
765 | 0 | sizeof(tlv_id_kp_OCSPSigning)))); |
766 | 0 | Input tamperedResponseInput; |
767 | 0 | ASSERT_EQ(Success, tamperedResponseInput.Init(tamperedResponse.data(), |
768 | 0 | tamperedResponse.length())); |
769 | 0 | bool expired; |
770 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
771 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
772 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
773 | 0 | tamperedResponseInput, expired)); |
774 | 0 | ASSERT_FALSE(expired); |
775 | 0 | } |
776 | | |
777 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_unknown_issuer) |
778 | 0 | { |
779 | 0 | static const char* subCAName = "good_indirect_unknown_issuer sub-CA"; |
780 | 0 | static const char* signerName = "good_indirect_unknown_issuer OCSP signer"; |
781 | 0 |
|
782 | 0 | // unknown issuer |
783 | 0 | ScopedTestKeyPair unknownKeyPair(GenerateKeyPair()); |
784 | 0 | ASSERT_TRUE(unknownKeyPair.get()); |
785 | 0 |
|
786 | 0 | // Delegated responder cert signed by unknown issuer |
787 | 0 | const ByteString extensions[] = { |
788 | 0 | CreateEncodedEKUExtension(OCSPSigningEKUDER, Critical::No), |
789 | 0 | ByteString() |
790 | 0 | }; |
791 | 0 | ScopedTestKeyPair signerKeyPair(GenerateKeyPair()); |
792 | 0 | ByteString signerDER(CreateEncodedCertificate( |
793 | 0 | 1, sha256WithRSAEncryption(), subCAName, |
794 | 0 | oneDayBeforeNow, oneDayAfterNow, signerName, |
795 | 0 | *signerKeyPair, extensions, *unknownKeyPair)); |
796 | 0 | ASSERT_FALSE(ENCODING_FAILED(signerDER)); |
797 | 0 |
|
798 | 0 | // OCSP response signed by that delegated responder |
799 | 0 | ByteString certs[] = { signerDER, ByteString() }; |
800 | 0 | ByteString responseString( |
801 | 0 | CreateEncodedOCSPSuccessfulResponse( |
802 | 0 | OCSPResponseContext::good, *endEntityCertID, |
803 | 0 | signerName, *signerKeyPair, oneDayBeforeNow, |
804 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
805 | 0 | sha256WithRSAEncryption(), certs)); |
806 | 0 | Input response; |
807 | 0 | ASSERT_EQ(Success, |
808 | 0 | response.Init(responseString.data(), responseString.length())); |
809 | 0 | bool expired; |
810 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
811 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
812 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
813 | 0 | response, expired)); |
814 | 0 | ASSERT_FALSE(expired); |
815 | 0 | } |
816 | | |
817 | | // The CA that issued the OCSP responder cert is a sub-CA of the issuer of |
818 | | // the certificate that the OCSP response is for. That sub-CA cert is included |
819 | | // in the OCSP response before the OCSP responder cert. |
820 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, |
821 | | good_indirect_subca_1_first) |
822 | 0 | { |
823 | 0 | static const char* subCAName = "good_indirect_subca_1_first sub-CA"; |
824 | 0 | static const char* signerName = "good_indirect_subca_1_first OCSP signer"; |
825 | 0 | static const long zero = 0; |
826 | 0 |
|
827 | 0 | // sub-CA of root (root is the direct issuer of endEntity) |
828 | 0 | const ByteString subCAExtensions[] = { |
829 | 0 | CreateEncodedBasicConstraints(true, &zero, Critical::No), |
830 | 0 | ByteString() |
831 | 0 | }; |
832 | 0 | ScopedTestKeyPair subCAKeyPair(GenerateKeyPair()); |
833 | 0 | ByteString subCADER(CreateEncodedCertificate( |
834 | 0 | ++rootIssuedCount, sha256WithRSAEncryption(), rootName, |
835 | 0 | oneDayBeforeNow, oneDayAfterNow, subCAName, |
836 | 0 | *subCAKeyPair, subCAExtensions, *rootKeyPair)); |
837 | 0 | ASSERT_FALSE(ENCODING_FAILED(subCADER)); |
838 | 0 |
|
839 | 0 | // Delegated responder cert signed by that sub-CA |
840 | 0 | const ByteString extensions[] = { |
841 | 0 | CreateEncodedEKUExtension(OCSPSigningEKUDER, Critical::No), |
842 | 0 | ByteString(), |
843 | 0 | }; |
844 | 0 | ScopedTestKeyPair signerKeyPair(GenerateKeyPair()); |
845 | 0 | ByteString signerDER(CreateEncodedCertificate( |
846 | 0 | 1, sha256WithRSAEncryption(), subCAName, |
847 | 0 | oneDayBeforeNow, oneDayAfterNow, signerName, |
848 | 0 | *signerKeyPair, extensions, *subCAKeyPair)); |
849 | 0 | ASSERT_FALSE(ENCODING_FAILED(signerDER)); |
850 | 0 |
|
851 | 0 | // OCSP response signed by the delegated responder issued by the sub-CA |
852 | 0 | // that is trying to impersonate the root. |
853 | 0 | ByteString certs[] = { subCADER, signerDER, ByteString() }; |
854 | 0 | ByteString responseString( |
855 | 0 | CreateEncodedOCSPSuccessfulResponse( |
856 | 0 | OCSPResponseContext::good, *endEntityCertID, |
857 | 0 | signerName, *signerKeyPair, oneDayBeforeNow, |
858 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
859 | 0 | sha256WithRSAEncryption(), certs)); |
860 | 0 | Input response; |
861 | 0 | ASSERT_EQ(Success, |
862 | 0 | response.Init(responseString.data(), responseString.length())); |
863 | 0 | bool expired; |
864 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
865 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
866 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
867 | 0 | response, expired)); |
868 | 0 | ASSERT_FALSE(expired); |
869 | 0 | } |
870 | | |
871 | | // The CA that issued the OCSP responder cert is a sub-CA of the issuer of |
872 | | // the certificate that the OCSP response is for. That sub-CA cert is included |
873 | | // in the OCSP response after the OCSP responder cert. |
874 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, |
875 | | good_indirect_subca_1_second) |
876 | 0 | { |
877 | 0 | static const char* subCAName = "good_indirect_subca_1_second sub-CA"; |
878 | 0 | static const char* signerName = "good_indirect_subca_1_second OCSP signer"; |
879 | 0 | static const long zero = 0; |
880 | 0 |
|
881 | 0 | // sub-CA of root (root is the direct issuer of endEntity) |
882 | 0 | const ByteString subCAExtensions[] = { |
883 | 0 | CreateEncodedBasicConstraints(true, &zero, Critical::No), |
884 | 0 | ByteString() |
885 | 0 | }; |
886 | 0 | ScopedTestKeyPair subCAKeyPair(GenerateKeyPair()); |
887 | 0 | ByteString subCADER(CreateEncodedCertificate(++rootIssuedCount, |
888 | 0 | sha256WithRSAEncryption(), |
889 | 0 | rootName, |
890 | 0 | oneDayBeforeNow, oneDayAfterNow, |
891 | 0 | subCAName, *subCAKeyPair, |
892 | 0 | subCAExtensions, *rootKeyPair)); |
893 | 0 | ASSERT_FALSE(ENCODING_FAILED(subCADER)); |
894 | 0 |
|
895 | 0 | // Delegated responder cert signed by that sub-CA |
896 | 0 | const ByteString extensions[] = { |
897 | 0 | CreateEncodedEKUExtension(OCSPSigningEKUDER, Critical::No), |
898 | 0 | ByteString() |
899 | 0 | }; |
900 | 0 | ScopedTestKeyPair signerKeyPair(GenerateKeyPair()); |
901 | 0 | ByteString signerDER(CreateEncodedCertificate( |
902 | 0 | 1, sha256WithRSAEncryption(), subCAName, |
903 | 0 | oneDayBeforeNow, oneDayAfterNow, signerName, |
904 | 0 | *signerKeyPair, extensions, *subCAKeyPair)); |
905 | 0 | ASSERT_FALSE(ENCODING_FAILED(signerDER)); |
906 | 0 |
|
907 | 0 | // OCSP response signed by the delegated responder issued by the sub-CA |
908 | 0 | // that is trying to impersonate the root. |
909 | 0 | ByteString certs[] = { signerDER, subCADER, ByteString() }; |
910 | 0 | ByteString responseString( |
911 | 0 | CreateEncodedOCSPSuccessfulResponse( |
912 | 0 | OCSPResponseContext::good, *endEntityCertID, |
913 | 0 | signerName, *signerKeyPair, oneDayBeforeNow, |
914 | 0 | oneDayBeforeNow, &oneDayAfterNow, |
915 | 0 | sha256WithRSAEncryption(), certs)); |
916 | 0 | Input response; |
917 | 0 | ASSERT_EQ(Success, |
918 | 0 | response.Init(responseString.data(), responseString.length())); |
919 | 0 | bool expired; |
920 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
921 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
922 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
923 | 0 | response, expired)); |
924 | 0 | ASSERT_FALSE(expired); |
925 | 0 | } |
926 | | |
927 | | TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, |
928 | | good_unsupportedSignatureAlgorithmOnResponder) |
929 | 0 | { |
930 | 0 | // Note that the algorithm ID (md5WithRSAEncryption) identifies the signature |
931 | 0 | // algorithm that will be used to sign the certificate that issues the OCSP |
932 | 0 | // responses, not the responses themselves. |
933 | 0 | ByteString responseString( |
934 | 0 | CreateEncodedIndirectOCSPSuccessfulResponse( |
935 | 0 | "good_indirect_unsupportedSignatureAlgorithm", |
936 | 0 | OCSPResponseContext::good, byKey, |
937 | 0 | md5WithRSAEncryption())); |
938 | 0 | Input response; |
939 | 0 | ASSERT_EQ(Success, |
940 | 0 | response.Init(responseString.data(), responseString.length())); |
941 | 0 | bool expired; |
942 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
943 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
944 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
945 | 0 | response, expired)); |
946 | 0 | } |
947 | | |
948 | | class pkixocsp_VerifyEncodedResponse_GetCertTrust |
949 | | : public pkixocsp_VerifyEncodedResponse_DelegatedResponder { |
950 | | public: |
951 | | void SetUp() |
952 | 0 | { |
953 | 0 | pkixocsp_VerifyEncodedResponse_DelegatedResponder::SetUp(); |
954 | 0 |
|
955 | 0 | responseString = |
956 | 0 | CreateEncodedIndirectOCSPSuccessfulResponse( |
957 | 0 | "OCSPGetCertTrustTest Signer", OCSPResponseContext::good, |
958 | 0 | byKey, sha256WithRSAEncryption(), &OCSPSigningEKUDER, |
959 | 0 | &signerCertDER); |
960 | 0 | if (ENCODING_FAILED(responseString)) { |
961 | 0 | abort(); |
962 | 0 | } |
963 | 0 | if (response.Init(responseString.data(), responseString.length()) |
964 | 0 | != Success) { |
965 | 0 | abort(); |
966 | 0 | } |
967 | 0 | if (signerCertDER.length() == 0) { |
968 | 0 | abort(); |
969 | 0 | } |
970 | 0 | } |
971 | | |
972 | | class TrustDomain final : public OCSPTestTrustDomain |
973 | | { |
974 | | public: |
975 | | TrustDomain() |
976 | | : certTrustLevel(TrustLevel::InheritsTrust) |
977 | 0 | { |
978 | 0 | } |
979 | | |
980 | | bool SetCertTrust(const ByteString& aCertDER, TrustLevel aCertTrustLevel) |
981 | 0 | { |
982 | 0 | this->certDER = aCertDER; |
983 | 0 | this->certTrustLevel = aCertTrustLevel; |
984 | 0 | return true; |
985 | 0 | } |
986 | | private: |
987 | | Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&, |
988 | | Input candidateCert, /*out*/ TrustLevel& trustLevel) |
989 | | override |
990 | 0 | { |
991 | 0 | EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity); |
992 | 0 | EXPECT_FALSE(certDER.empty()); |
993 | 0 | Input certDERInput; |
994 | 0 | EXPECT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length())); |
995 | 0 | EXPECT_TRUE(InputsAreEqual(certDERInput, candidateCert)); |
996 | 0 | trustLevel = certTrustLevel; |
997 | 0 | return Success; |
998 | 0 | } |
999 | | |
1000 | | ByteString certDER; |
1001 | | TrustLevel certTrustLevel; |
1002 | | }; |
1003 | | |
1004 | | // trustDomain deliberately shadows the inherited field so that it isn't used |
1005 | | // by accident. See bug 1339921. |
1006 | | // Unfortunately GCC can't parse __has_warning("-Wshadow-field") even if it's |
1007 | | // the latter part of a conjunction that would evaluate to false, so we have to |
1008 | | // wrap it in a separate preprocessor conditional rather than using &&. |
1009 | | #if defined(__clang__) |
1010 | | #if __has_warning("-Wshadow-field") |
1011 | | #pragma clang diagnostic push |
1012 | | #pragma clang diagnostic ignored "-Wshadow-field" |
1013 | | #endif |
1014 | | #endif |
1015 | | TrustDomain trustDomain; |
1016 | | #if defined(__clang__) |
1017 | | #if __has_warning("-Wshadow-field") |
1018 | | #pragma clang diagnostic pop |
1019 | | #endif |
1020 | | #endif |
1021 | | ByteString signerCertDER; |
1022 | | ByteString responseString; |
1023 | | Input response; // references data in responseString |
1024 | | }; |
1025 | | |
1026 | | TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, InheritTrust) |
1027 | 0 | { |
1028 | 0 | ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER, |
1029 | 0 | TrustLevel::InheritsTrust)); |
1030 | 0 | bool expired; |
1031 | 0 | ASSERT_EQ(Success, |
1032 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
1033 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
1034 | 0 | response, expired)); |
1035 | 0 | ASSERT_FALSE(expired); |
1036 | 0 | } |
1037 | | |
1038 | | TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, TrustAnchor) |
1039 | 0 | { |
1040 | 0 | ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER, |
1041 | 0 | TrustLevel::TrustAnchor)); |
1042 | 0 | bool expired; |
1043 | 0 | ASSERT_EQ(Success, |
1044 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
1045 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
1046 | 0 | response, expired)); |
1047 | 0 | ASSERT_FALSE(expired); |
1048 | 0 | } |
1049 | | |
1050 | | TEST_F(pkixocsp_VerifyEncodedResponse_GetCertTrust, ActivelyDistrusted) |
1051 | 0 | { |
1052 | 0 | ASSERT_TRUE(trustDomain.SetCertTrust(signerCertDER, |
1053 | 0 | TrustLevel::ActivelyDistrusted)); |
1054 | 0 | Input responseInput; |
1055 | 0 | ASSERT_EQ(Success, |
1056 | 0 | responseInput.Init(responseString.data(), |
1057 | 0 | responseString.length())); |
1058 | 0 | bool expired; |
1059 | 0 | ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT, |
1060 | 0 | VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(), |
1061 | 0 | END_ENTITY_MAX_LIFETIME_IN_DAYS, |
1062 | 0 | responseInput, expired)); |
1063 | 0 | ASSERT_FALSE(expired); |
1064 | 0 | } |