1 /*
2 * Copyright 2020, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <gtest/gtest.h>
18
19 #include "certificate_utils.h"
20
21 #include <openssl/err.h>
22 #include <openssl/evp.h>
23 #include <openssl/mem.h>
24
25 #include <iomanip>
26 #include <iostream>
27 #include <sstream>
28 #include <variant>
29
30 #include "test_keys.h"
31
32 using namespace keystore;
33
34 // I leave these here in case they are needed for debugging.
35 namespace debug_utils {
36
log_ssl_error()37 void log_ssl_error() {
38 unsigned long error = ERR_peek_last_error();
39
40 char buf[128];
41 ERR_error_string_n(error, buf, sizeof(buf));
42 std::cout << "BoringSslError: " << buf << std::endl;
43 }
44
hexdump(const std::vector<uint8_t> & data)45 std::string hexdump(const std::vector<uint8_t>& data) {
46 std::stringstream s;
47 size_t column_count = 0;
48 for (auto& c : data) {
49 s << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)c;
50 if (++column_count % 40 == 0) s << "\n";
51 }
52 return s.str();
53 }
54
55 } // namespace debug_utils
56
57 constexpr uint64_t kValidity = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
58
getMD(Digest digest)59 const EVP_MD* getMD(Digest digest) {
60 switch (digest) {
61 case Digest::SHA1:
62 return EVP_sha1();
63 case Digest::SHA224:
64 return EVP_sha224();
65 case Digest::SHA256:
66 return EVP_sha256();
67 case Digest::SHA384:
68 return EVP_sha384();
69 case Digest::SHA512:
70 return EVP_sha512();
71 }
72 }
73
74 std::array<Digest, 5> digests = {
75 Digest::SHA1, Digest::SHA224, Digest::SHA256, Digest::SHA384, Digest::SHA512,
76 };
77
toString(Digest d)78 static const char* toString(Digest d) {
79 switch (d) {
80 case Digest::SHA1:
81 return "SHA1";
82 case Digest::SHA224:
83 return "SHA224";
84 case Digest::SHA256:
85 return "SHA256";
86 case Digest::SHA384:
87 return "SHA384";
88 case Digest::SHA512:
89 return "SHA512";
90 }
91 }
92
93 std::array<Padding, 2> rsa_paddings = {
94 Padding::PSS,
95 Padding::PKCS1_5,
96 };
97
98 enum class EcCurve {
99 P224,
100 P256,
101 P384,
102 P521,
103 };
104
105 std::array<int, 4> ec_curves = {
106 NID_secp224r1,
107 NID_X9_62_prime256v1,
108 NID_secp384r1,
109 NID_secp521r1,
110 };
111
curveNidToString(int nid)112 static const char* curveNidToString(int nid) {
113 switch (nid) {
114 case NID_secp224r1:
115 return "P224";
116 case NID_X9_62_prime256v1:
117 return "P256";
118 case NID_secp384r1:
119 return "P384";
120 case NID_secp521r1:
121 return "P521";
122 default:
123 return "Unknown";
124 }
125 }
126
127 std::array<long, 2> rsa_key_sizes = {
128 2048,
129 4096,
130 };
131
132 using EcParam = std::tuple<int /* EC curve NID */, Digest>;
133
134 class CertificateUtilsWithEcCurve : public testing::TestWithParam<EcParam> {};
135
paramToStringEc(testing::TestParamInfo<EcParam> param)136 static std::string paramToStringEc(testing::TestParamInfo<EcParam> param) {
137 std::stringstream s;
138 auto [curve_nid, digest] = param.param;
139 s << param.index << "_" << curveNidToString(curve_nid) << "_" << toString(digest);
140 return s.str();
141 }
142
143 INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackEC, CertificateUtilsWithEcCurve,
144 testing::Combine(testing::ValuesIn(ec_curves), testing::ValuesIn(digests)),
145 paramToStringEc);
146
TEST_P(CertificateUtilsWithEcCurve,CertSigningWithCallbackEC)147 TEST_P(CertificateUtilsWithEcCurve, CertSigningWithCallbackEC) {
148 // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
149 // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
150 int curve_nid;
151 Digest digest;
152 std::tie(curve_nid, digest) = GetParam();
153 EVP_PKEY_CTX_Ptr pkey_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL));
154 ASSERT_TRUE((bool)pkey_ctx);
155 ASSERT_TRUE(EVP_PKEY_keygen_init(pkey_ctx.get()));
156 ASSERT_TRUE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx.get(), curve_nid));
157
158 EVP_PKEY* pkey_ptr = nullptr;
159 ASSERT_TRUE(EVP_PKEY_keygen(pkey_ctx.get(), &pkey_ptr));
160 EVP_PKEY_Ptr pkey(pkey_ptr);
161 ASSERT_TRUE(pkey);
162
163 uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
164
165 BasicConstraintsExtension bcons{
166 .isCa = true,
167 .pathLength = {},
168 };
169
170 KeyUsageExtension keyUsage{
171 .isSigningKey = true,
172 .isEncryptionKey = false,
173 .isCertificationKey = true,
174 };
175
176 auto certV = makeCert(pkey.get(), std::nullopt, std::nullopt, now_ms - kValidity,
177 now_ms + kValidity, true /* subject key id extension */, keyUsage, bcons);
178 ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
179 auto& cert = std::get<X509_Ptr>(certV);
180 ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
181
182 ASSERT_TRUE(!signCertWith(
183 cert.get(),
184 [&](const uint8_t* data, size_t len) {
185 bssl::ScopedEVP_MD_CTX sign_ctx;
186 EXPECT_TRUE(
187 EVP_DigestSignInit(sign_ctx.get(), nullptr, getMD(digest), nullptr, pkey.get()));
188
189 std::vector<uint8_t> sig_buf(512);
190 size_t sig_len = 512;
191 EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
192 sig_buf.resize(sig_len);
193 return sig_buf;
194 },
195 Algo::ECDSA, Padding::Ignored, digest));
196
197 auto encCertV = encodeCert(cert.get());
198 ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
199
200 auto& encCert = std::get<1>(encCertV);
201 // Uncomment the next line to dump the DER encoded signed certificate as hex string.
202 // You can pipe this dump into "xxd -r -p | openssl x509 -inform der -text -noout"
203 // to inspect the certificate.
204 // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
205
206 const uint8_t* p = encCert.data();
207 X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
208 EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
209 ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
210 }
211
212 using RsaParams = std::tuple<long /* key size */, Padding, Digest>;
213
214 class CertificateUtilsWithRsa : public testing::TestWithParam<RsaParams> {};
215
paramsToStringRsa(testing::TestParamInfo<RsaParams> param)216 static std::string paramsToStringRsa(testing::TestParamInfo<RsaParams> param) {
217 std::stringstream s;
218 auto [key_size, padding, digest] = param.param;
219 s << param.index << "_" << key_size << "_";
220 switch (padding) {
221 case Padding::PSS:
222 s << "PSS";
223 break;
224 case Padding::PKCS1_5:
225 s << "PKCS1_5";
226 break;
227 case Padding::Ignored:
228 s << "Ignored";
229 }
230 s << "_" << toString(digest);
231 return s.str();
232 }
233
234 INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackRsa, CertificateUtilsWithRsa,
235 testing::Combine(testing::ValuesIn(rsa_key_sizes),
236 testing::ValuesIn(rsa_paddings),
237 testing::ValuesIn(digests)),
238 paramsToStringRsa);
239
TEST_P(CertificateUtilsWithRsa,CertSigningWithCallbackRsa)240 TEST_P(CertificateUtilsWithRsa, CertSigningWithCallbackRsa) {
241 // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
242 // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
243 long key_size;
244 Padding padding;
245 Digest digest;
246 std::tie(key_size, padding, digest) = GetParam();
247
248 CBS cbs;
249 switch (key_size) {
250 case 2048:
251 CBS_init(&cbs, rsa_key_2k, rsa_key_2k_len);
252 break;
253 case 4096:
254 CBS_init(&cbs, rsa_key_4k, rsa_key_4k_len);
255 break;
256 default:
257 FAIL();
258 }
259 EVP_PKEY_Ptr pkey(EVP_parse_private_key(&cbs));
260 ASSERT_TRUE(pkey);
261
262 uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
263
264 BasicConstraintsExtension bcons{
265 .isCa = true,
266 .pathLength = 0,
267 };
268
269 KeyUsageExtension keyUsage{
270 .isSigningKey = true,
271 .isEncryptionKey = false,
272 .isCertificationKey = true,
273 };
274
275 auto certV = makeCert(pkey.get(), std::nullopt, std::nullopt, now_ms - kValidity,
276 now_ms + kValidity, true /* subject key id extension */, keyUsage, bcons);
277 ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
278 auto& cert = std::get<X509_Ptr>(certV);
279 ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
280
281 ASSERT_TRUE(!signCertWith(
282 cert.get(),
283 [&](const uint8_t* data, size_t len) {
284 bssl::ScopedEVP_MD_CTX sign_ctx;
285 EVP_PKEY_CTX* pkey_sign_ctx_ptr;
286 EXPECT_TRUE(EVP_DigestSignInit(sign_ctx.get(), &pkey_sign_ctx_ptr, getMD(digest),
287 nullptr, pkey.get()));
288
289 if (padding == Padding::PSS) {
290 EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PSS_PADDING));
291 EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_sign_ctx_ptr, -1));
292 } else {
293 EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PADDING));
294 }
295
296 std::vector<uint8_t> sig_buf(1024);
297 size_t sig_len = 1024;
298 EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
299 sig_buf.resize(sig_len);
300 return sig_buf;
301 },
302 Algo::RSA, padding, digest));
303
304 auto encCertV = encodeCert(cert.get());
305 ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
306
307 auto& encCert = std::get<1>(encCertV);
308 // Uncomment the next line to dump the DER encoded signed certificate as hex string.
309 // You can pipe this dump into "xxd -r -p | openssl x509 -inform der -text -noout"
310 // to inspect the certificate.
311 // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
312
313 const uint8_t* p = encCert.data();
314 X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
315 EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
316 ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
317 }
318
TEST(TimeStringTests,toTimeStringTest)319 TEST(TimeStringTests, toTimeStringTest) {
320 // Two test vectors that need to result in UTCTime
321 ASSERT_EQ(std::string(toTimeString(1622758591000)->data()), std::string("210603221631Z"));
322 ASSERT_EQ(std::string(toTimeString(0)->data()), std::string("700101000000Z"));
323 // Two test vectors that need to result in GeneralizedTime.
324 ASSERT_EQ(std::string(toTimeString(16227585910000)->data()), std::string("24840325064510Z"));
325 ASSERT_EQ(std::string(toTimeString(-1622758591000)->data()), std::string("19180731014329Z"));
326
327 // Highest possible UTCTime
328 ASSERT_EQ(std::string(toTimeString(2524607999999)->data()), "491231235959Z");
329 // And one millisecond later must be GeneralizedTime.
330 ASSERT_EQ(std::string(toTimeString(2524608000000)->data()), "20500101000000Z");
331
332 // Earliest possible UTCTime
333 ASSERT_EQ(std::string(toTimeString(-631152000000)->data()), "500101000000Z");
334 // And one millisecond earlier must be GeneralizedTime.
335 // This also checks that the rounding direction does not flip when the input is negative.
336 ASSERT_EQ(std::string(toTimeString(-631152000001)->data()), "19491231235959Z");
337 }