From c42782f6d3fec30f6215b8ae765d7404a0196c33 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Thu, 18 Feb 2021 14:16:45 -0800 Subject: [PATCH] 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 --- .../oemcrypto/ref/src/oemcrypto_ecc_key.cpp | 3 +- .../ref/test/oemcrypto_ecc_key_unittest.cpp | 232 ++++++++++++++++++ .../ref/test/oemcrypto_ref_test_utils.cpp | 20 ++ .../ref/test/oemcrypto_ref_test_utils.h | 21 ++ 4 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 libwvdrmengine/oemcrypto/ref/test/oemcrypto_ecc_key_unittest.cpp create mode 100644 libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.cpp create mode 100644 libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.h diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ecc_key.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ecc_key.cpp index 4d8bb217..c19f77c0 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ecc_key.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ecc_key.cpp @@ -4,6 +4,8 @@ // // Reference implementation of OEMCrypto APIs // +#include "oemcrypto_ecc_key.h" + #include #include @@ -18,7 +20,6 @@ #include #include "log.h" -#include "oemcrypto_ecc_key.h" #include "scoped_object.h" namespace wvoec_ref { diff --git a/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ecc_key_unittest.cpp b/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ecc_key_unittest.cpp new file mode 100644 index 00000000..2fa92749 --- /dev/null +++ b/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ecc_key_unittest.cpp @@ -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 + +#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 { + public: + void SetUp() override { key_ = EccPrivateKey::New(GetParam()); } + + void TearDown() override { key_.reset(); } + + protected: + std::unique_ptr 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 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 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 key_data = key_->Serialize(); + std::unique_ptr loaded_key = EccPrivateKey::Load(key_data); + ASSERT_TRUE(loaded_key); + + EXPECT_EQ(key_->curve(), loaded_key->curve()); + + const std::vector 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 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 pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t buffer_size = kInitialBufferSize; + std::vector 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 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 pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + const std::vector key_data = pub_key->Serialize(); + + std::unique_ptr loaded_key = EccPublicKey::Load(key_data); + ASSERT_TRUE(loaded_key); + + EXPECT_EQ(pub_key->curve(), loaded_key->curve()); + + const std::vector 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 message = RandomData(kMessageSize); + ASSERT_FALSE(message.empty()) << "CdmRandom failed"; + + constexpr size_t kInitialBufferSize = 10; // Definitely too small. + size_t signature_size = kInitialBufferSize; + std::vector 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 message = RandomData(kMessageSize); + ASSERT_FALSE(message.empty()) << "CdmRandom failed"; + + const std::vector signature = key_->GenerateSignature(message); + + std::unique_ptr pub_key = key_->MakePublicKey(); + ASSERT_TRUE(pub_key); + + EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->VerifySignature(message, signature)); + + // Check with different message. + const std::vector message_two = RandomData(kMessageSize); + EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + pub_key->VerifySignature(message_two, signature)); + + // Check with bad signature. + const std::vector 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 alice_public_key = + alice_private_key->MakePublicKey(); + ASSERT_TRUE(alice_public_key); + // Set up Bob. + std::unique_ptr bob_private_key = + EccPrivateKey::New(alice_private_key->curve()); + ASSERT_TRUE(bob_private_key); + std::unique_ptr 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 alice_session_key = + alice_private_key->DeriveSessionKey(*bob_public_key); + + // From Bob's perspective. + const std::vector 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 diff --git a/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.cpp b/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.cpp new file mode 100644 index 00000000..27415482 --- /dev/null +++ b/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.cpp @@ -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 + +#include "cdm_random.h" + +namespace wvoec_ref { + +std::vector RandomData(size_t length) { + const std::string data = wvcdm::CdmRandom::RandomData(length); + return std::vector(data.begin(), data.end()); +} + +} // namespace wvoec_ref diff --git a/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.h b/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.h new file mode 100644 index 00000000..e229077b --- /dev/null +++ b/libwvdrmengine/oemcrypto/ref/test/oemcrypto_ref_test_utils.h @@ -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 + +#include + +namespace wvoec_ref { + +// Returns a vector of random bytes. +std::vector RandomData(size_t length); + +} // namespace wvoec_ref + +#endif // OEMCRYPTO_REF_TEST_UTILS_H_