Updated OEMCrypto tests to use DRM key objects.

[ Merge of http://go/wvgerrit/147275 ]

Swapped out use of OpenSSL/BoringSSL RSA and EC_KEY to use OEMCrypto
reference utility classes RsaPublicKey/EccPublicKey.  This enables
further test development with ECC keys, and removes duplicate OpenSSL/
BoringSSL code.

For Android makefiles, only the minimally required files have been
added.

Bug: 205902021
Bug: 236317198
Test: run_prov30_test run_prov40_test oemcrypto_test
Change-Id: I64491018e8ffb69bf986083e3aae446eb9e5cf39
This commit is contained in:
Alex Dale
2022-06-16 19:56:56 -07:00
parent 2a371dce54
commit bfa2d782bd
7 changed files with 252 additions and 314 deletions

View File

@@ -310,12 +310,12 @@ void ProvisioningRoundTrip::PrepareSession(
OEMCrypto_BootCertificateChain) {
// TODO(chelu): change this to CSR provisioning.
session_->LoadOEMCert(true);
session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_);
session_->GenerateRsaSessionKey(&message_key_, &encrypted_message_key_);
encryptor_.set_enc_key(message_key_);
} else {
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_OEMCertificate);
session_->LoadOEMCert(true);
session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_);
session_->GenerateRsaSessionKey(&message_key_, &encrypted_message_key_);
encryptor_.set_enc_key(message_key_);
}
}
@@ -324,7 +324,7 @@ void ProvisioningRoundTrip::VerifyRequestSignature(
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t /* core_message_length */) {
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
session()->VerifyRSASignature(data, generated_signature.data(),
session()->VerifyRsaSignature(data, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS);
} else {
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox);
@@ -565,11 +565,11 @@ void LicenseRoundTrip::VerifyRequestSignature(
if (global_features.api_version < 17) {
const std::vector<uint8_t> subdata(data.begin() + core_message_length,
data.end());
session()->VerifyRSASignature(subdata, generated_signature.data(),
session()->VerifyRsaSignature(subdata, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS);
SHA256(data.data(), core_message_length, request_hash_);
} else {
session()->VerifyRSASignature(data, generated_signature.data(),
session()->VerifyRsaSignature(data, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS);
SHA256(data.data(), core_message_length, request_hash_);
}
@@ -1403,17 +1403,10 @@ OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) {
}
}
Session::Session()
: open_(false),
forced_session_id_(false),
session_id_(0),
nonce_(0),
public_rsa_(nullptr) {}
Session::Session() {}
Session::~Session() {
if (!forced_session_id_ && open_) close();
if (public_rsa_) RSA_free(public_rsa_);
if (public_ec_) EC_KEY_free(public_ec_);
}
void Session::open() {
@@ -1493,10 +1486,11 @@ void Session::GenerateDerivedKeysFromSessionKey() {
// Uses test certificate.
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
ASSERT_TRUE(public_rsa_ || public_ec_)
<< "No public RSA/ECC key loaded in test code";
// A failure here probably indicates that there is something wrong with the
// test program and its dependency on BoringSSL.
ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key));
ASSERT_TRUE(GenerateRsaSessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
FillDefaultContext(&mac_context, &enc_context);
@@ -1621,12 +1615,11 @@ void Session::LoadOEMCert(bool verify_cert) {
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pubkey(X509_get_pubkey(x509_cert));
ASSERT_TRUE(pubkey.NotNull());
if (i == 0) {
public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get());
if (!public_rsa_) {
cerr << "d2i_RSAPrivateKey failed.\n";
dump_boringssl_error();
ASSERT_TRUE(nullptr != public_rsa_);
}
public_rsa_ =
util::RsaPublicKey::FromSslHandle(EVP_PKEY_get0_RSA(pubkey.get()));
ASSERT_TRUE(public_rsa_)
<< "Failed to extract public RSA key from OEM certificate";
return;
}
if (verify_cert) {
vector<char> buffer(80);
@@ -1658,199 +1651,152 @@ void Session::LoadOEMCert(bool verify_cert) {
}
}
void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) {
if (rsa_key == nullptr) {
rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048;
rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048);
}
uint8_t* p = const_cast<uint8_t*>(rsa_key);
boringssl_ptr<BIO, BIO_vfree> bio(
BIO_new_mem_buf(p, static_cast<int>(rsa_key_length)));
ASSERT_TRUE(bio.NotNull());
boringssl_ptr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8_pki(
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
ASSERT_TRUE(pkcs8_pki.NotNull());
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> evp(EVP_PKCS82PKEY(pkcs8_pki.get()));
ASSERT_TRUE(evp.NotNull());
if (public_rsa_) RSA_free(public_rsa_);
public_rsa_ = EVP_PKEY_get1_RSA(evp.get());
if (!public_rsa_) {
cerr << "d2i_RSAPrivateKey failed. ";
dump_boringssl_error();
FAIL() << "Could not parse public RSA key.";
}
switch (RSA_check_key(public_rsa_)) {
case 1: // valid.
void Session::SetTestRsaPublicKey() {
public_ec_.reset();
public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo(
kTestRSAPKCS8PrivateKeyInfo2_2048,
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048));
ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key #2";
}
void Session::SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type,
const uint8_t* buffer,
size_t length) {
switch (key_type) {
case OEMCrypto_RSA_Private_Key:
ASSERT_NO_FATAL_FAILURE(
SetRsaPublicKeyFromPrivateKeyInfo(buffer, length));
return;
case 0: // not valid.
dump_boringssl_error();
FAIL() << "[rsa key not valid] ";
default: // -1 == check failed.
dump_boringssl_error();
FAIL() << "[error checking rsa key] ";
}
}
void Session::SetRsaPublicKey(const uint8_t* buffer, size_t length) {
if (public_rsa_) {
RSA_free(public_rsa_);
public_rsa_ = nullptr;
}
if (public_ec_) {
EC_KEY_free(public_ec_);
public_ec_ = nullptr;
}
public_rsa_ = d2i_RSA_PUBKEY(nullptr, &buffer, length);
if (!public_rsa_) {
cout << "d2i_RSAPrivateKey failed. ";
dump_boringssl_error();
FAIL() << "Could not parse public RSA key.";
}
switch (RSA_check_key(public_rsa_)) {
case 1: // valid.
case OEMCrypto_ECC_Private_Key:
ASSERT_NO_FATAL_FAILURE(
SetEccPublicKeyFromPrivateKeyInfo(buffer, length));
return;
case 0: // not valid.
dump_boringssl_error();
FAIL() << "[rsa key not valid] ";
default: // -1 == check failed.
dump_boringssl_error();
FAIL() << "[error checking rsa key] ";
}
FAIL() << "Unknown key type: " << static_cast<int>(key_type);
}
void Session::SetEcPublicKey(const uint8_t* buffer, size_t length) {
if (public_rsa_) {
RSA_free(public_rsa_);
public_rsa_ = nullptr;
}
if (public_ec_) {
EC_KEY_free(public_ec_);
public_ec_ = nullptr;
}
public_ec_ = d2i_EC_PUBKEY(nullptr, &buffer, length);
if (!public_ec_) {
cout << "d2i_RSAPrivateKey failed. ";
dump_boringssl_error();
FAIL() << "Could not parse public RSA key.";
}
switch (EC_KEY_check_key(public_ec_)) {
case 1: // valid.
void Session::SetRsaPublicKeyFromPrivateKeyInfo(const uint8_t* buffer,
size_t length) {
public_ec_.reset();
public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo(buffer, length);
ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key";
}
void Session::SetEccPublicKeyFromPrivateKeyInfo(const uint8_t* buffer,
size_t length) {
public_rsa_.reset();
public_ec_ = util::EccPublicKey::LoadPrivateKeyInfo(buffer, length);
ASSERT_TRUE(public_ec_) << "Could not parse ECC public key";
}
void Session::SetPublicKeyFromSubjectPublicKey(
OEMCrypto_PrivateKeyType key_type, const uint8_t* buffer, size_t length) {
switch (key_type) {
case OEMCrypto_RSA_Private_Key:
ASSERT_NO_FATAL_FAILURE(
SetRsaPublicKeyFromSubjectPublicKey(buffer, length));
return;
case OEMCrypto_ECC_Private_Key:
ASSERT_NO_FATAL_FAILURE(
SetEccPublicKeyFromSubjectPublicKey(buffer, length));
return;
case 0: // not valid.
default:
dump_boringssl_error();
FAIL() << "[ec key not valid] ";
}
FAIL() << "Unknown key type: " << static_cast<int>(key_type);
}
bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length) {
boringssl_ptr<EVP_MD_CTX, EVP_MD_CTX_free> md_ctx(EVP_MD_CTX_new());
EVP_PKEY_CTX* pkey_ctx = nullptr;
if (EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(),
nullptr /* no ENGINE */, pkey) != 1) {
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_signature_md(pkey_ctx,
const_cast<EVP_MD*>(EVP_sha1())) != 1) {
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) {
LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length) != 1) {
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyFinal(md_ctx.get(), const_cast<uint8_t*>(signature),
signature_length) != 1) {
LOGE(
"EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad "
"signature.)");
goto err;
}
return true;
err:
dump_boringssl_error();
return false;
void Session::SetRsaPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
size_t length) {
public_ec_.reset();
public_rsa_ = util::RsaPublicKey::Load(buffer, length);
ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key";
}
void Session::VerifyRSASignature(const vector<uint8_t>& message,
void Session::SetEccPublicKeyFromSubjectPublicKey(const uint8_t* buffer,
size_t length) {
public_rsa_.reset();
public_ec_ = util::EccPublicKey::Load(buffer, length);
ASSERT_TRUE(public_ec_) << "Could not parse ECC public key";
}
void Session::VerifyRsaSignature(const vector<uint8_t>& message,
const uint8_t* signature,
size_t signature_length,
RSA_Padding_Scheme padding_scheme) {
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
ASSERT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
<< "Signature size is wrong. " << signature_length << ", should be "
<< RSA_size(public_rsa_);
if (padding_scheme == kSign_RSASSA_PSS) {
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_));
const bool ok =
VerifyPSSSignature(pkey.get(), message.data(), message.size(),
signature, signature_length);
EXPECT_TRUE(ok) << "PSS signature check failed.";
} else if (padding_scheme == kSign_PKCS1_Block1) {
vector<uint8_t> padded_digest(signature_length);
int size;
// RSA_public_decrypt decrypts the signature, and then verifies that
// it was padded with RSA PKCS1 padding.
size = RSA_public_decrypt(static_cast<int>(signature_length), signature,
padded_digest.data(), public_rsa_,
RSA_PKCS1_PADDING);
EXPECT_GT(size, 0);
padded_digest.resize(size);
EXPECT_EQ(message, padded_digest);
} else {
EXPECT_TRUE(false) << "Padding scheme not supported.";
ASSERT_TRUE(public_rsa_) << "No public RSA key loaded in test code";
if (padding_scheme != kSign_RSASSA_PSS &&
padding_scheme != kSign_PKCS1_Block1) {
FAIL() << "Padding scheme not supported: " << padding_scheme;
return;
}
const util::RsaSignatureAlgorithm algorithm =
padding_scheme == kSign_RSASSA_PSS ? util::kRsaPssDefault
: util::kRsaPkcs1Cast;
const OEMCryptoResult result = public_rsa_->VerifySignature(
message.data(), message.size(), signature, signature_length, algorithm);
ASSERT_EQ(result, OEMCrypto_SUCCESS) << "RSA signature check failed";
}
bool Session::GenerateRSASessionKey(vector<uint8_t>* session_key,
void Session::VerifyEccSignature(const vector<uint8_t>& message,
const uint8_t* signature,
size_t signature_length) {
ASSERT_TRUE(public_ec_) << "No public ECC key loaded in test code";
const OEMCryptoResult result = public_ec_->VerifySignature(
message.data(), message.size(), signature, signature_length);
ASSERT_EQ(result, OEMCrypto_SUCCESS) << "ECC signature check failed";
}
bool Session::GenerateRsaSessionKey(vector<uint8_t>* session_key,
vector<uint8_t>* enc_session_key) {
if (!public_rsa_) {
cerr << "No public RSA key loaded in test code.\n";
cerr << "No public RSA key loaded in test code\n";
return false;
}
*session_key = wvutil::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1");
enc_session_key->assign(RSA_size(public_rsa_), 0);
int status = RSA_public_encrypt(
static_cast<int>(session_key->size()), &(session_key->front()),
&(enc_session_key->front()), public_rsa_, RSA_PKCS1_OAEP_PADDING);
int size = static_cast<int>(RSA_size(public_rsa_));
if (status != size) {
cerr << "GenerateRSASessionKey error encrypting session key.\n";
dump_boringssl_error();
*enc_session_key = public_rsa_->EncryptSessionKey(*session_key);
if (enc_session_key->empty()) {
return false;
}
return true;
}
void Session::InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key) {
bool Session::GenerateEccSessionKey(vector<uint8_t>* session_key,
vector<uint8_t>* ecdh_public_key_data) {
if (!public_ec_) {
cerr << "No public ECC key loaded in test code\n";
return false;
}
auto ephemeral_key = util::EccPrivateKey::New(public_ec_->curve());
if (!ephemeral_key) {
return false;
}
*session_key = ephemeral_key->DeriveSessionKey(*public_ec_);
if (session_key->empty()) {
return false;
}
*ecdh_public_key_data = ephemeral_key->SerializeAsPublicKey();
if (ecdh_public_key_data->empty()) {
session_key->clear();
return false;
}
return true;
}
void Session::LoadWrappedDrmKey(OEMCrypto_PrivateKeyType key_type,
const vector<uint8_t>& wrapped_drm_key) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDRMPrivateKey(session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key.data(),
wrapped_rsa_key.size()));
OEMCrypto_LoadDRMPrivateKey(session_id(), key_type,
wrapped_drm_key.data(),
wrapped_drm_key.size()));
}
void Session::LoadWrappedRsaDrmKey(const vector<uint8_t>& wrapped_rsa_key) {
ASSERT_NO_FATAL_FAILURE(
LoadWrappedDrmKey(OEMCrypto_RSA_Private_Key, wrapped_rsa_key));
}
void Session::LoadWrappedEccDrmKey(const vector<uint8_t>& wrapped_ecc_key) {
ASSERT_NO_FATAL_FAILURE(
LoadWrappedDrmKey(OEMCrypto_ECC_Private_Key, wrapped_ecc_key));
}
void Session::CreateNewUsageEntry(OEMCryptoResult* status) {