diff --git a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h index 680cb5ae..80231d3c 100644 --- a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h +++ b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h @@ -54,6 +54,7 @@ OEMCryptoResult OEMCrypto_CreateOldUsageEntry(SecurityLevel level, size_t pst_length); uint32_t OEMCrypto_GetAnalogOutputFlags(SecurityLevel level); const char* OEMCrypto_BuildInformation(SecurityLevel level); +uint32_t OEMCrypto_ResourceRatingTier(SecurityLevel level); } // namespace wvcdm /* The following functions are deprecated in OEMCrypto v13. They are defined diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 872a0221..c288e5a4 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -40,6 +40,7 @@ std::string EncodeUint32(unsigned int u) { } const uint32_t kRsaSignatureLength = 256; +// TODO(b/117112392): adjust chunk size based on resource rating. const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB const size_t kEstimatedInitialUsageTableHeader = 40; const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13; diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 23a708b2..16540364 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -270,6 +270,7 @@ typedef OEMCryptoResult (*L1_CreateOldUsageEntry_t)( size_t pst_length); typedef uint32_t (*L1_GetAnalogOutputFlags_t)(void); typedef const char* (*L1_BuildInformation_t)(void); +typedef uint32_t (*L1_ResourceRatingTier_t)(void); struct FunctionPointers { uint32_t version; @@ -338,6 +339,7 @@ struct FunctionPointers { L1_CreateOldUsageEntry_t CreateOldUsageEntry; L1_GetAnalogOutputFlags_t GetAnalogOutputFlags; L1_BuildInformation_t BuildInformation; + L1_ResourceRatingTier_t ResourceRatingTier; L1_LoadKeys_V8_t LoadKeys_V8; L1_GenerateRSASignature_V8_t GenerateRSASignature_V8; @@ -695,6 +697,7 @@ class Adapter { LOOKUP_ALL(10, GetHDCPCapability, OEMCrypto_GetHDCPCapability); LOOKUP_ALL(14, GetAnalogOutputFlags, OEMCrypto_GetAnalogOutputFlags); LOOKUP_ALL(15, BuildInformation, OEMCrypto_BuildInformation); + LOOKUP_ALL(15, ResourceRatingTier, OEMCrypto_ResourceRatingTier); LOOKUP_ALL( 8, GetKeyData, OEMCrypto_GetKeyData); LOOKUP_ALL(10, GetMaxNumberOfSessions, OEMCrypto_GetMaxNumberOfSessions); LOOKUP_ALL(10, GetNumberOfOpenSessions, OEMCrypto_GetNumberOfOpenSessions); @@ -840,6 +843,7 @@ class Adapter { level3_.GetHDCPCapability = Level3_GetHDCPCapability; level3_.GetAnalogOutputFlags = Level3_GetAnalogOutputFlags; // TODO(srujzs) level3_.BuildInformation = Level3_BuildInformation; + // TODO(srujzs) level3_.ResourceRatingTier = Level3_ResourceRatingTier; level3_.SupportsUsageTable = Level3_SupportsUsageTable; level3_.IsAntiRollbackHwPresent = Level3_IsAntiRollbackHwPresent; level3_.GetNumberOfOpenSessions = Level3_GetNumberOfOpenSessions; @@ -1084,6 +1088,15 @@ const char* OEMCrypto_BuildInformation(SecurityLevel level) { return fcn->BuildInformation(); } +uint32_t OEMCrypto_ResourceRatingTier(SecurityLevel level) { + if (!gAdapter.get()) return 0; + const FunctionPointers* fcn = gAdapter->GetFunctionPointers(level); + if (!fcn) return 0; + if (fcn->version < 14) return 0; + if (fcn->ResourceRatingTier == NULL) return 0; + return fcn->ResourceRatingTier(); +} + bool OEMCrypto_SupportsUsageTable(SecurityLevel level) { if (!gAdapter.get()) return false; const FunctionPointers* fcn = gAdapter->GetFunctionPointers(level); @@ -1249,10 +1262,14 @@ extern "C" uint32_t OEMCrypto_GetAnalogOutputFlags() { return OEMCrypto_GetAnalogOutputFlags(kLevelDefault); } -extern "C" const char* OEMCrypto_BuildInformation(){ +extern "C" const char* OEMCrypto_BuildInformation() { return OEMCrypto_BuildInformation(kLevelDefault); } +extern "C" uint32_t OEMCrypto_ResourceRatingTier() { + return OEMCrypto_ResourceRatingTier(kLevelDefault); +} + extern "C" OEMCryptoResult OEMCrypto_LoadKeys_Back_Compat( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h index 58f443be..352bb890 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h @@ -143,6 +143,7 @@ class CryptoEngine { virtual uint8_t config_security_patch_level() { return 0; } // If 0 no restriction, otherwise it's the max buffer for DecryptCENC. + // This is the same as the max subsample size, not the sample or frame size. virtual size_t max_buffer_size() { return 1024 * 100; } // 100 KiB. virtual bool srm_update_supported() { return false; } @@ -169,6 +170,8 @@ class CryptoEngine { // size is unlimited -- or limited only by memory size. virtual size_t max_usage_table_size() { return 0; } + virtual uint32_t resource_rating() { return 1; } + // Set destination pointer based on the output destination description. OEMCryptoResult SetDestination(OEMCrypto_DestBufferDesc* out_description, size_t data_length, uint8_t subsample_flags); diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp index daaf7356..cff1c742 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -1152,6 +1152,14 @@ extern "C" const char* OEMCrypto_BuildInformation() { return "OEMCrypto Ref Code " __DATE__ " " __TIME__; } +extern "C" uint32_t OEMCrypto_ResourceRatingTier(){ + if (!crypto_engine) { + LOGE("OEMCrypto_ResourceRatingTier: OEMCrypto Not Initialized."); + return 0; + } + return crypto_engine->resource_rating(); +} + extern "C" bool OEMCrypto_SupportsUsageTable() { if (!crypto_engine) { LOGE("OEMCrypto_SupportsUsageTable: OEMCrypto Not Initialized."); diff --git a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp index bb2d4c82..7f6e7061 100644 --- a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp @@ -94,6 +94,9 @@ void DeviceFeatures::Initialize(bool is_cast_receiver, } } printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false"); + resource_rating = OEMCrypto_ResourceRatingTier(); + printf("resource_rating = %d, security leve %s.\n", resource_rating, + OEMCrypto_SecurityLevel()); switch (derive_key_method) { case NO_METHOD: printf("NO_METHOD: Cannot derive known session keys.\n"); diff --git a/libwvdrmengine/oemcrypto/test/oec_device_features.h b/libwvdrmengine/oemcrypto/test/oec_device_features.h index ac0deb57..f0728c24 100644 --- a/libwvdrmengine/oemcrypto/test/oec_device_features.h +++ b/libwvdrmengine/oemcrypto/test/oec_device_features.h @@ -27,6 +27,7 @@ class DeviceFeatures { bool usage_table; // Device saves usage information. bool supports_rsa_3072; // Device supports 3072 bit RSA keys. bool supports_level_1; // Device supports Level 1 security. + uint32_t resource_rating; // Device's resource rating tier. uint32_t api_version; OEMCrypto_ProvisioningMethod provisioning_method; diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index 9002d1bf..0de68ec8 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -27,6 +27,7 @@ void PrintTo(const vector& value, ostream* os); namespace wvoec { +// Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp const size_t kMaxNumKeys = 20; namespace { @@ -61,7 +62,6 @@ const int kDefaultKeyIdLength = 16; const size_t kMaxTestRSAKeyLength = 2000; // Rough estimate. const size_t kMaxPSTLength = 255; // In specification. const size_t kMaxMessageSize = 8 * 1024; // In specification. -const size_t kMaxDecryptSize = 100 * 1024; // In specification. typedef struct { uint8_t key_id[kTestKeyIdMaxLength]; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 76e017ce..7a6bd875 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -58,15 +58,33 @@ void PrintTo(const tuple +T GetResourceValue(T (&resource_values)[N]) { + if (global_features.resource_rating < 1) return resource_values[0]; + if (global_features.resource_rating > N) return resource_values[N-1]; + return resource_values[global_features.resource_rating-1]; +} +const size_t kMaxSampleSize[] = { 1000*KiB, 2*MiB, 4*MiB}; +const size_t kMaxNumberSubsamples[] = { 10, 16, 32}; +const size_t kMaxSubsampleSize[] = { 100*KiB, 500*KiB, 1*MiB}; +const size_t kMaxGenericBuffer[] = { 10*KiB, 100*KiB, 500*KiB}; +const size_t kMaxConcurrentSession[] = { 10, 20, 20}; +const size_t kMaxKeysPerSession [] = { 4, 20, 20}; +// Note: Frame rate and simultaneous playback are specified by resource rating, +// but are tested at the system level, so there are no unit tests for frame +// rate. + int GetRandBytes(unsigned char* buf, int num) { // returns 1 on success, -1 if not supported, or 0 if other failure. return RAND_bytes(buf, num); } } // namespace -namespace wvoec { - class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { protected: OEMCryptoClientTest() {} @@ -123,6 +141,11 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { ASSERT_LE(version, 15u); } +TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) { + ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u); + ASSERT_LE(OEMCrypto_ResourceRatingTier(), 3u); +} + TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { OEMCrypto_ProvisioningMethod provisioning_method = OEMCrypto_GetProvisioningMethod(); @@ -192,6 +215,8 @@ TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { OEMCryptoResult sts = OEMCrypto_GetMaxNumberOfSessions(&maximum); ASSERT_EQ(OEMCrypto_SUCCESS, sts); printf(" Max Number of Sessions: %zu.\n", maximum); + size_t required_max = GetResourceValue(kMaxConcurrentSession); + ASSERT_GE(maximum, required_max); } // @@ -240,13 +265,13 @@ TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) { ASSERT_EQ(0u, sessions_count); size_t max_sessions; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetMaxNumberOfSessions(&max_sessions)); - // We expect OEMCrypto implementations support at least 10 sessions. - const size_t kMinimumSupportedMaxNumberOfSessions = 10u; - ASSERT_GE(max_sessions, kMinimumSupportedMaxNumberOfSessions); + // We expect OEMCrypto implementations support at least this many sessions. + size_t required_number = GetResourceValue(kMaxConcurrentSession); + ASSERT_GE(max_sessions, required_number); // We allow GetMaxNumberOfSessions to return an estimate. This tests with a // pad of 5%. Even if it's just an estimate, we still require 8 sessions. size_t max_sessions_with_pad = - max(max_sessions * 19 / 20, kMinimumSupportedMaxNumberOfSessions); + max(max_sessions * 19 / 20, required_number); vector sessions; // Limit the number of sessions for testing. const size_t kMaxNumberOfSessionsForTesting = 0x100u; @@ -439,12 +464,13 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); } -TEST_F(OEMCryptoClientTest, ClearCopyTestLargeBufferAPI10) { +TEST_F(OEMCryptoClientTest, ClearCopyTestLargeSubsample) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - vector input_buffer(kMaxDecryptSize); + size_t max_size = GetResourceValue(kMaxSubsampleSize); + vector input_buffer(max_size); GetRandBytes(&input_buffer[0], input_buffer.size()); - vector output_buffer(kMaxDecryptSize); + vector output_buffer(max_size); OEMCrypto_DestBufferDesc dest_buffer; dest_buffer.type = OEMCrypto_BufferType_Clear; dest_buffer.buffer.clear.address = &output_buffer[0]; @@ -1409,15 +1435,17 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { } } -TEST_F(OEMCryptoSessionTests, Minimum20KeysAPI12) { +TEST_F(OEMCryptoSessionTests, MinimumKeysAPI12) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - s.set_num_keys(kMaxNumKeys); + size_t num_keys = GetResourceValue(kMaxKeysPerSession); + ASSERT_LE(num_keys, kMaxNumKeys) << "Test constants need updating."; + s.set_num_keys(num_keys); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); - for (size_t key_index = 0; key_index < kMaxNumKeys; key_index++) { + for (size_t key_index = 0; key_index < num_keys; key_index++) { bool kSelectKeyFirst = true; ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); @@ -2065,15 +2093,35 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, PartialBlock) { TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); } -TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptLargeBuffer) { - subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); - subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); - subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); - subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); - subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); - subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); - subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); - subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); +// Based on the resource rating, oemcrypto should handle at least +// kMaxNumberSubsamples na kMaxSampleSize +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSample) { + size_t max_size = GetResourceValue(kMaxSampleSize); + size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize); + size_t num_subsamples = GetResourceValue(kMaxNumberSubsamples); + if (num_subsamples * max_subsample_size > max_size) { + max_subsample_size = max_size / num_subsamples; + } + for(size_t i = 0; i < num_subsamples/2; i += 2) { + subsample_size_.push_back(SampleSize(max_subsample_size, 0)); + subsample_size_.push_back(SampleSize(0, max_subsample_size)); + } + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { + size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize); + subsample_size_.push_back(SampleSize(max_subsample_size, 0)); + subsample_size_.push_back(SampleSize(0, max_subsample_size)); FindTotalSize(); vector unencryptedData(total_size_); vector encryptedData(total_size_); @@ -4243,7 +4291,7 @@ TEST_F(GenericCryptoTest, GenericKeyBadVerify) { } TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBuffer) { - buffer_size_ = kMaxDecryptSize; + buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; @@ -4264,7 +4312,7 @@ TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBuffer) { TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBuffer) { // Some applications are known to pass in a block that is almost 400k. - buffer_size_ = kMaxDecryptSize; + buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; @@ -4284,7 +4332,7 @@ TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBuffer) { } TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { - buffer_size_ = kMaxDecryptSize; + buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 2; vector expected_signature; @@ -4311,7 +4359,7 @@ TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { } TEST_F(GenericCryptoTest, GenericKeyVerifyLargeBuffer) { - buffer_size_ = kMaxDecryptSize; + buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 3; vector signature;