blob: 98ae8ddc225c718caa71c996a40b950acecdbd11 [file] [log] [blame]
Bob Beckbc97b7a2023-04-18 08:35:15 -06001// Copyright 2016 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Bob Beckbc97b7a2023-04-18 08:35:15 -06005#include "ocsp.h"
6
Bob Beckbc97b7a2023-04-18 08:35:15 -06007#include <gtest/gtest.h>
8#include <openssl/base64.h>
9#include <openssl/pool.h>
Bob Beck5c7a2a02023-11-20 17:28:21 -070010#include "encode_values.h"
11#include "string_util.h"
12#include "test_helpers.h"
Bob Beckbc97b7a2023-04-18 08:35:15 -060013
14namespace bssl {
15
16namespace {
17
18constexpr int64_t kOCSPAgeOneWeek = 7 * 24 * 60 * 60;
19
Bob Beck5c7a2a02023-11-20 17:28:21 -070020std::string GetFilePath(const std::string &file_name) {
Bob Beckbc97b7a2023-04-18 08:35:15 -060021 return std::string("testdata/ocsp_unittest/") + file_name;
22}
23
24std::shared_ptr<const ParsedCertificate> ParseCertificate(
25 std::string_view data) {
26 CertErrors errors;
27 return ParsedCertificate::Create(
Bob Beck5c7a2a02023-11-20 17:28:21 -070028 bssl::UniquePtr<CRYPTO_BUFFER>(
29 CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t *>(data.data()),
30 data.size(), nullptr)),
Bob Beckbc97b7a2023-04-18 08:35:15 -060031 {}, &errors);
32}
33
34struct TestParams {
Bob Beck5c7a2a02023-11-20 17:28:21 -070035 const char *file_name;
Bob Beckbc97b7a2023-04-18 08:35:15 -060036 OCSPRevocationStatus expected_revocation_status;
37 OCSPVerifyResult::ResponseStatus expected_response_status;
38};
39
40class CheckOCSPTest : public ::testing::TestWithParam<TestParams> {};
41
42const TestParams kTestParams[] = {
43 {"good_response.pem", OCSPRevocationStatus::GOOD,
44 OCSPVerifyResult::PROVIDED},
45
46 {"good_response_sha256.pem", OCSPRevocationStatus::GOOD,
47 OCSPVerifyResult::PROVIDED},
48
49 {"no_response.pem", OCSPRevocationStatus::UNKNOWN,
50 OCSPVerifyResult::NO_MATCHING_RESPONSE},
51
52 {"malformed_request.pem", OCSPRevocationStatus::UNKNOWN,
53 OCSPVerifyResult::ERROR_RESPONSE},
54
55 {"bad_status.pem", OCSPRevocationStatus::UNKNOWN,
56 OCSPVerifyResult::PARSE_RESPONSE_ERROR},
57
58 {"bad_ocsp_type.pem", OCSPRevocationStatus::UNKNOWN,
59 OCSPVerifyResult::PARSE_RESPONSE_ERROR},
60
61 {"bad_signature.pem", OCSPRevocationStatus::UNKNOWN,
62 OCSPVerifyResult::PROVIDED},
63
64 {"ocsp_sign_direct.pem", OCSPRevocationStatus::GOOD,
65 OCSPVerifyResult::PROVIDED},
66
67 {"ocsp_sign_indirect.pem", OCSPRevocationStatus::GOOD,
68 OCSPVerifyResult::PROVIDED},
69
70 {"ocsp_sign_indirect_missing.pem", OCSPRevocationStatus::UNKNOWN,
71 OCSPVerifyResult::PROVIDED},
72
73 {"ocsp_sign_bad_indirect.pem", OCSPRevocationStatus::UNKNOWN,
74 OCSPVerifyResult::PROVIDED},
75
76 {"ocsp_extra_certs.pem", OCSPRevocationStatus::GOOD,
77 OCSPVerifyResult::PROVIDED},
78
79 {"has_version.pem", OCSPRevocationStatus::GOOD, OCSPVerifyResult::PROVIDED},
80
81 {"responder_name.pem", OCSPRevocationStatus::GOOD,
82 OCSPVerifyResult::PROVIDED},
83
84 {"responder_id.pem", OCSPRevocationStatus::GOOD,
85 OCSPVerifyResult::PROVIDED},
86
87 {"has_extension.pem", OCSPRevocationStatus::GOOD,
88 OCSPVerifyResult::PROVIDED},
89
90 {"good_response_next_update.pem", OCSPRevocationStatus::GOOD,
91 OCSPVerifyResult::PROVIDED},
92
93 {"revoke_response.pem", OCSPRevocationStatus::REVOKED,
94 OCSPVerifyResult::PROVIDED},
95
96 {"revoke_response_reason.pem", OCSPRevocationStatus::REVOKED,
97 OCSPVerifyResult::PROVIDED},
98
99 {"unknown_response.pem", OCSPRevocationStatus::UNKNOWN,
100 OCSPVerifyResult::PROVIDED},
101
102 {"multiple_response.pem", OCSPRevocationStatus::UNKNOWN,
103 OCSPVerifyResult::PROVIDED},
104
105 {"other_response.pem", OCSPRevocationStatus::UNKNOWN,
106 OCSPVerifyResult::NO_MATCHING_RESPONSE},
107
108 {"has_single_extension.pem", OCSPRevocationStatus::GOOD,
109 OCSPVerifyResult::PROVIDED},
110
111 {"has_critical_single_extension.pem", OCSPRevocationStatus::UNKNOWN,
112 OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION},
113
114 {"has_critical_response_extension.pem", OCSPRevocationStatus::UNKNOWN,
115 OCSPVerifyResult::UNHANDLED_CRITICAL_EXTENSION},
116
117 {"has_critical_ct_extension.pem", OCSPRevocationStatus::GOOD,
118 OCSPVerifyResult::PROVIDED},
119
120 {"missing_response.pem", OCSPRevocationStatus::UNKNOWN,
121 OCSPVerifyResult::NO_MATCHING_RESPONSE},
122};
123
124// Parameterised test name generator for tests depending on RenderTextBackend.
125struct PrintTestName {
Bob Beck5c7a2a02023-11-20 17:28:21 -0700126 std::string operator()(const testing::TestParamInfo<TestParams> &info) const {
Bob Beckbc97b7a2023-04-18 08:35:15 -0600127 std::string_view name(info.param.file_name);
128 // Strip ".pem" from the end as GTest names cannot contain period.
129 name.remove_suffix(4);
130 return std::string(name);
131 }
132};
133
Bob Beck5c7a2a02023-11-20 17:28:21 -0700134INSTANTIATE_TEST_SUITE_P(All, CheckOCSPTest, ::testing::ValuesIn(kTestParams),
Bob Beckbc97b7a2023-04-18 08:35:15 -0600135 PrintTestName());
136
137TEST_P(CheckOCSPTest, FromFile) {
Bob Beck5c7a2a02023-11-20 17:28:21 -0700138 const TestParams &params = GetParam();
Bob Beckbc97b7a2023-04-18 08:35:15 -0600139
140 std::string ocsp_data;
141 std::string ca_data;
142 std::string cert_data;
143 std::string request_data;
144 const PemBlockMapping mappings[] = {
145 {"OCSP RESPONSE", &ocsp_data},
146 {"CA CERTIFICATE", &ca_data},
147 {"CERTIFICATE", &cert_data},
148 {"OCSP REQUEST", &request_data},
149 };
150
151 ASSERT_TRUE(ReadTestDataFromPemFile(GetFilePath(params.file_name), mappings));
152
153 // Mar 5 00:00:00 2017 GMT
154 int64_t kVerifyTime = 1488672000;
155
156 // Test that CheckOCSP() works.
157 OCSPVerifyResult::ResponseStatus response_status;
158 OCSPRevocationStatus revocation_status =
159 CheckOCSP(ocsp_data, cert_data, ca_data, kVerifyTime, kOCSPAgeOneWeek,
160 &response_status);
161
162 EXPECT_EQ(params.expected_revocation_status, revocation_status);
163 EXPECT_EQ(params.expected_response_status, response_status);
164
165 // Check that CreateOCSPRequest() works.
166 std::shared_ptr<const ParsedCertificate> cert = ParseCertificate(cert_data);
167 ASSERT_TRUE(cert);
168
169 std::shared_ptr<const ParsedCertificate> issuer = ParseCertificate(ca_data);
170 ASSERT_TRUE(issuer);
171
172 std::vector<uint8_t> encoded_request;
173 ASSERT_TRUE(CreateOCSPRequest(cert.get(), issuer.get(), &encoded_request));
174
Bob Beck2e119172023-08-14 11:06:38 -0600175 EXPECT_EQ(der::Input(encoded_request), der::Input(request_data));
Bob Beckbc97b7a2023-04-18 08:35:15 -0600176}
177
178std::string_view kGetURLTestParams[] = {
179 "http://www.example.com/",
180 "http://www.example.com/path/",
181 "http://www.example.com/path",
182 "http://www.example.com/path?query"
183 "http://user:pass@www.example.com/path?query",
184};
185
186class CreateOCSPGetURLTest : public ::testing::TestWithParam<std::string_view> {
187};
188
Bob Beck5c7a2a02023-11-20 17:28:21 -0700189INSTANTIATE_TEST_SUITE_P(All, CreateOCSPGetURLTest,
Bob Beckbc97b7a2023-04-18 08:35:15 -0600190 ::testing::ValuesIn(kGetURLTestParams));
191
192TEST_P(CreateOCSPGetURLTest, Basic) {
193 std::string ca_data;
194 std::string cert_data;
195 std::string request_data;
196 const PemBlockMapping mappings[] = {
197 {"CA CERTIFICATE", &ca_data},
198 {"CERTIFICATE", &cert_data},
199 {"OCSP REQUEST", &request_data},
200 };
201
202 // Load one of the test files. (Doesn't really matter which one as
203 // constructing the DER is tested elsewhere).
204 ASSERT_TRUE(
205 ReadTestDataFromPemFile(GetFilePath("good_response.pem"), mappings));
206
207 std::shared_ptr<const ParsedCertificate> cert = ParseCertificate(cert_data);
208 ASSERT_TRUE(cert);
209
210 std::shared_ptr<const ParsedCertificate> issuer = ParseCertificate(ca_data);
211 ASSERT_TRUE(issuer);
212
Bob Beck300f2212023-08-16 22:18:25 +0000213 std::optional<std::string> url =
214 CreateOCSPGetURL(cert.get(), issuer.get(), GetParam());
215 ASSERT_TRUE(url);
Bob Beckbc97b7a2023-04-18 08:35:15 -0600216
217 // Try to extract the encoded data and compare against |request_data|.
218 //
219 // A known answer output test would be better as this just reverses the logic
220 // from the implementation file.
Bob Beck300f2212023-08-16 22:18:25 +0000221 std::string b64 = url->substr(GetParam().size() + 1);
Bob Beckbc97b7a2023-04-18 08:35:15 -0600222
223 // Hex un-escape the data.
224 b64 = bssl::string_util::FindAndReplace(b64, "%2B", "+");
225 b64 = bssl::string_util::FindAndReplace(b64, "%2F", "/");
226 b64 = bssl::string_util::FindAndReplace(b64, "%3D", "=");
227
228 // Base64 decode the data.
229 size_t len;
230 EXPECT_TRUE(EVP_DecodedLength(&len, b64.size()));
231 std::vector<uint8_t> decoded(len);
232 EXPECT_TRUE(EVP_DecodeBase64(decoded.data(), &len, len,
Bob Beck5c7a2a02023-11-20 17:28:21 -0700233 reinterpret_cast<const uint8_t *>(b64.data()),
Bob Beckbc97b7a2023-04-18 08:35:15 -0600234 b64.size()));
235 std::string decoded_string(decoded.begin(), decoded.begin() + len);
236
237 EXPECT_EQ(request_data, decoded_string);
238}
239
240} // namespace
241
Bob Beck5c7a2a02023-11-20 17:28:21 -0700242} // namespace bssl