diff --git a/libwvdrmengine/oemcrypto/test/oec_device_features.h b/libwvdrmengine/oemcrypto/test/oec_device_features.h index 8c8f79f0..c0d94e09 100644 --- a/libwvdrmengine/oemcrypto/test/oec_device_features.h +++ b/libwvdrmengine/oemcrypto/test/oec_device_features.h @@ -8,8 +8,13 @@ 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 { 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. NO_METHOD, // Cannot derive known session keys. LOAD_TEST_KEYBOX, // Call LoadTestKeybox before deriving keys. @@ -32,15 +37,24 @@ class DeviceFeatures { uint32_t api_version; 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); + // 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); private: + // Decide which method should be used to derive session keys, based on + // supported featuers. void PickDerivedKey(); - bool IsTestKeyboxInstalled(); + // Add a GTest filter restriction to the current 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; const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method); diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 6d8babd3..4ed98c50 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -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 class boringssl_ptr { public: @@ -163,6 +165,7 @@ void Session::close() { } 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_)) { return; } @@ -170,6 +173,8 @@ void Session::GenerateNonce(int* error_counter) { (*error_counter)++; } else { 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, OEMCrypto_GenerateNonce(session_id(), &nonce_)); } @@ -196,6 +201,9 @@ void Session::FillDefaultContext(vector* mac_context, "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& context, int counter, vector* out) { ASSERT_FALSE(context.empty()); @@ -222,6 +230,9 @@ void Session::DeriveKey(const uint8_t* key, const vector& context, 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, const vector& mac_key_context, const vector& enc_key_context) { @@ -241,6 +252,8 @@ void Session::DeriveKeys(const uint8_t* master_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( const wvoec::WidevineKeybox& keybox) { GenerateNonce(); @@ -261,10 +274,13 @@ void Session::GenerateDerivedKeysFromSessionKey() { vector session_key; vector enc_session_key; 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)); vector mac_context; vector enc_context; FillDefaultContext(&mac_context, &enc_context); + // A failure here is probably caused by having the wrong RSA key loaded. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_DeriveKeysFromSessionKey( session_id(), enc_session_key.data(), enc_session_key.size(), @@ -424,6 +440,10 @@ void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) { 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() { for (unsigned int i = 0; i < num_keys_; i++) { 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() { for (unsigned int i = 0; i < num_keys_; i++) { KeyControlBlock block; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.cpp index ffc67fb4..c9643b8e 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.cpp @@ -20,7 +20,8 @@ const uint8_t* find(const vector& message, 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, bool force) { Session s; @@ -42,7 +43,8 @@ void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, 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( uint32_t allowed_schemes, bool force) { Session s; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.h b/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.h index 2f4010d2..70469bdf 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_session_tests_helper.h @@ -26,8 +26,12 @@ public: // If force is true, we assert that the key loads successfully. 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); + // This loads the test keybox or the test RSA key, using LoadTestKeybox or + // LoadTestRSAKey as needed. void EnsureTestKeys(); void InstallTestSessionKeys(Session* s); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 930f2ffd..21da261c 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -63,6 +63,10 @@ namespace { // Resource tiers: const size_t KiB = 1024; const size_t MiB = 1024 * 1024; +// With OEMCrypto v15 and above, we have different resource requirements +// depending on the resource rating reported by OEMCrypto. This function looks +// up the required value for the specified resource for the target OEMCrypto +// library. template T GetResourceValue(T (&resource_values)[N]) { if (global_features.resource_rating < 1) return resource_values[0]; @@ -142,11 +146,13 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { ASSERT_LE(version, 15u); } +// The resource rating is a number from 1 to 3, defined API 15. TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) { ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u); ASSERT_LE(OEMCrypto_ResourceRatingTier(), 3u); } +// OEMCrypto must declare what type of provisioning scheme it uses. TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { OEMCrypto_ProvisioningMethod provisioning_method = OEMCrypto_GetProvisioningMethod(); @@ -261,6 +267,8 @@ TEST_F(OEMCryptoClientTest, EightSessionsOpenClose) { } } +// This test verifies that OEMCrypto can open approximately as many sessions as +// it claims. TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) { size_t sessions_count; ASSERT_EQ(OEMCrypto_SUCCESS, @@ -311,6 +319,9 @@ TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) { } } +// Verify that GetRandom does work, and does some sanity checks on how random +// the data is. Basically, we say that calling GetRandom twice should not +// generate much overlap. TEST_F(OEMCryptoClientTest, GetRandomLargeBuffer) { // 32 bytes. Not very large, but that's all we really need in one call. const size_t size = 32; @@ -345,6 +356,9 @@ TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { ASSERT_TRUE(nonce1 != nonce2); // Very unlikely to be equal. } +// OEMCrypto should limit the number of nonces that it can generate in one +// second. A flood of nonce requests can be used for a replay attack, which we +// wish to protect against. TEST_F(OEMCryptoClientTest, PreventNonceFloodAPI09) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -427,6 +441,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { EXPECT_EQ(0, error_counter); } +// This verifies that CopyBuffer works, even before a license has been loaded. TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -467,6 +482,7 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); } +// This verifies that CopyBuffer works on the maximum required buffer size. TEST_F(OEMCryptoClientTest, ClearCopyTestLargeSubsample) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -491,6 +507,8 @@ TEST_F(OEMCryptoClientTest, CanLoadTestKeys) { << "Session tests cannot run with out a test keybox or RSA cert."; } +// Tests using this class are only used for devices with a keybox. They are not +// run for devices with an OEM Certificate. class OEMCryptoKeyboxTest : public OEMCryptoClientTest { void SetUp() override { OEMCryptoClientTest::SetUp(); @@ -507,6 +525,7 @@ class OEMCryptoKeyboxTest : public OEMCryptoClientTest { } }; +// This test is used to print the device ID to stdout. TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) { OEMCryptoResult sts; uint8_t dev_id[128] = {0}; @@ -580,8 +599,11 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { enc_context.data(),enc_context.size())); } +// This class is for tests that have an OEM Certificate instead of a keybox. class OEMCryptoProv30Test : public OEMCryptoClientTest {}; +// This verifies that the device really does claim to have a certificate. +// It should be filtered out for devices that have a keybox. TEST_F(OEMCryptoProv30Test, DeviceClaimsOEMCertificate) { ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); } @@ -615,6 +637,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) { ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. } +// This verifies that an OEM Certificate can be used to generate a signature. TEST_F(OEMCryptoProv30Test, OEMCertSignature) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -643,6 +666,8 @@ TEST_F(OEMCryptoProv30Test, OEMCertSignature) { data, signature.data(), signature_length, kSign_RSASSA_PSS)); } +// This verifies that the OEM Certificate cannot be used for other RSA padding +// schemes. Those schemes should only be used by cast receiver certificates. TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -673,6 +698,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { ASSERT_EQ(zero, signature); // signature should not be computed. } +// Verify that the OEM Certificate can be used to sign a large buffer. TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -801,6 +827,8 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, BadDataForceKeybox) { ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); } +// Verify that keys can be derived from the test keybox, and then those derived +// keys can be used to sign a message. TEST_F(OEMCryptoSessionTestKeyboxTest, GenerateSignature) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -833,6 +861,7 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, GenerateSignature) { ASSERT_EQ(expected_signature, signature); } +// Verify that a license may be loaded without a nonce. TEST_F(OEMCryptoSessionTests, LoadKeyNoNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -842,6 +871,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoNonce) { ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); } +// Verify that a license may be loaded with a nonce. TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -869,6 +899,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeySeveralNonce) { ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); } +// A license might update the mac keys and it might not. This tests that +// OEMCrypto keeps the old mac keys if the license does not update them. TEST_F(OEMCryptoSessionTests, LoadKeyWithNoMAC) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -904,6 +936,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNoMAC) { ASSERT_EQ(expected_signature, signature); } +// This verifies that entitlement keys and entitled content keys can be loaded. TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysAPI14) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -917,6 +950,8 @@ TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysAPI14) { ASSERT_NO_FATAL_FAILURE(s.LoadEntitledContentKeys()); } +// This verifies that entitled content keys cannot be loaded if we have not yet +// loaded the entitlement keys. TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysNoEntitlementKeysAPI14) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -928,6 +963,8 @@ TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysNoEntitlementKeysAPI14) { s.LoadEntitledContentKeys(OEMCrypto_ERROR_INVALID_CONTEXT); } +// This verifies that entitled content keys cannot be loaded if we have loaded +// the wrong entitlement keys. TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysWrongEntitlementKeysAPI14) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -996,9 +1033,9 @@ std::string DuplicateMessage(MessageData& message) { return double_message; } -/* The Bad Range tests verify that OEMCrypto_LoadKeys checks the range - of all the pointers. It should reject a message if the pointer does - not point into the message buffer */ +// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range +// of all the pointers. It should reject a message if the pointer does +// not point into the message buffer. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange1) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1019,6 +1056,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange1) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range +// of all the pointers. It should reject a message if the pointer does +// not point into the message buffer. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange2) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1038,6 +1078,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange2) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range +// of all the pointers. It should reject a message if the pointer does +// not point into the message buffer. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange3) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1055,6 +1098,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange3) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range +// of all the pointers. It should reject a message if the pointer does +// not point into the message buffer. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange4) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1073,6 +1119,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange4) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range +// of all the pointers. It should reject a message if the pointer does +// not point into the message buffer. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange5) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1089,6 +1138,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange5) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range +// of all the pointers. It should reject a message if the pointer does +// not point into the message buffer. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange6) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1107,6 +1159,9 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange6) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range +// of all the pointers. It should reject a message if the pointer does +// not point into the message buffer. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1124,6 +1179,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// Test that LoadKeys fails when a key is loaded with no key control block. TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControl) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1141,6 +1197,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControl) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// Test that LoadKeys fails when the key control block encryption has a null IV. TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControlIv) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1158,6 +1215,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControlIv) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// Verify that LoadKeys fails when a key's nonce is not in the table. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1174,6 +1232,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// Verify that LoadKeys fails when an attempt is made to use a nonce twice. TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1182,6 +1241,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, nonce)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + // This is the first attempt. It should succeed. ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); ASSERT_NO_FATAL_FAILURE(s.close()); @@ -1190,6 +1250,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, nonce)); // same old nonce. ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + // This is the second attempt to load the keys with a repeated nonce. It + // should fail. OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), @@ -1199,7 +1261,10 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } -// This tests that a nonce cannot be used in new session. +// This tests that a nonce cannot be used in new session. This is similar to +// the previous test, but does not use the nonce in the first session. The nonce +// table should be tied to a session, so generating a nonce in the first session +// and then using it in the second session should fail. TEST_F(OEMCryptoSessionTests, LoadKeyNonceReopenSession) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1225,7 +1290,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNonceReopenSession) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } -// This tests that a nonce cannot be used in wrong session. +// This tests that a nonce cannot be used in wrong session. This is similar to +// the previous test, except we do not close session 1 before we open session 2. TEST_F(OEMCryptoSessionTests, LoadKeyNonceWrongSession) { Session s1; ASSERT_NO_FATAL_FAILURE(s1.open()); @@ -1250,6 +1316,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNonceWrongSession) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// LoadKeys should fail if the key control block as a bad verification string. TEST_F(OEMCryptoSessionTests, LoadKeyWithBadVerification) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1294,6 +1361,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyUnalignedMessage) { } // This tests each key control block verification string in the range kc09-kc1?. +// This test is parameterized by the API number in the key control lock. class SessionTestAlternateVerification : public OEMCryptoSessionTests, public WithParamInterface { public: @@ -1356,6 +1424,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// We should not be able to load keys if we haven't derived the mac keys. TEST_F(OEMCryptoSessionTests, LoadKeysWithNoDerivedKeys) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1370,6 +1439,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeysWithNoDerivedKeys) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// LoadKeys should fail if we try to load keys with no keys. TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1385,6 +1455,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { GetSubstring(), OEMCrypto_ContentLicense)); } +// Like the previous test, except we ask for a nonce first. TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1401,6 +1472,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { GetSubstring(), OEMCrypto_ContentLicense)); } +// SelectKey should fail if we attempt to select a key that has not been loaded. +// Also, the error should be NO_CONTENT_KEY. TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1416,6 +1489,9 @@ TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { strlen(key_id), OEMCrypto_CipherMode_CTR)); } +// After loading keys, we should be able to query the key control block. If we +// attempt to query a key that has not been loaded, the error should be +// NO_CONTENT_KEY. TEST_F(OEMCryptoSessionTests, QueryKeyControl) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1443,6 +1519,9 @@ TEST_F(OEMCryptoSessionTests, QueryKeyControl) { strlen(key_id), reinterpret_cast(&block), &size)); } +// If the device says it supports anti-rollback in the hardware, then it should +// accept a key control block the anti-rollback hardware bit set. Otherwise, it +// should reject that key control block. TEST_F(OEMCryptoSessionTests, AntiRollbackHardwareRequired) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1462,6 +1541,9 @@ TEST_F(OEMCryptoSessionTests, AntiRollbackHardwareRequired) { } } +// This test verifies that the minimum patch level can be required. The device +// should accept a key control block with the current patch level, and it should +// reject any key control blocks with a future patch level. TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { uint8_t patch_level = OEMCrypto_Security_Patch_Level(); printf(" Current Patch Level: %u.\n", patch_level); @@ -1480,6 +1562,7 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), OEMCrypto_ContentLicense)); } + // Reject any future patch levels. if (patch_level < 0x3F) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1495,6 +1578,7 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), OEMCrypto_ContentLicense)); } + // Accept an old patch level. if (patch_level > 0) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1512,6 +1596,8 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { } } +// This test verifies that OEMCrypto can load the number of keys required for +// the reported resource level. TEST_F(OEMCryptoSessionTests, MinimumKeysAPI12) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1529,6 +1615,8 @@ TEST_F(OEMCryptoSessionTests, MinimumKeysAPI12) { } } +// Used to test the different HDCP versions. This test is parameterized by the +// required HDCP version in the key control block. class SessionTestDecryptWithHDCP : public OEMCryptoSessionTests, public WithParamInterface { public: @@ -1566,6 +1654,11 @@ INSTANTIATE_TEST_CASE_P(TestHDCP, SessionTestDecryptWithHDCP, Range(1, 6)); // // Load, Refresh Keys Test // +// This test is parameterized by two parameters: +// 1. A boolean that determines if the license sets a new pair of mac keys in +// the license. +// 2. The number of keys refreshed in the refresh method. If the number of keys +// is zero, then all of the keys should be refreshed. class SessionTestRefreshKeyTest : public OEMCryptoSessionTests, public WithParamInterface > { @@ -1582,6 +1675,7 @@ class SessionTestRefreshKeyTest size_t num_keys_; // Number of keys to refresh. }; +// Refresh keys should work if the license does not use a nonce. TEST_P(SessionTestRefreshKeyTest, RefreshWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1598,6 +1692,7 @@ TEST_P(SessionTestRefreshKeyTest, RefreshWithNonce) { s.get_nonce(), OEMCrypto_SUCCESS)); } +// Refresh keys should work if the license does use a nonce. TEST_P(SessionTestRefreshKeyTest, RefreshNoNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1611,6 +1706,7 @@ TEST_P(SessionTestRefreshKeyTest, RefreshNoNonce) { s.RefreshTestKeys(num_keys_, 0, 0, OEMCrypto_SUCCESS)); } +// Refresh keys should fail if the nonce has already been used. TEST_P(SessionTestRefreshKeyTest, RefreshOldNonceAPI11) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1628,6 +1724,7 @@ TEST_P(SessionTestRefreshKeyTest, RefreshOldNonceAPI11) { OEMCrypto_ERROR_INVALID_NONCE)); } +// Refresh keys should fail if the nonce is not in the table. TEST_P(SessionTestRefreshKeyTest, RefreshBadNonceAPI11) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1645,6 +1742,7 @@ TEST_P(SessionTestRefreshKeyTest, RefreshBadNonceAPI11) { OEMCrypto_ERROR_INVALID_NONCE)); } +// Refresh keys should handle the maximum message size. TEST_P(SessionTestRefreshKeyTest, RefreshLargeBuffer) { Session s; s.set_message_size(kMaxMessageSize); @@ -1743,7 +1841,7 @@ TEST_F(OEMCryptoSessionTests, HashForbiddenAPI15) { } // -// Decrypt Tests +// Decrypt Tests -- these test Decrypt CTR mode only. // TEST_F(OEMCryptoSessionTests, Decrypt) { Session s; @@ -1755,6 +1853,7 @@ TEST_F(OEMCryptoSessionTests, Decrypt) { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); } +// Verify that a zero duration means infinite license duration. TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -1765,6 +1864,8 @@ TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); } +// Verify that several sessions may each load a license and then each may +// decrypt. TEST_F(OEMCryptoSessionTests, SimultaneousDecrypt) { vector s(8); for (int i = 0; i < 8; i++) { @@ -1834,6 +1935,13 @@ struct SampleInitData { size_t block_offset; }; +// A class of tests that test decryption for a variety of patterns and modes. +// This test is parameterized by three parameters: +// 1. The pattern used for pattern decryption. +// 2. The cipher mode for decryption: either CTR or CBC. +// 3. A boolean that determines if decrypt in place should be done. When the +// output buffer is clear, it should be possible for the input and output +// buffers to be the same. class OEMCryptoSessionTestsDecryptTests : public OEMCryptoSessionTests, public WithParamInterface unencryptedData(total_size_); @@ -2084,6 +2192,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) { TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); } +// Test that a single block can be decrypted. TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) { subsample_size_.push_back(SampleSize(0, 16)); FindTotalSize(); @@ -2235,6 +2344,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSample) { TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); } +// This tests that we can decrypt the required maximum number of subsamples. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize); subsample_size_.push_back(SampleSize(max_subsample_size, 0)); @@ -2251,8 +2361,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); } +// There are probably no frames this small, but we should handle them anyway. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { - // There are probably no frames this small, but we should handle them anyway. subsample_size_.push_back(SampleSize(5, 5)); FindTotalSize(); vector unencryptedData(total_size_); @@ -2266,6 +2376,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); } +// Test the case where there is only a clear subsample and no encrypted +// subsample. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) { subsample_size_.push_back(SampleSize(256, 0)); FindTotalSize(); @@ -2363,6 +2475,8 @@ INSTANTIATE_TEST_CASE_P( MakePattern(2, 1)), Values(OEMCrypto_CipherMode_CBC), Bool())); +// A request to decrypt data to a clear buffer when the key control block +// requires a secure data path. TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2377,6 +2491,7 @@ TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { s.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } +// If analog is forbidden, then decrypt to a clear buffer should be forbidden. TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2389,6 +2504,7 @@ TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { s.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); } +// Test that key duration is honored. TEST_F(OEMCryptoSessionTests, KeyDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2410,6 +2526,7 @@ TEST_F(OEMCryptoSessionTests, KeyDuration) { // class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest {}; +// This test verifies that we can create a wrapped RSA key, and then reload it. TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { CreateWrappedRSAKey(kSign_RSASSA_PSS, true); Session s; @@ -2417,6 +2534,9 @@ TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); } +// This creates a wrapped RSA key, and then does the sanity check that the +// unencrypted key is not found in the wrapped key. The wrapped key should be +// encrypted. TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { CreateWrappedRSAKey(kSign_RSASSA_PSS, true); // We should not be able to find the rsa key in the wrapped key. It should @@ -2424,6 +2544,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); } +// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning +// message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1KeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2454,6 +2576,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1KeyboxTest) { encrypted.rsa_key_iv, &(wrapped_key.front()), &wrapped_key_length)); } +// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning +// message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2KeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2486,6 +2610,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2KeyboxTest) { encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); } +// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning +// message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3KeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2519,6 +2645,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3KeyboxTest) { &(wrapped_key.front()), &wrapped_key_length)); } +// Test that RewrapDeviceRSAKey verifies the message signature. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2550,6 +2677,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) { encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); } +// Test that RewrapDeviceRSAKey verifies the nonce is current. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceKeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2581,6 +2709,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceKeyboxTest) { encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); } +// Test that RewrapDeviceRSAKey verifies the RSA key is valid. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2612,6 +2741,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) { encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); } +// Test that RewrapDeviceRSAKey accepts the maximum message size. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBufferKeyboxTest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2632,6 +2762,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBufferKeyboxTest) { ASSERT_EQ(NULL, find(wrapped_key, encoded_rsa_key_)); } +// Test that RewrapDeviceRSAKey30 verifies the nonce. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceProv30Test) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2662,6 +2793,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceProv30Test) { wrapped_key.data(), &wrapped_key_length)); } +// Test that RewrapDeviceRSAKey30 verifies that the RSA key is valid. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyProv30Test) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2693,6 +2825,7 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyProv30Test) { wrapped_key.data(), &wrapped_key_length)); } +// Test that a wrapped RSA key can be loaded. TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { OEMCryptoResult sts; CreateWrappedRSAKey(kSign_RSASSA_PSS, true); @@ -2917,6 +3050,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { license_request_time, derive_keys_time); } +// Test that OEMCrypto can compute an RSA signature. TEST_F(OEMCryptoUsesCertificate, RSASignature) { OEMCryptoResult sts; // Sign a Message @@ -2946,6 +3080,8 @@ TEST_F(OEMCryptoUsesCertificate, RSASignature) { licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); } +// Test that OEMCrypto can compute an RSA signature of a message with the +// maximum size. TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { OEMCryptoResult sts; // Sign a Message @@ -2975,6 +3111,7 @@ TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); } +// Test DeriveKeysFromSessionKey using the maximum size for the HMAC context. TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { vector session_key; vector enc_session_key; @@ -3131,10 +3268,10 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { } } +// Try to load an RSA key with alternative padding schemes. This key is allowed +// to sign with either padding scheme. Devices are not required to support both +// padding schemes. TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignatureBoth) { - // Try to load an RSA key with alternative padding schemes. This key - // is allowed to sign with either padding scheme. Devices are not required - // to support both padding schemes. LoadWithAllowedSchemes(kSign_RSASSA_PSS | kSign_PKCS1_Block1, false); // If the device loads this key, it should process it correctly. if (key_loaded_) { @@ -4059,6 +4196,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { TestSignature(kSign_PKCS1_Block1, message, signature); } +// This class is for testing the generic crypto functionality. class GenericCryptoTest : public OEMCryptoSessionTests { protected: // buffer_size_ must be a multiple of encryption block size, 16. We'll use a @@ -4077,6 +4215,8 @@ class GenericCryptoTest : public OEMCryptoSessionTests { OEMCryptoSessionTests::TearDown(); } + // This makes four keys, one for each of the generic operations that we want + // to test. void MakeFourKeys(uint32_t duration = kDuration, uint32_t control = 0, uint32_t nonce = 0, const std::string& pst = "") { ASSERT_NO_FATAL_FAILURE( @@ -4107,6 +4247,7 @@ class GenericCryptoTest : public OEMCryptoSessionTests { session_.LoadTestKeys(); } + // Encrypt the buffer with the specified key made in MakeFourKeys. void EncryptBuffer(unsigned int key_index, const vector& in_buffer, vector* out_buffer) { AES_KEY aes_key; @@ -4132,6 +4273,8 @@ class GenericCryptoTest : public OEMCryptoSessionTests { signature->data(), &md_len); } + // This asks OEMCrypto to encrypt with the specified key, and expects a + // failure. void BadEncrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, size_t buffer_length) { OEMCryptoResult sts; @@ -4152,6 +4295,8 @@ class GenericCryptoTest : public OEMCryptoSessionTests { EXPECT_NE(encrypted, expected_encrypted); } + // This asks OEMCrypto to decrypt with the specified key, and expects a + // failure. void BadDecrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, size_t buffer_length) { OEMCryptoResult sts; @@ -4171,6 +4316,8 @@ class GenericCryptoTest : public OEMCryptoSessionTests { EXPECT_NE(clear_buffer_, resultant); } + // This asks OEMCrypto to sign with the specified key, and expects a + // failure. void BadSign(unsigned int key_index, OEMCrypto_Algorithm algorithm) { OEMCryptoResult sts; vector expected_signature; @@ -4190,6 +4337,8 @@ class GenericCryptoTest : public OEMCryptoSessionTests { EXPECT_NE(signature, expected_signature); } + // This asks OEMCrypto to verify a signature with the specified key, and + // expects a failure. void BadVerify(unsigned int key_index, OEMCrypto_Algorithm algorithm, size_t signature_size, bool alter_data) { OEMCryptoResult sts; @@ -4220,6 +4369,7 @@ class GenericCryptoTest : public OEMCryptoSessionTests { TEST_F(GenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } +// Test that the Generic_Encrypt function works correctly. TEST_F(GenericCryptoTest, GenericKeyEncrypt) { EncryptAndLoadKeys(); unsigned int key_index = 0; @@ -4240,6 +4390,7 @@ TEST_F(GenericCryptoTest, GenericKeyEncrypt) { ASSERT_EQ(expected_encrypted, encrypted); } +// Test that the Generic_Encrypt function fails when not allowed. TEST_F(GenericCryptoTest, GenericKeyBadEncrypt) { EncryptAndLoadKeys(); BadEncrypt(0, OEMCrypto_HMAC_SHA256, buffer_size_); @@ -4250,6 +4401,8 @@ TEST_F(GenericCryptoTest, GenericKeyBadEncrypt) { BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } +// Test that the Generic_Encrypt works if the input and output buffers are the +// same. TEST_F(GenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 0; @@ -4270,6 +4423,7 @@ TEST_F(GenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { ASSERT_EQ(expected_encrypted, buffer); } +// Test Generic_Decrypt works correctly. TEST_F(GenericCryptoTest, GenericKeyDecrypt) { EncryptAndLoadKeys(); unsigned int key_index = 1; @@ -4289,6 +4443,8 @@ TEST_F(GenericCryptoTest, GenericKeyDecrypt) { ASSERT_EQ(clear_buffer_, resultant); } +// Test that Generic_Decrypt works correctly when the input and output buffers +// are the same. TEST_F(GenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 1; @@ -4308,6 +4464,8 @@ TEST_F(GenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { ASSERT_EQ(clear_buffer_, buffer); } +// Test that Generic_Decrypt fails to decrypt to an insecure buffer if the key +// requires a secure data path. TEST_F(GenericCryptoTest, GenericSecureToClear) { session_.license().keys[1].control.control_bits |= htonl( wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure); @@ -4329,6 +4487,7 @@ TEST_F(GenericCryptoTest, GenericSecureToClear) { ASSERT_NE(clear_buffer_, resultant); } +// Test that the Generic_Decrypt function fails when not allowed. TEST_F(GenericCryptoTest, GenericKeyBadDecrypt) { EncryptAndLoadKeys(); BadDecrypt(1, OEMCrypto_HMAC_SHA256, buffer_size_); @@ -4365,6 +4524,7 @@ TEST_F(GenericCryptoTest, GenericKeySign) { ASSERT_EQ(expected_signature, signature); } +// Test that the Generic_Sign function fails when not allowed. TEST_F(GenericCryptoTest, GenericKeyBadSign) { EncryptAndLoadKeys(); BadSign(0, OEMCrypto_HMAC_SHA256); // Can't sign with encrypt key. @@ -4392,6 +4552,7 @@ TEST_F(GenericCryptoTest, GenericKeyVerify) { signature.size())); } +// Test that the Generic_Verify function fails when not allowed. TEST_F(GenericCryptoTest, GenericKeyBadVerify) { EncryptAndLoadKeys(); BadVerify(0, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); @@ -4403,6 +4564,7 @@ TEST_F(GenericCryptoTest, GenericKeyBadVerify) { BadVerify(3, OEMCrypto_AES_CBC_128_NO_PADDING, SHA256_DIGEST_LENGTH, false); } +// Test Generic_Encrypt with the maximum buffer size. TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); @@ -4424,6 +4586,7 @@ TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBuffer) { ASSERT_EQ(expected_encrypted, encrypted); } +// Test Generic_Decrypt with the maximum buffer size. TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBuffer) { // Some applications are known to pass in a block that is almost 400k. buffer_size_ = GetResourceValue(kMaxGenericBuffer); @@ -4445,6 +4608,7 @@ TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBuffer) { ASSERT_EQ(clear_buffer_, resultant); } +// Test Generic_Sign with the maximum buffer size. TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); @@ -4472,6 +4636,7 @@ TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { ASSERT_EQ(expected_signature, signature); } +// Test Generic_Verify with the maximum buffer size. TEST_F(GenericCryptoTest, GenericKeyVerifyLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); @@ -4492,6 +4657,7 @@ TEST_F(GenericCryptoTest, GenericKeyVerifyLargeBuffer) { signature.size())); } +// Test Generic_Encrypt when the key duration has expired. TEST_F(GenericCryptoTest, KeyDurationEncrypt) { EncryptAndLoadKeys(); vector expected_encrypted; @@ -4526,6 +4692,7 @@ TEST_F(GenericCryptoTest, KeyDurationEncrypt) { ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } +// Test Generic_Decrypt when the key duration has expired. TEST_F(GenericCryptoTest, KeyDurationDecrypt) { EncryptAndLoadKeys(); @@ -4560,6 +4727,7 @@ TEST_F(GenericCryptoTest, KeyDurationDecrypt) { ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } +// Test Generic_Sign when the key duration has expired. TEST_F(GenericCryptoTest, KeyDurationSign) { EncryptAndLoadKeys(); @@ -4596,6 +4764,7 @@ TEST_F(GenericCryptoTest, KeyDurationSign) { ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } +// Test Generic_Verify when the key duration has expired. TEST_F(GenericCryptoTest, KeyDurationVerify) { EncryptAndLoadKeys(); @@ -4630,6 +4799,7 @@ TEST_F(GenericCryptoTest, KeyDurationVerify) { const unsigned int kLongKeyId = 2; +// Test that short key ids are allowed. class GenericCryptoKeyIdLengthTest : public GenericCryptoTest { protected: void SetUp() override { @@ -4706,6 +4876,7 @@ TEST_F(GenericCryptoKeyIdLengthTest, UniformLongKeyId) { TestWithKey(2); } +// Test usage table functionality. class UsageTableTest : public GenericCryptoTest { public: void SetUp() override { @@ -4761,6 +4932,8 @@ class UsageTableTest : public GenericCryptoTest { // Some usage tables we want to check a license either with or without a // new pair of mac keys in the license response. This affects signatures after // the license is loaded. +// This test is parameterized by a boolean which determines if the license +// installs new mac keys in LoadKeys. class UsageTableTestWithMAC : public UsageTableTest, public WithParamInterface { public: @@ -4770,6 +4943,8 @@ class UsageTableTestWithMAC : public UsageTableTest, } }; +// Test an online or streaming license with PST. This license requires a valid +// nonce and can only be loaded once. TEST_P(UsageTableTestWithMAC, OnlineLicense) { std::string pst = "my_pst"; Session s; @@ -4806,6 +4981,8 @@ TEST_P(UsageTableTestWithMAC, OnlineLicense) { ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); } +// Test the usage report when the license is loaded but the keys are never used +// for decryption. TEST_P(UsageTableTestWithMAC, OnlineLicenseUnused) { std::string pst = "my_pst"; Session s; @@ -4836,6 +5013,8 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseUnused) { ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUnused)); } +// Test that the usage table has been updated and saved before a report can be +// generated. TEST_P(UsageTableTestWithMAC, ForbidReportWithNoUpdate) { std::string pst = "my_pst"; Session s; @@ -4866,6 +5045,7 @@ TEST_P(UsageTableTestWithMAC, ForbidReportWithNoUpdate) { s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } +// Test an online license with a license renewal. TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { std::string pst = "my_pst"; Session s; @@ -4895,6 +5075,7 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { 0)); // last decrypt is now. } +// Verify that a streaming license cannot be reloaded. TEST_F(UsageTableTest, RepeatOnlineLicense) { std::string pst = "my_pst"; Session s; @@ -4924,7 +5105,7 @@ TEST_F(UsageTableTest, RepeatOnlineLicense) { ASSERT_NO_FATAL_FAILURE(s2.close()); } -// A license with non-zero replay control bits needs a valid pst.. +// A license with non-zero replay control bits needs a valid pst. TEST_F(UsageTableTest, OnlineEmptyPST) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -4943,7 +5124,7 @@ TEST_F(UsageTableTest, OnlineEmptyPST) { ASSERT_NO_FATAL_FAILURE(s.close()); } -// A license with non-zero replay control bits needs a valid pst.. +// A license with non-zero replay control bits needs a valid pst. TEST_F(UsageTableTest, OnlineMissingEntry) { std::string pst = "my_pst"; Session s; @@ -4963,6 +5144,7 @@ TEST_F(UsageTableTest, OnlineMissingEntry) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// Test generic encrypt when the license uses a PST. TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { std::string pst = "A PST"; uint32_t nonce = session_.get_nonce(); @@ -4999,6 +5181,7 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { EXPECT_NE(encrypted, expected_encrypted); } +// Test generic decrypt when the license uses a PST. TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { std::string pst = "my_pst"; uint32_t nonce = session_.get_nonce(); @@ -5036,6 +5219,7 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { EXPECT_NE(clear_buffer_, resultant); } +// Test generic sign when the license uses a PST. TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { std::string pst = "my_pst"; uint32_t nonce = session_.get_nonce(); @@ -5084,6 +5268,7 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { ASSERT_NE(signature, expected_signature); } +// Test generic verify when the license uses a PST. TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { std::string pst = "my_pst"; uint32_t nonce = session_.get_nonce(); @@ -5119,12 +5304,15 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// Test that an offline license can be loaded. TEST_P(UsageTableTestWithMAC, OfflineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); } +// Test that an offline license can be loaded and that the license can be +// renewed. TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { std::string pst = "my_pst"; Session s; @@ -5152,6 +5340,7 @@ TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { 0)); // last decrypt now. } +// Test that an offline license can be reloaded in a new session. TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { std::string pst = "my_pst"; Session s; @@ -5171,6 +5360,8 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// Test that an offline license can be reloaded in a new session, and then +// refreshed. TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { std::string pst = "my_pst"; Session s; @@ -5205,12 +5396,15 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// Verify that a license that has been we can still reload an offline license +// after OEMCrypto_Terminate and Initialize are called. This is as close to a +// reboot as we can do in a unit test. TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); - ShutDown(); - Restart(); + ShutDown(); // This calls OEMCrypto_Terminate. + Restart(); // This calls OEMCrypto_Initialize. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); @@ -5229,6 +5423,9 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// If we attempt to load a second license with the same usage entry as the +// first, but it has different mac keys, then the attempt should fail. This is +// how we verify that we are reloading the same license. TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { std::string pst = "my_pst"; Session s; @@ -5301,6 +5498,7 @@ TEST_P(UsageTableTestWithMAC, OfflineEmptyPST) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// If we try to reload a license with a different PST, the attempt should fail. TEST_P(UsageTableTestWithMAC, ReloadOfflineWrongPST) { std::string pst = "my_pst1"; Session s; @@ -5320,6 +5518,8 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineWrongPST) { GetSubstring(), OEMCrypto_ContentLicense)); } +// Once a license has been deactivated, the keys can no longer be used for +// decryption. However, we can still generate a usage report. TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { std::string pst = "my_pst"; Session s; @@ -5367,6 +5567,8 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } +// The usage report should indicate that the keys were never used for +// decryption. TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { std::string pst = "my_pst"; Session s1; @@ -5414,6 +5616,8 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { EXPECT_EQ(kInactiveUnused, s3.pst_report().status()); } +// If the PST pointers are not contained in the message, then LoadKeys should +// reject the attempt. TEST_P(UsageTableTestWithMAC, BadRange) { std::string pst = "my_pst"; Session s; @@ -5436,6 +5640,7 @@ TEST_P(UsageTableTestWithMAC, BadRange) { s.key_array(), wrong_pst, GetSubstring(), OEMCrypto_ContentLicense)); } +// Test update usage table fails when passed a null pointer. TEST_F(UsageTableTest, UpdateFailsWithNullPtr) { std::string pst = "my_pst"; Session s; @@ -5462,6 +5667,7 @@ TEST_F(UsageTableTest, UpdateFailsWithNullPtr) { &header_buffer_length, NULL, &entry_buffer_length)); } +// Class used to test usage table defragmentation. class UsageTableDefragTest : public UsageTableTest { protected: void LoadFirstLicense(Session* s, uint32_t index) { @@ -5533,6 +5739,7 @@ class UsageTableDefragTest : public UsageTableTest { } }; +// Verify that usage table entries can be moved around in the table. TEST_F(UsageTableDefragTest, MoveUsageEntries) { const size_t ENTRY_COUNT = 10; vector sessions(ENTRY_COUNT); @@ -5568,6 +5775,8 @@ TEST_F(UsageTableDefragTest, MoveUsageEntries) { FailReload(&sessions[3], OEMCrypto_ERROR_UNKNOWN_FAILURE)); } +// A usage table entry cannot be moved into an entry where an open session is +// currently using the entry. TEST_F(UsageTableDefragTest, MoveUsageEntriesToOpenSession) { Session s0; Session s1; @@ -5580,6 +5789,8 @@ TEST_F(UsageTableDefragTest, MoveUsageEntriesToOpenSession) { OEMCrypto_ERROR_ENTRY_IN_USE)); } +// The usage table cannot be shrunk if any session is using an entry that would +// be deleted. TEST_F(UsageTableDefragTest, ShrinkOverOpenSessions) { Session s0; Session s1; @@ -5595,6 +5806,7 @@ TEST_F(UsageTableDefragTest, ShrinkOverOpenSessions) { ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_SUCCESS)); } +// Verify the usage table size can be increased. TEST_F(UsageTableDefragTest, EnlargeHeader) { Session s0; Session s1; @@ -5604,6 +5816,7 @@ TEST_F(UsageTableDefragTest, EnlargeHeader) { ASSERT_NO_FATAL_FAILURE(ShrinkHeader(4, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } +// A new header can only be created while no entries are in use. TEST_F(UsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { Session s0; Session s1; @@ -5615,6 +5828,8 @@ TEST_F(UsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { ASSERT_NO_FATAL_FAILURE(CreateUsageTableHeader(kExpectFailure)); } +// Verify that a usage table entry can only be loaded into the correct index of +// the table. TEST_F(UsageTableDefragTest, ReloadUsageEntryWrongIndex) { Session s0; Session s1; @@ -5625,6 +5840,7 @@ TEST_F(UsageTableDefragTest, ReloadUsageEntryWrongIndex) { FailReload(&s0, OEMCrypto_ERROR_INVALID_SESSION)); } +// Verify that a usage table entry cannot be loaded if it has been altered. TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) { Session s; LoadFirstLicense(&s, 0); @@ -5645,6 +5861,7 @@ static std::string MakePST(size_t n) { return stream.str(); } +// This verifies we can actually create two hundered usage table entries. TEST_F(UsageTableDefragTest, TwoHundredEntries) { // OEMCrypto is required to store at least 200 entries in the usage table // header, but it is allowed to store more. This test verifies that if we keep @@ -5728,6 +5945,7 @@ TEST_F(UsageTableDefragTest, TwoHundredEntries) { } } +// This verifies that copying the old usage table to the new one works. TEST_F(UsageTableTest, CopyOldEntries) { // First create three old entries. We open sessions first to force creation // of the mac keys. @@ -5778,6 +5996,8 @@ TEST_F(UsageTableTest, CopyOldEntries) { s3.CopyAndVerifyOldEntry(report3, &encrypted_usage_header_)); } +// This verifies that the usage table header can be loaded if the generation +// number is off by one, but not off by two. TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { // This also tests a few other error conditions with usage table headers. std::string pst = "my_pst"; @@ -5861,6 +6081,7 @@ TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// A usage report with the wrong pst should fail. TEST_F(UsageTableTest, GenerateReportWrongPST) { std::string pst = "my_pst"; Session s; @@ -5876,6 +6097,7 @@ TEST_F(UsageTableTest, GenerateReportWrongPST) { OEMCrypto_ERROR_WRONG_PST)); } +// Test usage table timing. TEST_F(UsageTableTest, TimingTest) { std::string pst1 = "my_pst_1"; std::string pst2 = "my_pst_2"; @@ -5958,6 +6180,11 @@ TEST_F(UsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(s3.GenerateVerifyReport(pst3, kUnused, loaded3)); } +// Verify the times in the usage report. For performance reasons, we allow the +// times in the usage report to be off by as much as kUsageTimeTolerance, which +// is 10 seconds. This acceptable error is called slop. This test needs to run +// long enough that the reported values are distinct, even after accounting for +// this slop. TEST_F(UsageTableTest, VerifyUsageTimes) { std::string pst = "my_pst"; Session s; @@ -6128,6 +6355,7 @@ TEST_F(UsageTableTest, TimeRollbackPrevention) { ASSERT_NO_FATAL_FAILURE(s1.close()); } +// Verify that a large PST can be used with usage table entries. TEST_F(UsageTableTest, PSTLargeBuffer) { std::string pst(kMaxPSTLength, 'a'); // A large PST. Session s; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp index 79211a34..e0739ea6 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp @@ -43,6 +43,7 @@ TEST_F(OEMCryptoAndroidLMPTest, GetKeyDataImplemented) { } } +// Android devices must have a valid keybox. TEST_F(OEMCryptoAndroidLMPTest, ValidKeybox) { if (OEMCrypto_GetProvisioningMethod() == OEMCrypto_Keybox) { 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) { ASSERT_NE( OEMCrypto_ERROR_NOT_IMPLEMENTED, OEMCrypto_GenerateRSASignature(0, NULL, 0, NULL, NULL, kSign_RSASSA_PSS)); } +// The Generic Crypto API functions are required for Android. TEST_F(OEMCryptoAndroidLMPTest, GenericCryptoImplemented) { ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, 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)); } +// Android requires support of usage table. The usage table is used for Secure +// Stops and for offline licenses. TEST_F(OEMCryptoAndroidLMPTest, SupportsUsageTable) { ASSERT_TRUE(OEMCrypto_SupportsUsageTable()); } +// Android devices require L1 OEMCrypto. TEST_F(OEMCryptoAndroidLMPTest, Level1Required) { const char* char_level = OEMCrypto_SecurityLevel(); std::string security_level(char_level ? char_level : ""); @@ -112,6 +118,8 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) { 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) { if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox( @@ -123,6 +131,7 @@ TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { } } +// Android requires implementation of these functions. TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) { ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, OEMCrypto_GetNumberOfOpenSessions(NULL)); @@ -130,6 +139,7 @@ TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) { OEMCrypto_GetMaxNumberOfSessions(NULL)); } +// Android requires implementation of these functions. TEST_F(OEMCryptoAndroidMNCTest, QueryKeyControlImplemented) { ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, OEMCrypto_QueryKeyControl(0, NULL, 0, NULL, NULL)); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp index f53d7edd..baf839b9 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp @@ -12,6 +12,9 @@ static void acknowledge_cast() { << "==================================================================\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) { ::testing::InitGoogleTest(&argc, argv); wvcdm::g_cutoff = wvcdm::LOG_INFO;