Files
ce_cdm/oemcrypto/util/test/oemcrypto_rsa_key_unittest.cpp
John "Juce" Bruce 694cf6fb25 Source release 17.1.0
2022-07-07 17:14:31 -07:00

418 lines
16 KiB
C++

// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Reference implementation utilities of OEMCrypto APIs
//
#include <stdlib.h>
#include <mutex>
#include <gtest/gtest.h>
#include "OEMCryptoCENC.h"
#include "log.h"
#include "oemcrypto_ref_test_utils.h"
#include "oemcrypto_rsa_key.h"
namespace wvoec {
namespace util {
constexpr size_t kMessageSize = 4 * 1024; // 4 kB
constexpr size_t kCastMessageSize = 83; // Special max size.
class OEMCryptoRsaKeyTest : public ::testing::TestWithParam<RsaFieldSize> {
public:
void SetUp() override {
// RSA key generation is slow (~2 seconds) compared to the
// operations they perform (<50 ms). Each key type is generated
// once and globally stored in serialized form.
// Caching the instance may result in test failures for
// memory-leak detection.
const RsaFieldSize field_size = GetParam();
std::lock_guard<std::mutex> rsa_key_lock(rsa_key_mutex_);
// Use of a switch case is intentional to cause compiler warnings
// if a new field size is introduced without updating the test.
switch (field_size) {
case kRsa2048Bit: {
if (!rsa_2048_key_data_.empty()) {
key_ = RsaPrivateKey::Load(rsa_2048_key_data_);
}
if (!key_) {
key_ = RsaPrivateKey::New(kRsa2048Bit);
}
if (rsa_2048_key_data_.empty() && key_) {
rsa_2048_key_data_ = key_->Serialize();
}
} break;
case kRsa3072Bit: {
if (!rsa_3072_key_data_.empty()) {
key_ = RsaPrivateKey::Load(rsa_3072_key_data_);
}
if (!key_) {
key_ = RsaPrivateKey::New(kRsa3072Bit);
}
if (rsa_3072_key_data_.empty() && key_) {
rsa_3072_key_data_ = key_->Serialize();
}
} break;
case kRsaFieldUnknown: // Suppress compiler warnings
LOGE("RSA test was incorrectly instantiation");
exit(EXIT_FAILURE);
break;
}
ASSERT_TRUE(key_) << "Key initialization failed "
<< RsaFieldSizeToString(field_size);
}
void TearDown() override { key_.reset(); }
protected:
std::unique_ptr<RsaPrivateKey> key_;
static std::mutex rsa_key_mutex_;
static std::vector<uint8_t> rsa_2048_key_data_;
static std::vector<uint8_t> rsa_3072_key_data_;
};
std::mutex OEMCryptoRsaKeyTest::rsa_key_mutex_;
std::vector<uint8_t> OEMCryptoRsaKeyTest::rsa_2048_key_data_;
std::vector<uint8_t> OEMCryptoRsaKeyTest::rsa_3072_key_data_;
// Basic verification of RSA private key generation.
TEST_P(OEMCryptoRsaKeyTest, KeyProperties) {
const RsaFieldSize expected_field_size = GetParam();
EXPECT_EQ(key_->field_size(), expected_field_size);
EXPECT_NE(nullptr, key_->GetRsaKey());
}
// Checks that the private key serialization APIs are compatible
// and performing in a manner that is similar to other OEMCrypto methods
// that retrieve data.
TEST_P(OEMCryptoRsaKeyTest, SerializePrivateKey) {
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
size_t buffer_size = kInitialBufferSize;
std::vector<uint8_t> buffer(buffer_size);
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
key_->Serialize(buffer.data(), &buffer_size));
EXPECT_GT(buffer_size, kInitialBufferSize);
buffer.resize(buffer_size);
EXPECT_EQ(OEMCrypto_SUCCESS, key_->Serialize(buffer.data(), &buffer_size));
buffer.resize(buffer_size);
const std::vector<uint8_t> direct_key_data = key_->Serialize();
EXPECT_FALSE(direct_key_data.empty());
ASSERT_EQ(buffer.size(), direct_key_data.size());
for (size_t i = 0; i < buffer.size(); i++) {
ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << std::to_string(i);
}
}
// Checks that a private key that is serialized can be deserialized and
// reload. Also checks that the serialization of a key produces the
// same data to ensure consistency.
TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPrivateKey) {
const std::vector<uint8_t> key_data = key_->Serialize();
std::unique_ptr<RsaPrivateKey> loaded_key = RsaPrivateKey::Load(key_data);
ASSERT_TRUE(loaded_key);
EXPECT_EQ(key_->field_size(), loaded_key->field_size());
const std::vector<uint8_t> loaded_key_data = loaded_key->Serialize();
ASSERT_EQ(key_data.size(), loaded_key_data.size());
for (size_t i = 0; i < key_data.size(); i++) {
ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << std::to_string(i);
}
}
// Checks that a private key with explicitly indicated schemes include
// the scheme fields in the reserialized key.
TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPrivateKeyWithAllowedSchemes) {
const std::vector<uint8_t> raw_key_data = key_->Serialize();
std::vector<uint8_t> key_data = {'S', 'I', 'G', 'N', 0x00, 0x00, 0x00, 0x03};
key_data.insert(key_data.end(), raw_key_data.begin(), raw_key_data.end());
std::unique_ptr<RsaPrivateKey> explicit_key = RsaPrivateKey::Load(key_data);
ASSERT_TRUE(explicit_key);
EXPECT_EQ(key_->field_size(), explicit_key->field_size());
const uint32_t kExpectedSchemes = 0x03;
EXPECT_EQ(explicit_key->allowed_schemes(), kExpectedSchemes);
const std::vector<uint8_t> explicit_key_data = explicit_key->Serialize();
ASSERT_EQ(key_data.size(), explicit_key_data.size());
ASSERT_EQ(key_data, explicit_key_data);
}
// Checks that a public key can be created from the private key.
TEST_P(OEMCryptoRsaKeyTest, DerivePublicKey) {
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
ASSERT_TRUE(pub_key);
EXPECT_TRUE(key_->IsMatchingPublicKey(*pub_key));
}
// Checks that the public key serialization APIs are compatible
// and performing in a manner that is similar to other OEMCrypto methods
// that retrieve data.
TEST_P(OEMCryptoRsaKeyTest, SerializePublicKey) {
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
ASSERT_TRUE(pub_key);
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
size_t buffer_size = kInitialBufferSize;
std::vector<uint8_t> buffer(buffer_size);
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
pub_key->Serialize(buffer.data(), &buffer_size));
EXPECT_GT(buffer_size, kInitialBufferSize);
buffer.resize(buffer_size);
EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->Serialize(buffer.data(), &buffer_size));
buffer.resize(buffer_size);
const std::vector<uint8_t> direct_key_data = pub_key->Serialize();
EXPECT_FALSE(direct_key_data.empty());
ASSERT_EQ(buffer.size(), direct_key_data.size());
for (size_t i = 0; i < buffer.size(); i++) {
ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << std::to_string(i);
}
}
// Checks that a public key that is serialized can be deserialized and
// reload. Also checks that the serialization of a key produces the
// same data to ensure consistency.
TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPublicKey) {
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
ASSERT_TRUE(pub_key);
const std::vector<uint8_t> key_data = pub_key->Serialize();
std::unique_ptr<RsaPublicKey> loaded_key = RsaPublicKey::Load(key_data);
ASSERT_TRUE(loaded_key);
EXPECT_EQ(pub_key->field_size(), loaded_key->field_size());
EXPECT_EQ(pub_key->allowed_schemes(), loaded_key->allowed_schemes());
const std::vector<uint8_t> loaded_key_data = loaded_key->Serialize();
ASSERT_EQ(key_data.size(), loaded_key_data.size());
for (size_t i = 0; i < key_data.size(); i++) {
ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << std::to_string(i);
}
}
// Checks that a public key can be initialized from a ASN.1 DER encoded
// PrivateKeyInfo message.
TEST_P(OEMCryptoRsaKeyTest, SerializePrivateKeyAndReloadAsPublicKey) {
const std::vector<uint8_t> key_data = key_->Serialize();
ASSERT_FALSE(key_data.empty()) << "Failed to serialize as private key";
auto key_by_buffer =
RsaPublicKey::LoadPrivateKeyInfo(key_data.data(), key_data.size());
ASSERT_TRUE(key_by_buffer)
<< "Failed to deserialize private key into public key";
EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_buffer));
key_by_buffer.reset();
auto key_by_vector = RsaPublicKey::LoadPrivateKeyInfo(key_data);
ASSERT_TRUE(key_by_vector)
<< "Failed to deserialize private key into public key";
EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_vector));
key_by_vector.reset();
const std::string key_data_str(key_data.begin(), key_data.end());
auto key_by_string = RsaPublicKey::LoadPrivateKeyInfo(key_data_str);
ASSERT_TRUE(key_by_string)
<< "Failed to deserialize private key into public key";
EXPECT_TRUE(key_->IsMatchingPublicKey(*key_by_string));
}
// Checks that the RSA signature generating API operates similar to
// existing signature generation functions.
TEST_P(OEMCryptoRsaKeyTest, GenerateSignature) {
const std::vector<uint8_t> message = RandomData(kMessageSize);
ASSERT_FALSE(message.empty()) << "CdmRandom failed";
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
size_t signature_size = kInitialBufferSize;
std::vector<uint8_t> signature(signature_size);
EXPECT_EQ(
OEMCrypto_ERROR_SHORT_BUFFER,
key_->GenerateSignature(message.data(), message.size(), kRsaPssDefault,
signature.data(), &signature_size));
EXPECT_GT(signature_size, kInitialBufferSize);
signature.resize(signature_size);
EXPECT_EQ(
OEMCrypto_SUCCESS,
key_->GenerateSignature(message.data(), message.size(), kRsaPssDefault,
signature.data(), &signature_size));
signature.resize(signature_size);
EXPECT_LE(signature_size, key_->SignatureSize());
}
// Checks that RSA signatures can be verified by an RSA public key.
TEST_P(OEMCryptoRsaKeyTest, VerifySignature) {
const std::vector<uint8_t> message = RandomData(kMessageSize);
ASSERT_FALSE(message.empty()) << "CdmRandom failed";
const std::vector<uint8_t> signature = key_->GenerateSignature(message);
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
ASSERT_TRUE(pub_key);
EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->VerifySignature(message, signature));
// Check with different message.
const std::vector<uint8_t> message_two = RandomData(kMessageSize);
EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
pub_key->VerifySignature(message_two, signature));
// Check with bad signature.
const std::vector<uint8_t> bad_signature = RandomData(signature.size());
EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
pub_key->VerifySignature(message, bad_signature));
}
// Checks that the special CAST receiver signature scheme works
// to the degree that it is possible to test.
TEST_P(OEMCryptoRsaKeyTest, GenerateAndVerifyRsaSignature) {
// Key must be enabled for PKCS1 Block 1 padding scheme.
// To do so, the key is serialized and the padding scheme is
// added to the key data.
const std::vector<uint8_t> key_data = key_->Serialize();
ASSERT_FALSE(key_data.empty());
std::vector<uint8_t> pkcs_enabled_key_data = {
'S', 'I', 'G', 'N', 0x00, 0x00, 0x00, kSign_PKCS1_Block1};
pkcs_enabled_key_data.insert(pkcs_enabled_key_data.end(), key_data.begin(),
key_data.end());
std::unique_ptr<RsaPrivateKey> pkcs_enabled_key =
RsaPrivateKey::Load(pkcs_enabled_key_data);
ASSERT_TRUE(pkcs_enabled_key);
// The actual cast message is a domain specific hash of the message,
// however, random data works for testing purposes.
const std::vector<uint8_t> message = RandomData(kCastMessageSize);
// Generate signature.
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
size_t signature_size = kInitialBufferSize;
std::vector<uint8_t> signature(signature_size);
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
pkcs_enabled_key->GenerateSignature(message.data(), message.size(),
kRsaPkcs1Cast, signature.data(),
&signature_size));
EXPECT_GT(signature_size, kInitialBufferSize);
signature.resize(signature_size);
EXPECT_EQ(OEMCrypto_SUCCESS,
pkcs_enabled_key->GenerateSignature(message.data(), message.size(),
kRsaPkcs1Cast, signature.data(),
&signature_size));
signature.resize(signature_size);
EXPECT_LE(signature_size, pkcs_enabled_key->SignatureSize());
// Verify signature.
std::unique_ptr<RsaPublicKey> pub_key = pkcs_enabled_key->MakePublicKey();
ASSERT_TRUE(pub_key);
EXPECT_EQ(OEMCrypto_SUCCESS,
pub_key->VerifySignature(message, signature, kRsaPkcs1Cast));
}
// Verifies the session key exchange protocol used by the licensing
// server.
TEST_P(OEMCryptoRsaKeyTest, ShareSessionKey) {
constexpr size_t kSessionKeySize = 16;
std::unique_ptr<RsaPublicKey> public_key = key_->MakePublicKey();
ASSERT_TRUE(public_key);
// Generate session key.
const std::vector<uint8_t> session_key = RandomData(kSessionKeySize);
ASSERT_FALSE(session_key.empty());
// Server's perspective.
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
size_t enc_session_key_size = kInitialBufferSize;
std::vector<uint8_t> enc_session_key(enc_session_key_size);
OEMCryptoResult result = public_key->EncryptSessionKey(
session_key.data(), session_key.size(), enc_session_key.data(),
&enc_session_key_size);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
enc_session_key.resize(enc_session_key_size);
result = public_key->EncryptSessionKey(session_key.data(), session_key.size(),
enc_session_key.data(),
&enc_session_key_size);
ASSERT_EQ(OEMCrypto_SUCCESS, result);
enc_session_key.resize(enc_session_key_size);
// Client's perspective.
size_t received_session_key_size = kInitialBufferSize;
std::vector<uint8_t> received_session_key(received_session_key_size);
result = key_->DecryptSessionKey(
enc_session_key.data(), enc_session_key.size(),
received_session_key.data(), &received_session_key_size);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
received_session_key.resize(received_session_key_size);
result = key_->DecryptSessionKey(
enc_session_key.data(), enc_session_key.size(),
received_session_key.data(), &received_session_key_size);
ASSERT_EQ(OEMCrypto_SUCCESS, result);
received_session_key.resize(received_session_key_size);
// Compare keys.
ASSERT_EQ(session_key.size(), received_session_key.size());
ASSERT_EQ(session_key, received_session_key);
}
// Verifies the encryption key exchange protocol used by the licensing
// server.
TEST_P(OEMCryptoRsaKeyTest, ShareEncryptionKey) {
constexpr size_t kEncryptionKeySize = 16;
std::unique_ptr<RsaPublicKey> public_key = key_->MakePublicKey();
ASSERT_TRUE(public_key);
// Generate session key.
const std::vector<uint8_t> encryption_key = RandomData(kEncryptionKeySize);
ASSERT_FALSE(encryption_key.empty());
// Server's perspective.
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
size_t enc_encryption_key_size = kInitialBufferSize;
std::vector<uint8_t> enc_encryption_key(enc_encryption_key_size);
OEMCryptoResult result = public_key->EncryptEncryptionKey(
encryption_key.data(), encryption_key.size(), enc_encryption_key.data(),
&enc_encryption_key_size);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
enc_encryption_key.resize(enc_encryption_key_size);
result = public_key->EncryptEncryptionKey(
encryption_key.data(), encryption_key.size(), enc_encryption_key.data(),
&enc_encryption_key_size);
ASSERT_EQ(OEMCrypto_SUCCESS, result);
enc_encryption_key.resize(enc_encryption_key_size);
// Client's perspective.
size_t received_encryption_key_size = kInitialBufferSize;
std::vector<uint8_t> received_encryption_key(received_encryption_key_size);
result = key_->DecryptEncryptionKey(
enc_encryption_key.data(), enc_encryption_key.size(),
received_encryption_key.data(), &received_encryption_key_size);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
received_encryption_key.resize(received_encryption_key_size);
result = key_->DecryptEncryptionKey(
enc_encryption_key.data(), enc_encryption_key.size(),
received_encryption_key.data(), &received_encryption_key_size);
ASSERT_EQ(OEMCrypto_SUCCESS, result);
received_encryption_key.resize(received_encryption_key_size);
// Compare keys.
ASSERT_EQ(encryption_key.size(), received_encryption_key.size());
ASSERT_EQ(encryption_key, received_encryption_key);
}
INSTANTIATE_TEST_SUITE_P(AllFieldSizes, OEMCryptoRsaKeyTest,
::testing::Values(kRsa2048Bit, kRsa3072Bit));
} // namespace util
} // namespace wvoec