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 "certificate_utils.h"
18 #include <gtest/gtest.h>
19
20 #include <openssl/err.h>
21 #include <openssl/evp.h>
22 #include <openssl/mem.h>
23
24 #include <iomanip>
25 #include <iostream>
26 #include <sstream>
27 #include <variant>
28
29 #include "test_keys.h"
30
31 using namespace keystore;
32
33 // I leave these here in case they are needed for debugging.
34 namespace debug_utils {
35
log_ssl_error()36 void log_ssl_error() {
37 unsigned long error = ERR_peek_last_error();
38
39 char buf[128];
40 ERR_error_string_n(error, buf, sizeof(buf));
41 std::cout << "BoringSslError: " << buf << std::endl;
42 }
43
hexdump(const std::vector<uint8_t> & data)44 std::string hexdump(const std::vector<uint8_t>& data) {
45 std::stringstream s;
46 size_t column_count = 0;
47 for (auto& c : data) {
48 s << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)c;
49 if (++column_count % 40 == 0) s << "\n";
50 }
51 return s.str();
52 }
53
54 } // namespace debug_utils
55
56 constexpr uint64_t kValidity = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
57
getMD(Digest digest)58 const EVP_MD* getMD(Digest digest) {
59 switch (digest) {
60 case Digest::SHA1:
61 return EVP_sha1();
62 case Digest::SHA224:
63 return EVP_sha224();
64 case Digest::SHA256:
65 return EVP_sha256();
66 case Digest::SHA384:
67 return EVP_sha384();
68 case Digest::SHA512:
69 return EVP_sha512();
70 }
71 }
72
73 std::array<Digest, 5> digests = {
74 Digest::SHA1, Digest::SHA224, Digest::SHA256, Digest::SHA384, Digest::SHA512,
75 };
76
toString(Digest d)77 static const char* toString(Digest d) {
78 switch (d) {
79 case Digest::SHA1:
80 return "SHA1";
81 case Digest::SHA224:
82 return "SHA224";
83 case Digest::SHA256:
84 return "SHA256";
85 case Digest::SHA384:
86 return "SHA384";
87 case Digest::SHA512:
88 return "SHA512";
89 }
90 }
91
92 std::array<Padding, 2> rsa_paddings = {
93 Padding::PSS,
94 Padding::PKCS1_5,
95 };
96
97 enum class EcCurve {
98 P224,
99 P256,
100 P384,
101 P521,
102 };
103
104 std::array<int, 4> ec_curves = {
105 NID_secp224r1,
106 NID_X9_62_prime256v1,
107 NID_secp384r1,
108 NID_secp521r1,
109 };
110
curveNidToString(int nid)111 static const char* curveNidToString(int nid) {
112 switch (nid) {
113 case NID_secp224r1:
114 return "P224";
115 case NID_X9_62_prime256v1:
116 return "P256";
117 case NID_secp384r1:
118 return "P384";
119 case NID_secp521r1:
120 return "P521";
121 default:
122 return "Unknown";
123 }
124 }
125
126 std::array<long, 2> rsa_key_sizes = {
127 2048,
128 4096,
129 };
130
131 using EcParam = std::tuple<int /* EC curve NID */, Digest>;
132
133 class CertificateUtilsWithEcCurve : public testing::TestWithParam<EcParam> {};
134
paramToStringEc(testing::TestParamInfo<EcParam> param)135 static std::string paramToStringEc(testing::TestParamInfo<EcParam> param) {
136 std::stringstream s;
137 auto [curve_nid, digest] = param.param;
138 s << param.index << "_" << curveNidToString(curve_nid) << "_" << toString(digest);
139 return s.str();
140 }
141
142 INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackEC, CertificateUtilsWithEcCurve,
143 testing::Combine(testing::ValuesIn(ec_curves), testing::ValuesIn(digests)),
144 paramToStringEc);
145
TEST_P(CertificateUtilsWithEcCurve,CertSigningWithCallbackEC)146 TEST_P(CertificateUtilsWithEcCurve, CertSigningWithCallbackEC) {
147 // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
148 // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
149 int curve_nid;
150 Digest digest;
151 std::tie(curve_nid, digest) = GetParam();
152 EVP_PKEY_CTX_Ptr pkey_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL));
153 ASSERT_TRUE((bool)pkey_ctx);
154 ASSERT_TRUE(EVP_PKEY_keygen_init(pkey_ctx.get()));
155 ASSERT_TRUE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx.get(), curve_nid));
156
157 EVP_PKEY* pkey_ptr = nullptr;
158 ASSERT_TRUE(EVP_PKEY_keygen(pkey_ctx.get(), &pkey_ptr));
159 EVP_PKEY_Ptr pkey(pkey_ptr);
160 ASSERT_TRUE(pkey);
161
162 uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
163
164 BasicConstraintsExtension bcons{
165 .isCa = true,
166 .pathLength = {},
167 };
168
169 KeyUsageExtension keyUsage{
170 .isSigningKey = true,
171 .isEncryptionKey = false,
172 .isCertificationKey = true,
173 };
174
175 auto certV = makeCert(pkey.get(), std::nullopt, std::nullopt, now_ms - kValidity,
176 now_ms + kValidity, true /* subject key id extension */, keyUsage, bcons);
177 ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
178 auto& cert = std::get<X509_Ptr>(certV);
179 ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
180
181 ASSERT_TRUE(!signCertWith(
182 cert.get(),
183 [&](const uint8_t* data, size_t len) {
184 bssl::ScopedEVP_MD_CTX sign_ctx;
185 EXPECT_TRUE(
186 EVP_DigestSignInit(sign_ctx.get(), nullptr, getMD(digest), nullptr, pkey.get()));
187
188 std::vector<uint8_t> sig_buf(512);
189 size_t sig_len = 512;
190 EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
191 sig_buf.resize(sig_len);
192 return sig_buf;
193 },
194 Algo::ECDSA, Padding::Ignored, digest));
195
196 auto encCertV = encodeCert(cert.get());
197 ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
198
199 auto& encCert = std::get<1>(encCertV);
200 // Uncomment the next line to dump the DER encoded signed certificate as hex string.
201 // You can pipe this dump into "xxd -r -p | openssl x509 -inform der -text -noout"
202 // to inspect the certificate.
203 // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
204
205 const uint8_t* p = encCert.data();
206 X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
207 EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
208 ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
209 }
210
211 using RsaParams = std::tuple<long /* key size */, Padding, Digest>;
212
213 class CertificateUtilsWithRsa : public testing::TestWithParam<RsaParams> {};
214
paramsToStringRsa(testing::TestParamInfo<RsaParams> param)215 static std::string paramsToStringRsa(testing::TestParamInfo<RsaParams> param) {
216 std::stringstream s;
217 auto [key_size, padding, digest] = param.param;
218 s << param.index << "_" << key_size << "_";
219 switch (padding) {
220 case Padding::PSS:
221 s << "PSS";
222 break;
223 case Padding::PKCS1_5:
224 s << "PKCS1_5";
225 break;
226 case Padding::Ignored:
227 s << "Ignored";
228 }
229 s << "_" << toString(digest);
230 return s.str();
231 }
232
EncodeX509Algor(const X509_ALGOR * alg)233 static std::optional<std::vector<uint8_t>> EncodeX509Algor(const X509_ALGOR* alg) {
234 uint8_t* der = nullptr;
235 int der_len = i2d_X509_ALGOR(alg, &der);
236 if (der_len < 0) {
237 return std::nullopt;
238 }
239 std::vector<uint8_t> ret(der, der + der_len);
240 OPENSSL_free(der);
241 return ret;
242 }
243
244 // `x509_verify` not working with RSA-PSS & SHA1/SHA224 digests. so, manually
245 // verify the certificate with RSA-PSS & SHA1/SHA224 digests.
246 // BoringSSL after https://boringssl-review.googlesource.com/c/boringssl/+/53865
247 // does not support RSA-PSS with SHA1/SHA224 digests.
verifyCertFieldsExplicitly(X509 * cert,Digest digest)248 static void verifyCertFieldsExplicitly(X509* cert, Digest digest) {
249 // RSA-PSS-SHA1 AlgorithmIdentifier DER encoded value
250 const std::vector<uint8_t> expected_rsa_pss_sha1 = {
251 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x00,
252 };
253 // RSA-PSS-SHA224 AlgorithmIdentifier DER encoded value
254 const std::vector<uint8_t> expected_rsa_pss_sha224 = {
255 0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30,
256 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
257 0x02, 0x04, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
258 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
259 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x1c,
260 };
261 const X509_ALGOR* alg;
262 const ASN1_BIT_STRING* sig;
263 const EVP_MD* evp_digest;
264 X509_get0_signature(&sig, &alg, cert);
265 auto encoded = EncodeX509Algor(alg);
266 ASSERT_TRUE(encoded);
267
268 // Check the AlgorithmIdentifiers.
269 if (digest == Digest::SHA1) {
270 evp_digest = EVP_sha1();
271 EXPECT_EQ(encoded.value(), expected_rsa_pss_sha1);
272 } else if (digest == Digest::SHA224) {
273 evp_digest = EVP_sha224();
274 EXPECT_EQ(encoded.value(), expected_rsa_pss_sha224);
275 } else {
276 GTEST_FAIL()
277 << "Error: This is expected to be used only for RSA-PSS with SHA1/SHA224 as digests";
278 }
279
280 // Check the signature.
281 EVP_PKEY_Ptr pubkey(X509_get_pubkey(cert));
282 ASSERT_TRUE(pubkey);
283
284 uint8_t* tbs = nullptr;
285 int tbs_len = i2d_X509_tbs(cert, &tbs);
286 ASSERT_GT(tbs_len, 0);
287
288 size_t sig_len;
289 ASSERT_TRUE(ASN1_BIT_STRING_num_bytes(sig, &sig_len));
290 EVP_PKEY_CTX* pctx;
291 bssl::ScopedEVP_MD_CTX ctx;
292 ASSERT_TRUE(EVP_DigestVerifyInit(ctx.get(), &pctx, evp_digest, nullptr, pubkey.get()));
293 ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING));
294 // The salt length should match the digest length.
295 ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1));
296 EXPECT_TRUE(EVP_DigestVerify(ctx.get(), ASN1_STRING_get0_data(sig), sig_len, tbs, tbs_len));
297 }
298
299 INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackRsa, CertificateUtilsWithRsa,
300 testing::Combine(testing::ValuesIn(rsa_key_sizes),
301 testing::ValuesIn(rsa_paddings),
302 testing::ValuesIn(digests)),
303 paramsToStringRsa);
304
TEST_P(CertificateUtilsWithRsa,CertSigningWithCallbackRsa)305 TEST_P(CertificateUtilsWithRsa, CertSigningWithCallbackRsa) {
306 // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
307 // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
308 long key_size;
309 Padding padding;
310 Digest digest;
311 std::tie(key_size, padding, digest) = GetParam();
312
313 CBS cbs;
314 switch (key_size) {
315 case 2048:
316 CBS_init(&cbs, rsa_key_2k, rsa_key_2k_len);
317 break;
318 case 4096:
319 CBS_init(&cbs, rsa_key_4k, rsa_key_4k_len);
320 break;
321 default:
322 FAIL();
323 }
324 EVP_PKEY_Ptr pkey(EVP_parse_private_key(&cbs));
325 ASSERT_TRUE(pkey);
326
327 uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
328
329 BasicConstraintsExtension bcons{
330 .isCa = true,
331 .pathLength = 0,
332 };
333
334 KeyUsageExtension keyUsage{
335 .isSigningKey = true,
336 .isEncryptionKey = false,
337 .isCertificationKey = true,
338 };
339
340 auto certV = makeCert(pkey.get(), std::nullopt, std::nullopt, now_ms - kValidity,
341 now_ms + kValidity, true /* subject key id extension */, keyUsage, bcons);
342 ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
343 auto& cert = std::get<X509_Ptr>(certV);
344 ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
345
346 ASSERT_TRUE(!signCertWith(
347 cert.get(),
348 [&](const uint8_t* data, size_t len) {
349 bssl::ScopedEVP_MD_CTX sign_ctx;
350 EVP_PKEY_CTX* pkey_sign_ctx_ptr;
351 EXPECT_TRUE(EVP_DigestSignInit(sign_ctx.get(), &pkey_sign_ctx_ptr, getMD(digest),
352 nullptr, pkey.get()));
353
354 if (padding == Padding::PSS) {
355 EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PSS_PADDING));
356 EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_sign_ctx_ptr, -1));
357 } else {
358 EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PADDING));
359 }
360
361 std::vector<uint8_t> sig_buf(1024);
362 size_t sig_len = 1024;
363 EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
364 sig_buf.resize(sig_len);
365 return sig_buf;
366 },
367 Algo::RSA, padding, digest));
368
369 auto encCertV = encodeCert(cert.get());
370 ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
371
372 auto& encCert = std::get<1>(encCertV);
373 // Uncomment the next line to dump the DER encoded signed certificate as hex string.
374 // You can pipe this dump into "xxd -r -p | openssl x509 -inform der -text -noout"
375 // to inspect the certificate.
376 // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
377
378 const uint8_t* p = encCert.data();
379 X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
380 EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
381 if ((padding == Padding::PSS) && (digest == Digest::SHA1 || digest == Digest::SHA224)) {
382 // BoringSSL after https://boringssl-review.googlesource.com/c/boringssl/+/53865
383 // does not support these PSS combinations, so verify these certificates manually.
384 EXPECT_NE(decoded_cert.get(), nullptr);
385 EXPECT_NE(decoded_pkey.get(), nullptr);
386 verifyCertFieldsExplicitly(decoded_cert.get(), digest);
387 } else {
388 ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
389 }
390 }
391
TEST(TimeStringTests,toTimeStringTest)392 TEST(TimeStringTests, toTimeStringTest) {
393 // Two test vectors that need to result in UTCTime
394 ASSERT_EQ(std::string(toTimeString(1622758591000)->data()), std::string("210603221631Z"));
395 ASSERT_EQ(std::string(toTimeString(0)->data()), std::string("700101000000Z"));
396 // Two test vectors that need to result in GeneralizedTime.
397 ASSERT_EQ(std::string(toTimeString(16227585910000)->data()), std::string("24840325064510Z"));
398 ASSERT_EQ(std::string(toTimeString(-1622758591000)->data()), std::string("19180731014329Z"));
399
400 // Highest possible UTCTime
401 ASSERT_EQ(std::string(toTimeString(2524607999999)->data()), "491231235959Z");
402 // And one millisecond later must be GeneralizedTime.
403 ASSERT_EQ(std::string(toTimeString(2524608000000)->data()), "20500101000000Z");
404
405 // Earliest possible UTCTime
406 ASSERT_EQ(std::string(toTimeString(-631152000000)->data()), "500101000000Z");
407 // And one millisecond earlier must be GeneralizedTime.
408 // This also checks that the rounding direction does not flip when the input is negative.
409 ASSERT_EQ(std::string(toTimeString(-631152000001)->data()), "19491231235959Z");
410 }
411