| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/ssl/ssl_private_key_test_util.h" |
| |
| #include <stdint.h> |
| |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/containers/span.h" |
| #include "base/location.h" |
| #include "base/run_loop.h" |
| #include "crypto/openssl_util.h" |
| #include "net/base/net_errors.h" |
| #include "net/ssl/ssl_private_key.h" |
| #include "net/test/gtest_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/boringssl/src/include/openssl/bytestring.h" |
| #include "third_party/boringssl/src/include/openssl/digest.h" |
| #include "third_party/boringssl/src/include/openssl/evp.h" |
| #include "third_party/boringssl/src/include/openssl/rsa.h" |
| #include "third_party/boringssl/src/include/openssl/ssl.h" |
| |
| using net::test::IsOk; |
| |
| namespace net { |
| |
| namespace { |
| |
| bool VerifyWithOpenSSL(uint16_t algorithm, |
| base::span<const uint8_t> input, |
| EVP_PKEY* key, |
| base::span<const uint8_t> signature) { |
| bssl::ScopedEVP_MD_CTX ctx; |
| EVP_PKEY_CTX* pctx; |
| if (!EVP_DigestVerifyInit(ctx.get(), &pctx, |
| SSL_get_signature_algorithm_digest(algorithm), |
| nullptr, key)) { |
| return false; |
| } |
| if (SSL_is_signature_algorithm_rsa_pss(algorithm)) { |
| if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || |
| !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* hash length */)) { |
| return false; |
| } |
| } |
| return EVP_DigestVerify(ctx.get(), signature.data(), signature.size(), |
| input.data(), input.size()); |
| } |
| |
| void OnSignComplete(base::RunLoop* loop, |
| Error* out_error, |
| std::vector<uint8_t>* out_signature, |
| Error error, |
| const std::vector<uint8_t>& signature) { |
| *out_error = error; |
| *out_signature = signature; |
| loop->Quit(); |
| } |
| |
| Error DoKeySigningWithWrapper(SSLPrivateKey* key, |
| uint16_t algorithm, |
| base::span<const uint8_t> input, |
| std::vector<uint8_t>* result) { |
| Error error; |
| base::RunLoop loop; |
| key->Sign(algorithm, input, |
| base::BindOnce(OnSignComplete, base::Unretained(&loop), |
| base::Unretained(&error), base::Unretained(result))); |
| loop.Run(); |
| return error; |
| } |
| |
| } // namespace |
| |
| void TestSSLPrivateKeyMatches(SSLPrivateKey* key, const std::string& pkcs8) { |
| crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| |
| // Create the equivalent OpenSSL key. |
| CBS cbs; |
| CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pkcs8.data()), pkcs8.size()); |
| bssl::UniquePtr<EVP_PKEY> openssl_key(EVP_parse_private_key(&cbs)); |
| ASSERT_TRUE(openssl_key); |
| EXPECT_EQ(0u, CBS_len(&cbs)); |
| |
| // Test all supported algorithms. |
| std::vector<uint16_t> preferences = key->GetAlgorithmPreferences(); |
| |
| // To support TLS 1.1 and earlier, RSA keys must implicitly support MD5-SHA1, |
| // despite not being advertised. |
| preferences.push_back(SSL_SIGN_RSA_PKCS1_MD5_SHA1); |
| |
| for (uint16_t algorithm : preferences) { |
| SCOPED_TRACE( |
| SSL_get_signature_algorithm_name(algorithm, 0 /* exclude curve */)); |
| // BoringSSL will skip signatures algorithms that don't match the key type. |
| if (EVP_PKEY_id(openssl_key.get()) != |
| SSL_get_signature_algorithm_key_type(algorithm)) { |
| continue; |
| } |
| // If the RSA key is too small for the hash, skip the algorithm. BoringSSL |
| // will filter this algorithm out and decline using it. In particular, |
| // 1024-bit RSA keys cannot sign RSA-PSS with SHA-512 and test keys are |
| // often 1024 bits. |
| if (SSL_is_signature_algorithm_rsa_pss(algorithm) && |
| static_cast<size_t>(EVP_PKEY_size(openssl_key.get())) < |
| 2 * EVP_MD_size(SSL_get_signature_algorithm_digest(algorithm)) + |
| 2) { |
| continue; |
| } |
| |
| // Test the key generates valid signatures. |
| std::vector<uint8_t> input(100, 'a'); |
| std::vector<uint8_t> signature; |
| Error error = DoKeySigningWithWrapper(key, algorithm, input, &signature); |
| EXPECT_THAT(error, IsOk()); |
| EXPECT_TRUE( |
| VerifyWithOpenSSL(algorithm, input, openssl_key.get(), signature)); |
| } |
| } |
| |
| } // namespace net |