Added unittests for reference ECC implementation.

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

The unittests check that the ECC keys are being created as expected
and that they can perform their basic operations.

Bug: 135283522
Test: oemcrypto_unittests
Change-Id: I1bdb26421ba47e1ab135f5ce5a54da304627a7c3
This commit is contained in:
Alex Dale
2021-02-18 14:16:45 -08:00
parent 4568ed6a04
commit c42782f6d3
4 changed files with 275 additions and 1 deletions

View File

@@ -4,6 +4,8 @@
//
// Reference implementation of OEMCrypto APIs
//
#include "oemcrypto_ecc_key.h"
#include <assert.h>
#include <string.h>
@@ -18,7 +20,6 @@
#include <openssl/x509.h>
#include "log.h"
#include "oemcrypto_ecc_key.h"
#include "scoped_object.h"
namespace wvoec_ref {

View File

@@ -0,0 +1,232 @@
// 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 of OEMCrypto APIs
//
#include <gtest/gtest.h>
#include "OEMCryptoCENCCommon.h"
#include "oemcrypto_ecc_key.h"
#include "oemcrypto_ref_test_utils.h"
namespace wvoec_ref {
constexpr size_t kMessageSize = 4 * 1024; // 4 kB
class OEMCryptoEccKeyTest : public ::testing::TestWithParam<EccCurve> {
public:
void SetUp() override { key_ = EccPrivateKey::New(GetParam()); }
void TearDown() override { key_.reset(); }
protected:
std::unique_ptr<EccPrivateKey> key_;
};
// Basic verification of ECC private key generation.
TEST_P(OEMCryptoEccKeyTest, KeyProperties) {
ASSERT_TRUE(key_);
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) {
ASSERT_TRUE(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,
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) {
ASSERT_TRUE(key_);
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 public key can be created from the private key.
TEST_P(OEMCryptoEccKeyTest, DerivePublicKey) {
ASSERT_TRUE(key_);
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) {
ASSERT_TRUE(key_);
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) {
ASSERT_TRUE(key_);
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) {
ASSERT_TRUE(key_);
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) {
ASSERT_TRUE(key_);
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) {
ASSERT_TRUE(key_);
// 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_CASE_P(AllCurves, OEMCryptoEccKeyTest,
::testing::Values(kEccSecp256r1, kEccSecp384r1,
kEccSecp521r1));
} // namespace wvoec_ref

View File

@@ -0,0 +1,20 @@
// 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 of OEMCrypto APIs
//
#include "oemcrypto_ref_test_utils.h"
#include <string>
#include "cdm_random.h"
namespace wvoec_ref {
std::vector<uint8_t> RandomData(size_t length) {
const std::string data = wvcdm::CdmRandom::RandomData(length);
return std::vector<uint8_t>(data.begin(), data.end());
}
} // namespace wvoec_ref

View File

@@ -0,0 +1,21 @@
// 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 of OEMCrypto APIs
//
#ifndef OEMCRYPTO_REF_TEST_UTILS_H_
#define OEMCRYPTO_REF_TEST_UTILS_H_
#include <stdint.h>
#include <vector>
namespace wvoec_ref {
// Returns a vector of random bytes.
std::vector<uint8_t> RandomData(size_t length);
} // namespace wvoec_ref
#endif // OEMCRYPTO_REF_TEST_UTILS_H_