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