Add comments to unit tests

Merge from Widevine repo of http://go/wvgerrit/73383

Partners have been requesting more explanation for what each test is
doing.  This is an attempt to clarify some of the tests.

Bug: 119640588
Test: unit tests
Change-Id: I270203b4e8fa7e65abb55297788e4d55856e7dcd
This commit is contained in:
Fred Gylys-Colwell
2019-03-03 21:16:55 -08:00
parent 50e4d67415
commit 5f7803dedd
7 changed files with 306 additions and 21 deletions

View File

@@ -8,8 +8,13 @@
namespace wvoec { namespace wvoec {
// Keeps track of which features are supported by the version of OEMCrypto being
// tested. See the integration guide for a list of optional features.
class DeviceFeatures { class DeviceFeatures {
public: public:
// There are several possible methods used to derive a set of known session
// keys. For example, the test can install a known test keybox, or it can
// parse the OEM certificate.
enum DeriveMethod { // Method to use derive session keys. enum DeriveMethod { // Method to use derive session keys.
NO_METHOD, // Cannot derive known session keys. NO_METHOD, // Cannot derive known session keys.
LOAD_TEST_KEYBOX, // Call LoadTestKeybox before deriving keys. LOAD_TEST_KEYBOX, // Call LoadTestKeybox before deriving keys.
@@ -32,15 +37,24 @@ class DeviceFeatures {
uint32_t api_version; uint32_t api_version;
OEMCrypto_ProvisioningMethod provisioning_method; OEMCrypto_ProvisioningMethod provisioning_method;
// This should be called from the test program's main procedure.
void Initialize(bool is_cast_receiver, bool force_load_test_keybox); void Initialize(bool is_cast_receiver, bool force_load_test_keybox);
// Generate a GTest filter of tests that should not be run. This should be
// called after Initialize. Tests are filtered out based on which features
// are not supported. For example, a device that uses Provisioning 3.0 will
// have all keybox tests filtered out.
std::string RestrictFilter(const std::string& initial_filter); std::string RestrictFilter(const std::string& initial_filter);
private: private:
// Decide which method should be used to derive session keys, based on
// supported featuers.
void PickDerivedKey(); void PickDerivedKey();
bool IsTestKeyboxInstalled(); // Add a GTest filter restriction to the current filter.
void FilterOut(std::string* current_filter, const std::string& new_filter); void FilterOut(std::string* current_filter, const std::string& new_filter);
}; };
// There is one global set of features for the version of OEMCrypto being
// tested. This should be initialized in the test program's main procedure.
extern DeviceFeatures global_features; extern DeviceFeatures global_features;
const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method); const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method);

View File

@@ -80,6 +80,8 @@ void dump_boringssl_error() {
} }
} }
// A smart pointer for BoringSSL objects. It uses the specified free function
// to release resources and free memory when the pointer is deleted.
template <typename T, void (*func)(T*)> template <typename T, void (*func)(T*)>
class boringssl_ptr { class boringssl_ptr {
public: public:
@@ -163,6 +165,7 @@ void Session::close() {
} }
void Session::GenerateNonce(int* error_counter) { void Session::GenerateNonce(int* error_counter) {
// We make one attempt. If it fails, we assume there was a nonce flood.
if (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(session_id(), &nonce_)) { if (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(session_id(), &nonce_)) {
return; return;
} }
@@ -170,6 +173,8 @@ void Session::GenerateNonce(int* error_counter) {
(*error_counter)++; (*error_counter)++;
} else { } else {
sleep(1); // wait a second, then try again. sleep(1); // wait a second, then try again.
// The following is after a 1 second pause, so it cannot be from a nonce
// flood.
ASSERT_EQ(OEMCrypto_SUCCESS, ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateNonce(session_id(), &nonce_)); OEMCrypto_GenerateNonce(session_id(), &nonce_));
} }
@@ -196,6 +201,9 @@ void Session::FillDefaultContext(vector<uint8_t>* mac_context,
"180120002a0c31383836373837343035000000000080"); "180120002a0c31383836373837343035000000000080");
} }
// This generates the truth data for deriving one key. If there are failures in
// this function, then there is something wrong with the test program and its
// dependency on BoringSSL.
void Session::DeriveKey(const uint8_t* key, const vector<uint8_t>& context, void Session::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
int counter, vector<uint8_t>* out) { int counter, vector<uint8_t>* out) {
ASSERT_FALSE(context.empty()); ASSERT_FALSE(context.empty());
@@ -222,6 +230,9 @@ void Session::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
CMAC_CTX_free(cmac_ctx); CMAC_CTX_free(cmac_ctx);
} }
// This generates the truth data for deriving a set of keys. If there are
// failures in this function, then there is something wrong with the test
// program and its dependency on BoringSSL.
void Session::DeriveKeys(const uint8_t* master_key, void Session::DeriveKeys(const uint8_t* master_key,
const vector<uint8_t>& mac_key_context, const vector<uint8_t>& mac_key_context,
const vector<uint8_t>& enc_key_context) { const vector<uint8_t>& enc_key_context) {
@@ -241,6 +252,8 @@ void Session::DeriveKeys(const uint8_t* master_key,
DeriveKey(master_key, enc_key_context, 1, &enc_key_); DeriveKey(master_key, enc_key_context, 1, &enc_key_);
} }
// This should only be called if the device uses Provisioning 2.0. A failure in
// this function is probably caused by a bad keybox.
void Session::GenerateDerivedKeysFromKeybox( void Session::GenerateDerivedKeysFromKeybox(
const wvoec::WidevineKeybox& keybox) { const wvoec::WidevineKeybox& keybox) {
GenerateNonce(); GenerateNonce();
@@ -261,10 +274,13 @@ void Session::GenerateDerivedKeysFromSessionKey() {
vector<uint8_t> session_key; vector<uint8_t> session_key;
vector<uint8_t> enc_session_key; vector<uint8_t> enc_session_key;
if (public_rsa_ == NULL) PreparePublicKey(); if (public_rsa_ == NULL) PreparePublicKey();
// 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> mac_context;
vector<uint8_t> enc_context; vector<uint8_t> enc_context;
FillDefaultContext(&mac_context, &enc_context); FillDefaultContext(&mac_context, &enc_context);
// A failure here is probably caused by having the wrong RSA key loaded.
ASSERT_EQ(OEMCrypto_SUCCESS, ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey( OEMCrypto_DeriveKeysFromSessionKey(
session_id(), enc_session_key.data(), enc_session_key.size(), session_id(), enc_session_key.data(), enc_session_key.size(),
@@ -424,6 +440,10 @@ void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) {
VerifyEntitlementTestKeys(); VerifyEntitlementTestKeys();
} }
// This function verifies that the key control block reported by OEMCrypto agree
// with the truth key control block. Failures in this function probably
// indicate the OEMCrypto_LoadKeys did not correctly process the key control
// block.
void Session::VerifyTestKeys() { void Session::VerifyTestKeys() {
for (unsigned int i = 0; i < num_keys_; i++) { for (unsigned int i = 0; i < num_keys_; i++) {
KeyControlBlock block; KeyControlBlock block;
@@ -446,6 +466,10 @@ void Session::VerifyTestKeys() {
} }
} }
// This function verifies that the key control block reported by OEMCrypto agree
// with the truth key control block. Failures in this function probably
// indicate the OEMCrypto_LoadEntitledKeys did not correctly process the key
// control block.
void Session::VerifyEntitlementTestKeys() { void Session::VerifyEntitlementTestKeys() {
for (unsigned int i = 0; i < num_keys_; i++) { for (unsigned int i = 0; i < num_keys_; i++) {
KeyControlBlock block; KeyControlBlock block;

View File

@@ -20,7 +20,8 @@ const uint8_t* find(const vector<uint8_t>& message,
return &(*pos); return &(*pos);
} }
// If force is true, we assert that the key loads successfully. // This creates a wrapped RSA key for devices that have the test keybox
// installed. If force is true, we assert that the key loads successfully.
void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes,
bool force) { bool force) {
Session s; Session s;
@@ -42,7 +43,8 @@ void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes,
ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_));
} }
// If force is true, we assert that the key loads successfully. // This creates a wrapped RSA key for devices using provisioning 3.0. If force
// is true, we assert that the key loads successfully.
void SessionUtil::CreateWrappedRSAKeyFromOEMCert( void SessionUtil::CreateWrappedRSAKeyFromOEMCert(
uint32_t allowed_schemes, bool force) { uint32_t allowed_schemes, bool force) {
Session s; Session s;

View File

@@ -26,8 +26,12 @@ public:
// If force is true, we assert that the key loads successfully. // If force is true, we assert that the key loads successfully.
void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force); void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force);
// This is used to force installation of a keybox. This overwrites the
// production keybox -- it does NOT use OEMCrypto_LoadTestKeybox.
void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good); void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good);
// This loads the test keybox or the test RSA key, using LoadTestKeybox or
// LoadTestRSAKey as needed.
void EnsureTestKeys(); void EnsureTestKeys();
void InstallTestSessionKeys(Session* s); void InstallTestSessionKeys(Session* s);

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,7 @@ TEST_F(OEMCryptoAndroidLMPTest, GetKeyDataImplemented) {
} }
} }
// Android devices must have a valid keybox.
TEST_F(OEMCryptoAndroidLMPTest, ValidKeybox) { TEST_F(OEMCryptoAndroidLMPTest, ValidKeybox) {
if (OEMCrypto_GetProvisioningMethod() == OEMCrypto_Keybox) { if (OEMCrypto_GetProvisioningMethod() == OEMCrypto_Keybox) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid());
@@ -70,12 +71,14 @@ TEST_F(OEMCryptoAndroidLMPTest, RewrapDeviceRSAKeyImplemented) {
} }
} }
// This verifies that the device can load a DRM Certificate.
TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) { TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) {
ASSERT_NE( ASSERT_NE(
OEMCrypto_ERROR_NOT_IMPLEMENTED, OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_GenerateRSASignature(0, NULL, 0, NULL, NULL, kSign_RSASSA_PSS)); OEMCrypto_GenerateRSASignature(0, NULL, 0, NULL, NULL, kSign_RSASSA_PSS));
} }
// The Generic Crypto API functions are required for Android.
TEST_F(OEMCryptoAndroidLMPTest, GenericCryptoImplemented) { TEST_F(OEMCryptoAndroidLMPTest, GenericCryptoImplemented) {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_Generic_Encrypt(0, NULL, 0, NULL, OEMCrypto_Generic_Encrypt(0, NULL, 0, NULL,
@@ -91,10 +94,13 @@ TEST_F(OEMCryptoAndroidLMPTest, GenericCryptoImplemented) {
OEMCrypto_Generic_Verify(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, 0)); OEMCrypto_Generic_Verify(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, 0));
} }
// Android requires support of usage table. The usage table is used for Secure
// Stops and for offline licenses.
TEST_F(OEMCryptoAndroidLMPTest, SupportsUsageTable) { TEST_F(OEMCryptoAndroidLMPTest, SupportsUsageTable) {
ASSERT_TRUE(OEMCrypto_SupportsUsageTable()); ASSERT_TRUE(OEMCrypto_SupportsUsageTable());
} }
// Android devices require L1 OEMCrypto.
TEST_F(OEMCryptoAndroidLMPTest, Level1Required) { TEST_F(OEMCryptoAndroidLMPTest, Level1Required) {
const char* char_level = OEMCrypto_SecurityLevel(); const char* char_level = OEMCrypto_SecurityLevel();
std::string security_level(char_level ? char_level : ""); std::string security_level(char_level ? char_level : "");
@@ -112,6 +118,8 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) {
ASSERT_GE(version, 10u); ASSERT_GE(version, 10u);
} }
// Android devices using Provisioning 2.0 must be able to load a test keybox.
// If they are not using Provisioning 2.0, then they must use Provisioning 3.0.
TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) {
if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox( ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox(
@@ -123,6 +131,7 @@ TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) {
} }
} }
// Android requires implementation of these functions.
TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) { TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_GetNumberOfOpenSessions(NULL)); OEMCrypto_GetNumberOfOpenSessions(NULL));
@@ -130,6 +139,7 @@ TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) {
OEMCrypto_GetMaxNumberOfSessions(NULL)); OEMCrypto_GetMaxNumberOfSessions(NULL));
} }
// Android requires implementation of these functions.
TEST_F(OEMCryptoAndroidMNCTest, QueryKeyControlImplemented) { TEST_F(OEMCryptoAndroidMNCTest, QueryKeyControlImplemented) {
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
OEMCrypto_QueryKeyControl(0, NULL, 0, NULL, NULL)); OEMCrypto_QueryKeyControl(0, NULL, 0, NULL, NULL));

View File

@@ -12,6 +12,9 @@ static void acknowledge_cast() {
<< "==================================================================\n"; << "==================================================================\n";
} }
// This special main procedure is used instead of the standard GTest main,
// because we need to initialize the list of features supported by the device.
// Also, the test filter is updated based on the feature list.
int main(int argc, char** argv) { int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
wvcdm::g_cutoff = wvcdm::LOG_INFO; wvcdm::g_cutoff = wvcdm::LOG_INFO;