| Index: net/ssl/ssl_private_key_test_util.cc
|
| diff --git a/net/ssl/ssl_private_key_test_util.cc b/net/ssl/ssl_private_key_test_util.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0e60cbcbf9e59d4a4b85c4f1161ae5265d44e335
|
| --- /dev/null
|
| +++ b/net/ssl/ssl_private_key_test_util.cc
|
| @@ -0,0 +1,221 @@
|
| +// 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/location.h"
|
| +#include "base/logging.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/strings/string_util.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/ec.h"
|
| +#include "third_party/boringssl/src/include/openssl/ec_key.h"
|
| +#include "third_party/boringssl/src/include/openssl/evp.h"
|
| +
|
| +using net::test::IsOk;
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +const char* HashToString(SSLPrivateKey::Hash hash) {
|
| + switch (hash) {
|
| + case SSLPrivateKey::Hash::MD5_SHA1:
|
| + return "MD5_SHA1";
|
| + case SSLPrivateKey::Hash::SHA1:
|
| + return "SHA1";
|
| + case SSLPrivateKey::Hash::SHA256:
|
| + return "SHA256";
|
| + case SSLPrivateKey::Hash::SHA384:
|
| + return "SHA384";
|
| + case SSLPrivateKey::Hash::SHA512:
|
| + return "SHA512";
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return "";
|
| +}
|
| +
|
| +const EVP_MD* HashToMD(SSLPrivateKey::Hash hash) {
|
| + switch (hash) {
|
| + case SSLPrivateKey::Hash::MD5_SHA1:
|
| + return EVP_md5_sha1();
|
| + case SSLPrivateKey::Hash::SHA1:
|
| + return EVP_sha1();
|
| + case SSLPrivateKey::Hash::SHA256:
|
| + return EVP_sha256();
|
| + case SSLPrivateKey::Hash::SHA384:
|
| + return EVP_sha384();
|
| + case SSLPrivateKey::Hash::SHA512:
|
| + return EVP_sha512();
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return nullptr;
|
| +}
|
| +
|
| +SSLPrivateKey::Type TypeForOpenSSLKey(EVP_PKEY* pkey) {
|
| + switch (EVP_PKEY_id(pkey)) {
|
| + case EVP_PKEY_RSA:
|
| + return SSLPrivateKey::Type::RSA;
|
| + case EVP_PKEY_EC: {
|
| + switch (EC_GROUP_get_curve_name(
|
| + EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)))) {
|
| + case NID_X9_62_prime256v1:
|
| + return SSLPrivateKey::Type::ECDSA_P256;
|
| + case NID_secp384r1:
|
| + return SSLPrivateKey::Type::ECDSA_P384;
|
| + case NID_secp521r1:
|
| + return SSLPrivateKey::Type::ECDSA_P521;
|
| + }
|
| + }
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return SSLPrivateKey::Type::RSA;
|
| +}
|
| +
|
| +// Resize a string to |size| bytes of data, then return its data buffer address
|
| +// cast as an 'uint8_t*', as expected by OpenSSL functions.
|
| +// |str| the target string.
|
| +// |size| the number of bytes to write into the string.
|
| +// Return the string's new buffer in memory, as an 'uint8_t*' pointer.
|
| +uint8_t* OpenSSLWriteInto(std::string* str, size_t size) {
|
| + return reinterpret_cast<uint8_t*>(base::WriteInto(str, size + 1));
|
| +}
|
| +
|
| +bool VerifyWithOpenSSL(const EVP_MD* md,
|
| + const base::StringPiece& digest,
|
| + EVP_PKEY* key,
|
| + const base::StringPiece& signature) {
|
| + bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(key, nullptr));
|
| + if (!ctx || !EVP_PKEY_verify_init(ctx.get()) ||
|
| + !EVP_PKEY_CTX_set_signature_md(ctx.get(), md) ||
|
| + !EVP_PKEY_verify(
|
| + ctx.get(), reinterpret_cast<const uint8_t*>(signature.data()),
|
| + signature.size(), reinterpret_cast<const uint8_t*>(digest.data()),
|
| + digest.size())) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool SignWithOpenSSL(const EVP_MD* md,
|
| + const base::StringPiece& digest,
|
| + EVP_PKEY* key,
|
| + std::string* result) {
|
| + size_t sig_len;
|
| + bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(key, nullptr));
|
| + if (!ctx || !EVP_PKEY_sign_init(ctx.get()) ||
|
| + !EVP_PKEY_CTX_set_signature_md(ctx.get(), md) ||
|
| + !EVP_PKEY_sign(ctx.get(), OpenSSLWriteInto(result, EVP_PKEY_size(key)),
|
| + &sig_len, reinterpret_cast<const uint8_t*>(digest.data()),
|
| + digest.size())) {
|
| + return false;
|
| + }
|
| +
|
| + result->resize(sig_len);
|
| + return true;
|
| +}
|
| +
|
| +void OnSignComplete(base::RunLoop* loop,
|
| + Error* out_error,
|
| + std::string* out_signature,
|
| + Error error,
|
| + const std::vector<uint8_t>& signature) {
|
| + *out_error = error;
|
| + out_signature->assign(signature.begin(), signature.end());
|
| + loop->Quit();
|
| +}
|
| +
|
| +Error DoKeySigningWithWrapper(SSLPrivateKey* key,
|
| + SSLPrivateKey::Hash hash,
|
| + const base::StringPiece& message,
|
| + std::string* result) {
|
| + Error error;
|
| + base::RunLoop loop;
|
| + key->SignDigest(
|
| + hash, message,
|
| + base::Bind(OnSignComplete, base::Unretained(&loop),
|
| + base::Unretained(&error), base::Unretained(result)));
|
| + loop.Run();
|
| + return error;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +const char* SSLPrivateKeyTypeToString(SSLPrivateKey::Type type) {
|
| + switch (type) {
|
| + case SSLPrivateKey::Type::RSA:
|
| + return "RSA";
|
| + case SSLPrivateKey::Type::ECDSA_P256:
|
| + return "ECDSA_P256";
|
| + case SSLPrivateKey::Type::ECDSA_P384:
|
| + return "ECDSA_P384";
|
| + case SSLPrivateKey::Type::ECDSA_P521:
|
| + return "ECDSA_P521";
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return "";
|
| +}
|
| +
|
| +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));
|
| +
|
| + // Check the length and type matches.
|
| + EXPECT_EQ(TypeForOpenSSLKey(openssl_key.get()), key->GetType());
|
| + EXPECT_EQ(static_cast<size_t>(EVP_PKEY_size(openssl_key.get())),
|
| + key->GetMaxSignatureLengthInBytes());
|
| +
|
| + // Test all supported hash algorithms.
|
| + std::vector<SSLPrivateKey::Hash> hashes = key->GetDigestPreferences();
|
| +
|
| + // To support TLS 1.1 and earlier, RSA keys must implicitly support MD5-SHA1,
|
| + // despite not being advertised.
|
| + if (key->GetType() == SSLPrivateKey::Type::RSA)
|
| + hashes.push_back(SSLPrivateKey::Hash::MD5_SHA1);
|
| +
|
| + for (SSLPrivateKey::Hash hash : hashes) {
|
| + SCOPED_TRACE(HashToString(hash));
|
| + const EVP_MD* md = HashToMD(hash);
|
| +
|
| + std::string digest(EVP_MD_size(md), 'a');
|
| +
|
| + // Test the key generates valid signatures.
|
| + std::string signature;
|
| + Error error = DoKeySigningWithWrapper(key, hash, digest, &signature);
|
| + EXPECT_THAT(error, IsOk());
|
| + EXPECT_TRUE(VerifyWithOpenSSL(md, digest, openssl_key.get(), signature));
|
| +
|
| + // RSA signing is deterministic, so further check the signature matches.
|
| + if (key->GetType() == SSLPrivateKey::Type::RSA) {
|
| + std::string openssl_signature;
|
| + ASSERT_TRUE(
|
| + SignWithOpenSSL(md, digest, openssl_key.get(), &openssl_signature));
|
| + EXPECT_EQ(openssl_signature, signature);
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace net
|
|
|