xref: /aosp_15_r20/external/cronet/third_party/boringssl/src/pki/ocsp_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 <gtest/gtest.h>
8 #include <openssl/base64.h>
9 #include <openssl/pool.h>
10 #include "encode_values.h"
11 #include "string_util.h"
12 #include "test_helpers.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>(
29           CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t *>(data.data()),
30                             data.size(), nullptr)),
31       {}, &errors);
32 }
33 
34 struct TestParams {
35   const char *file_name;
36   OCSPRevocationStatus expected_revocation_status;
37   OCSPVerifyResult::ResponseStatus expected_response_status;
38 };
39 
40 class CheckOCSPTest : public ::testing::TestWithParam<TestParams> {};
41 
42 const 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.
125 struct PrintTestName {
operator ()bssl::__anon016592ce0111::PrintTestName126   std::string operator()(const testing::TestParamInfo<TestParams> &info) const {
127     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 
134 INSTANTIATE_TEST_SUITE_P(All, CheckOCSPTest, ::testing::ValuesIn(kTestParams),
135                          PrintTestName());
136 
TEST_P(CheckOCSPTest,FromFile)137 TEST_P(CheckOCSPTest, FromFile) {
138   const TestParams &params = GetParam();
139 
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 
175   EXPECT_EQ(der::Input(encoded_request), der::Input(request_data));
176 }
177 
178 std::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:[email protected]/path?query",
184 };
185 
186 class CreateOCSPGetURLTest : public ::testing::TestWithParam<std::string_view> {
187 };
188 
189 INSTANTIATE_TEST_SUITE_P(All, CreateOCSPGetURLTest,
190                          ::testing::ValuesIn(kGetURLTestParams));
191 
TEST_P(CreateOCSPGetURLTest,Basic)192 TEST_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 
213   std::optional<std::string> url =
214       CreateOCSPGetURL(cert.get(), issuer.get(), GetParam());
215   ASSERT_TRUE(url);
216 
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.
221   std::string b64 = url->substr(GetParam().size() + 1);
222 
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,
233                                reinterpret_cast<const uint8_t *>(b64.data()),
234                                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 
242 }  // namespace bssl
243