blob: 7ec84469e17cbef0429b2ec2bfae2d08ec5560c8 [file] [log] [blame]
// Copyright 2020 The Chromium OS 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 "hwsec-test-utils/common/openssl_utility.h"
#include <memory>
#include <base/logging.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
namespace hwsec_test_utils {
namespace {
constexpr int kAesBlockSize = 16;
} // namespace
void InitializeOpenSSL() {
static bool g_openssl_initialized = false;
if (g_openssl_initialized) {
return;
}
g_openssl_initialized = true;
OpenSSL_add_all_algorithms();
// Some certificates to RSA keys, e.g., endorsement certificates for TPM1.2,
// could have the algorithm type "rsaesOaep", which is not recognized by
// OpenSSL directly.
EVP_PKEY_asn1_add_alias(EVP_PKEY_RSA, NID_rsaesOaep);
ERR_load_crypto_strings();
}
std::string GetOpenSSLError() {
crypto::ScopedBIO bio(BIO_new(BIO_s_mem()));
ERR_print_errors(bio.get());
char* data = nullptr;
int data_len = BIO_get_mem_data(bio.get(), &data);
std::string error_string(data, data_len);
return error_string;
}
crypto::ScopedEVP_PKEY CreateNewEcKey() {
crypto::ScopedEC_KEY ec_key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
if (!ec_key) {
LOG(ERROR) << __func__ << ": Failed to call EC_KEY_new_by_curve_name: "
<< GetOpenSSLError();
return nullptr;
}
EC_KEY_set_asn1_flag(ec_key.get(), OPENSSL_EC_NAMED_CURVE);
if (EC_KEY_generate_key(ec_key.get()) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EC_KEY_generate_key: " << GetOpenSSLError();
return nullptr;
}
// Not really sure if we need this call; supposedly it does no harm except for
// neglectable performance overhead.
if (EC_KEY_check_key(ec_key.get()) != 1) {
LOG(WARNING) << __func__
<< ": Retry due to Bad ECC key (EC_KEY_check_key failed): "
<< GetOpenSSLError();
// Probabilistically impossible to result in infinite loop; just invoke
// recursive call.
return CreateNewEcKey();
}
crypto::ScopedEVP_PKEY key(EVP_PKEY_new());
if (!key) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_new: " << GetOpenSSLError();
return nullptr;
}
if (EVP_PKEY_set1_EC_KEY(key.get(), ec_key.get()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_PKEY_set1_EC_KEY: "
<< GetOpenSSLError();
return nullptr;
}
return key;
}
crypto::ScopedEVP_PKEY PemToEVP(const std::string& pem) {
crypto::ScopedBIO bio(
BIO_new_mem_buf(const_cast<char*>(pem.data()), pem.size()));
if (!bio) {
LOG(ERROR) << __func__
<< ": Failed to create mem BIO: " << GetOpenSSLError();
return nullptr;
}
crypto::ScopedEVP_PKEY key(
PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
if (!key) {
LOG(ERROR) << __func__
<< ": Failed to read key with PEM_read_bio_PrivateKey: "
<< GetOpenSSLError();
return nullptr;
}
return key;
}
crypto::ScopedX509 PemToX509(const std::string& pem) {
crypto::ScopedBIO bio(
BIO_new_mem_buf(const_cast<char*>(pem.data()), pem.size()));
if (!bio) {
LOG(ERROR) << __func__
<< ": Failed to create mem BIO: " << GetOpenSSLError();
return nullptr;
}
crypto::ScopedX509 x509(
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
if (!x509) {
LOG(ERROR) << __func__
<< ": Failed to call PEM_read_bio_X509: " << GetOpenSSLError();
return nullptr;
}
return x509;
}
base::Optional<std::string> GetRandom(size_t length) {
std::unique_ptr<unsigned char[]> buffer =
std::make_unique<unsigned char[]>(length);
if (RAND_bytes(buffer.get(), length) != 1) {
return {};
}
return std::string(buffer.get(), buffer.get() + length);
}
base::Optional<std::string> EVPDigestSign(const crypto::ScopedEVP_PKEY& key,
const EVP_MD* md_type,
const std::string& data) {
CHECK(key.get());
CHECK(md_type != nullptr);
crypto::ScopedEVP_MD_CTX mdctx(EVP_MD_CTX_new());
if (!mdctx) {
LOG(ERROR) << __func__
<< ": Failed to allocate EVP_MD_CTX: " << GetOpenSSLError();
return {};
}
if (EVP_DigestSignInit(mdctx.get(), nullptr, md_type, nullptr, key.get()) !=
1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_DigestSignInit: " << GetOpenSSLError();
return {};
}
if (EVP_DigestSignUpdate(mdctx.get(), data.data(), data.length()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_DigestSignUpdate: "
<< GetOpenSSLError();
return {};
}
size_t output_length = 0;
if (EVP_DigestSignFinal(mdctx.get(), nullptr, &output_length) != 1) {
LOG(ERROR)
<< __func__
<< ": Failed to call EVP_DigestSignFinal to get signature length: "
<< GetOpenSSLError();
return {};
}
std::unique_ptr<unsigned char[]> output =
std::make_unique<unsigned char[]>(output_length);
if (EVP_DigestSignFinal(mdctx.get(), output.get(), &output_length) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_DigestVerifyFinal: "
<< GetOpenSSLError();
return {};
}
return std::string(output.get(), output.get() + output_length);
}
bool EVPDigestVerify(const crypto::ScopedEVP_PKEY& key,
const EVP_MD* md_type,
const std::string& data,
const std::string& signature) {
CHECK(key.get());
CHECK(md_type != nullptr);
crypto::ScopedEVP_MD_CTX mdctx(EVP_MD_CTX_new());
if (!mdctx) {
LOG(ERROR) << __func__
<< ": Failed to allocate EVP_MD_CTX: " << GetOpenSSLError();
return false;
}
if (EVP_DigestVerifyInit(mdctx.get(), nullptr, md_type, nullptr, key.get()) !=
1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_DigestVerifyInit: "
<< GetOpenSSLError();
return false;
}
if (EVP_DigestVerifyUpdate(mdctx.get(), data.data(), data.length()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_DigestVerifyUpdate: "
<< GetOpenSSLError();
return false;
}
if (EVP_DigestVerifyFinal(
mdctx.get(), reinterpret_cast<const unsigned char*>(signature.data()),
signature.length()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_DigestVerifyFinal: "
<< GetOpenSSLError();
return false;
}
return true;
}
base::Optional<std::string> EVPRsaEncrypt(const crypto::ScopedEVP_PKEY& key,
const std::string& data,
int rsa_padding) {
crypto::ScopedEVP_PKEY_CTX ctx(EVP_PKEY_CTX_new(key.get(), nullptr));
if (!ctx) {
LOG(ERROR) << __func__
<< ": Failed to allocate EVP_PKEY_CTX: " << GetOpenSSLError();
return {};
}
if (EVP_PKEY_encrypt_init(ctx.get()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_PKEY_decrypt_init: "
<< GetOpenSSLError();
return {};
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), rsa_padding) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_PKEY_CTX_set_rsa_padding: "
<< GetOpenSSLError();
return {};
}
size_t output_length = 0;
if (EVP_PKEY_encrypt(ctx.get(), nullptr, &output_length,
reinterpret_cast<const unsigned char*>(data.data()),
data.length()) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_encrypt to get output length: "
<< GetOpenSSLError();
return {};
}
std::unique_ptr<unsigned char[]> output =
std::make_unique<unsigned char[]>(output_length);
if (EVP_PKEY_encrypt(ctx.get(), output.get(), &output_length,
reinterpret_cast<const unsigned char*>(data.data()),
data.length()) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_encrypt: " << GetOpenSSLError();
return {};
}
return std::string(output.get(), output.get() + output_length);
}
base::Optional<std::string> EVPRsaDecrypt(const crypto::ScopedEVP_PKEY& key,
const std::string& encrypted_data,
int rsa_padding) {
crypto::ScopedEVP_PKEY_CTX ctx(EVP_PKEY_CTX_new(key.get(), nullptr));
if (!ctx) {
LOG(ERROR) << __func__
<< ": Failed to allocate EVP_PKEY_CTX: " << GetOpenSSLError();
return {};
}
if (EVP_PKEY_decrypt_init(ctx.get()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_PKEY_decrypt_init: "
<< GetOpenSSLError();
return {};
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), rsa_padding) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_PKEY_CTX_set_rsa_padding: "
<< GetOpenSSLError();
return {};
}
size_t output_length = 0;
if (EVP_PKEY_decrypt(
ctx.get(), nullptr, &output_length,
reinterpret_cast<const unsigned char*>(encrypted_data.data()),
encrypted_data.length()) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_decrypt to get output length: "
<< GetOpenSSLError();
return {};
}
std::unique_ptr<unsigned char[]> output =
std::make_unique<unsigned char[]>(output_length);
if (EVP_PKEY_decrypt(
ctx.get(), output.get(), &output_length,
reinterpret_cast<const unsigned char*>(encrypted_data.data()),
encrypted_data.length()) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_decrypt: " << GetOpenSSLError();
return {};
}
return std::string(output.get(), output.get() + output_length);
}
base::Optional<std::string> EVPAesEncrypt(const std::string& data,
const EVP_CIPHER* evp_cipher,
const std::string& aes_key,
const std::string& iv) {
crypto::ScopedEVP_CIPHER_CTX ctx(EVP_CIPHER_CTX_new());
if (!ctx) {
LOG(ERROR) << __func__
<< ": Failed to allocate EVP_CIPHER_CTX: " << GetOpenSSLError();
return {};
}
if (EVP_EncryptInit_ex(ctx.get(), evp_cipher, /*engine=*/nullptr,
reinterpret_cast<const unsigned char*>(aes_key.data()),
reinterpret_cast<const unsigned char*>(iv.data())) !=
1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_DecryptInit_ex: " << GetOpenSSLError();
return {};
}
// Allocate the generous buffer and resize at the end.
std::unique_ptr<unsigned char[]> output =
std::make_unique<unsigned char[]>(data.size() + kAesBlockSize);
int output_length = 0;
if (EVP_EncryptUpdate(ctx.get(), output.get(), &output_length,
reinterpret_cast<const unsigned char*>(data.data()),
data.length()) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_EncryptUpdate: " << GetOpenSSLError();
return {};
}
int extra_output_length = 0;
if (EVP_EncryptFinal_ex(ctx.get(), output.get() + output_length,
&extra_output_length) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_EncryptFinal_ex: " << GetOpenSSLError();
return {};
}
output_length += extra_output_length;
return std::string(output.get(), output.get() + output_length);
}
base::Optional<std::string> EVPAesDecrypt(const std::string& encrypted_data,
const EVP_CIPHER* evp_cipher,
const std::string& aes_key,
const std::string& iv) {
crypto::ScopedEVP_CIPHER_CTX ctx(EVP_CIPHER_CTX_new());
if (!ctx) {
LOG(ERROR) << __func__
<< ": Failed to allocate EVP_CIPHER_CTX: " << GetOpenSSLError();
return {};
}
if (EVP_DecryptInit_ex(ctx.get(), evp_cipher, nullptr,
reinterpret_cast<const unsigned char*>(aes_key.data()),
reinterpret_cast<const unsigned char*>(iv.data())) !=
1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_DecryptInit_ex: " << GetOpenSSLError();
return {};
}
// The decrypted data is shorter than the encrypted data; allocate the
// generous buffer and resize at the end.
std::unique_ptr<unsigned char[]> output =
std::make_unique<unsigned char[]>(encrypted_data.size());
int output_length = 0;
if (EVP_DecryptUpdate(
ctx.get(), output.get(), &output_length,
reinterpret_cast<const unsigned char*>(encrypted_data.data()),
encrypted_data.length()) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_DecryptUpdate: " << GetOpenSSLError();
return {};
}
int extra_output_length = 0;
if (EVP_DecryptFinal_ex(ctx.get(), output.get() + output_length,
&extra_output_length) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_DecryptFinal_ex: " << GetOpenSSLError();
return {};
}
output_length += extra_output_length;
return std::string(output.get(), output.get() + output_length);
}
base::Optional<std::string> EVPDerive(const crypto::ScopedEVP_PKEY& key,
const crypto::ScopedEVP_PKEY& peer_key) {
crypto::ScopedEVP_PKEY_CTX ctx(EVP_PKEY_CTX_new(key.get(), nullptr));
if (!ctx) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_CTX_new: " << GetOpenSSLError();
return {};
}
if (EVP_PKEY_derive_init(ctx.get()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_PKEY_derive_init: "
<< GetOpenSSLError();
return {};
}
if (EVP_PKEY_derive_set_peer(ctx.get(), peer_key.get()) != 1) {
LOG(ERROR) << __func__ << ": Failed to call EVP_PKEY_derive_set_peer: "
<< GetOpenSSLError();
return {};
}
size_t output_length = 0;
if (EVP_PKEY_derive(ctx.get(), nullptr, &output_length) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_derive to get output size: "
<< GetOpenSSLError();
return {};
}
std::unique_ptr<unsigned char[]> output =
std::make_unique<unsigned char[]>(output_length);
if (EVP_PKEY_derive(ctx.get(), output.get(), &output_length) != 1) {
LOG(ERROR) << __func__
<< ": Failed to call EVP_PKEY_derive: " << GetOpenSSLError();
return {};
}
return std::string(output.get(), output.get() + output_length);
}
} // namespace hwsec_test_utils