259 lines
9.8 KiB
C++
259 lines
9.8 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 <gtest/gtest.h>
|
|
|
|
#include "OEMCryptoCENCCommon.h"
|
|
#include "oemcrypto_ecc_key.h"
|
|
#include "oemcrypto_ref_test_utils.h"
|
|
|
|
namespace wvoec {
|
|
namespace util {
|
|
constexpr size_t kMessageSize = 4 * 1024; // 4 kB
|
|
|
|
class OEMCryptoEccKeyTest : public ::testing::TestWithParam<EccCurve> {
|
|
public:
|
|
void SetUp() override {
|
|
key_ = EccPrivateKey::New(GetParam());
|
|
ASSERT_TRUE(key_) << "Key initialization failed: key = "
|
|
<< EccCurveToString(GetParam());
|
|
}
|
|
|
|
void TearDown() override { key_.reset(); }
|
|
|
|
protected:
|
|
std::unique_ptr<EccPrivateKey> key_;
|
|
};
|
|
|
|
// Basic verification of ECC private key generation.
|
|
TEST_P(OEMCryptoEccKeyTest, KeyProperties) {
|
|
const EccCurve expected_curve = GetParam();
|
|
EXPECT_EQ(key_->curve(), expected_curve);
|
|
EXPECT_NE(nullptr, key_->GetEcKey());
|
|
}
|
|
|
|
// 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(OEMCryptoEccKeyTest, 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 = " << 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(OEMCryptoEccKeyTest, SerializeAndReloadPrivateKey) {
|
|
const std::vector<uint8_t> key_data = key_->Serialize();
|
|
std::unique_ptr<EccPrivateKey> loaded_key = EccPrivateKey::Load(key_data);
|
|
ASSERT_TRUE(loaded_key);
|
|
|
|
EXPECT_EQ(key_->curve(), loaded_key->curve());
|
|
|
|
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 = " << i;
|
|
}
|
|
}
|
|
|
|
// Checks that a private key can be serialized as a public key, and
|
|
// that the serialized public key and be reloaded.
|
|
TEST_P(OEMCryptoEccKeyTest, SerializePrivateKeyAsPublicKey) {
|
|
const std::vector<uint8_t> key_data = key_->SerializeAsPublicKey();
|
|
ASSERT_FALSE(key_data.empty()) << "Failed to serialize as public key";
|
|
|
|
auto loaded_key = EccPublicKey::Load(key_data);
|
|
ASSERT_TRUE(loaded_key) << "Failed to deserialize public key";
|
|
EXPECT_TRUE(key_->IsMatchingPublicKey(*loaded_key));
|
|
EXPECT_TRUE(loaded_key->IsMatchingPrivateKey(*key_));
|
|
}
|
|
|
|
// Checks that a public key can be initialized from a ASN.1 DER encoded
|
|
// PrivateKeyInfo message.
|
|
TEST_P(OEMCryptoEccKeyTest, 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 =
|
|
EccPublicKey::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 = EccPublicKey::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 = EccPublicKey::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 a public key can be created from the private key.
|
|
TEST_P(OEMCryptoEccKeyTest, DerivePublicKey) {
|
|
std::unique_ptr<EccPublicKey> pub_key = key_->MakePublicKey();
|
|
ASSERT_TRUE(pub_key);
|
|
EXPECT_TRUE(key_->IsMatchingPublicKey(*pub_key));
|
|
}
|
|
|
|
// 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(OEMCryptoEccKeyTest, SerializePublicKey) {
|
|
std::unique_ptr<EccPublicKey> 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 = " << 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.
|
|
// It is anticipated that OEMCrypto will need to parse the licensing
|
|
// server's ephemerial key when deriving the session key.
|
|
TEST_P(OEMCryptoEccKeyTest, SerializeAndReloadPublicKey) {
|
|
std::unique_ptr<EccPublicKey> pub_key = key_->MakePublicKey();
|
|
ASSERT_TRUE(pub_key);
|
|
|
|
const std::vector<uint8_t> key_data = pub_key->Serialize();
|
|
|
|
std::unique_ptr<EccPublicKey> loaded_key = EccPublicKey::Load(key_data);
|
|
ASSERT_TRUE(loaded_key);
|
|
|
|
EXPECT_EQ(pub_key->curve(), loaded_key->curve());
|
|
|
|
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 = " << i;
|
|
}
|
|
}
|
|
|
|
// Checks that the ECC signature generating API operates similar to
|
|
// existing signature generation functions.
|
|
TEST_P(OEMCryptoEccKeyTest, 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(),
|
|
signature.data(), &signature_size));
|
|
EXPECT_GT(signature_size, kInitialBufferSize);
|
|
|
|
signature.resize(signature_size);
|
|
EXPECT_EQ(OEMCrypto_SUCCESS,
|
|
key_->GenerateSignature(message.data(), message.size(),
|
|
signature.data(), &signature_size));
|
|
signature.resize(signature_size);
|
|
|
|
EXPECT_LE(signature_size, key_->SignatureSize());
|
|
}
|
|
|
|
// Checks that ECC signatures can be verified by an ECC public key.
|
|
TEST_P(OEMCryptoEccKeyTest, 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<EccPublicKey> 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));
|
|
}
|
|
|
|
// Verifies the session key exchange protocol used by the licensing
|
|
// server.
|
|
TEST_P(OEMCryptoEccKeyTest, DeriveSessionKey) {
|
|
// Set up Alice.
|
|
EccPrivateKey* alice_private_key = key_.get();
|
|
std::unique_ptr<EccPublicKey> alice_public_key =
|
|
alice_private_key->MakePublicKey();
|
|
ASSERT_TRUE(alice_public_key);
|
|
// Set up Bob.
|
|
std::unique_ptr<EccPrivateKey> bob_private_key =
|
|
EccPrivateKey::New(alice_private_key->curve());
|
|
ASSERT_TRUE(bob_private_key);
|
|
std::unique_ptr<EccPublicKey> bob_public_key =
|
|
bob_private_key->MakePublicKey();
|
|
ASSERT_TRUE(bob_public_key);
|
|
|
|
const size_t session_key_length = alice_private_key->SessionKeyLength();
|
|
EXPECT_EQ(session_key_length, bob_private_key->SessionKeyLength());
|
|
// From Alice's perspective.
|
|
const std::vector<uint8_t> alice_session_key =
|
|
alice_private_key->DeriveSessionKey(*bob_public_key);
|
|
|
|
// From Bob's perspective.
|
|
const std::vector<uint8_t> bob_session_key =
|
|
bob_private_key->DeriveSessionKey(*alice_public_key);
|
|
|
|
// Both should have the same session key.
|
|
ASSERT_EQ(session_key_length, alice_session_key.size());
|
|
ASSERT_EQ(session_key_length, bob_session_key.size());
|
|
for (size_t i = 0; i < session_key_length; i++) {
|
|
ASSERT_EQ(alice_session_key[i], bob_session_key[i]) << "i = " << i;
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(AllCurves, OEMCryptoEccKeyTest,
|
|
::testing::Values(kEccSecp256r1, kEccSecp384r1,
|
|
kEccSecp521r1));
|
|
} // namespace util
|
|
} // namespace wvoec
|