OEMCrypto Resource Rating
Merge from master branch of Widevine repo of http://go/wvgerrit/66072 Merge from oemcrypto-v15 branch of Widevine repo of http://go/wvgerrit/63764 This adds the function OEMCrypto_ResourceRatingTier to the oemcrypto referenece code, dynamic adapter, and unit tests. Bug: 117110800 Test: tested as part of http://go/ag/5501993 Change-Id: Idf47af405f0c69601108b75c788a97b30abdb39d
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ void PrintTo(const vector<uint8_t>& 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];
|
||||
|
||||
@@ -58,15 +58,33 @@ void PrintTo(const tuple<OEMCrypto_CENCEncryptPatternDesc, OEMCryptoCipherMode,
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
namespace wvoec {
|
||||
namespace {
|
||||
// Resource tiers:
|
||||
const size_t KiB = 1024;
|
||||
const size_t MiB = 1024 * 1024;
|
||||
template<typename T, size_t N>
|
||||
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<OEMCrypto_SESSION> 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<uint8_t> input_buffer(kMaxDecryptSize);
|
||||
size_t max_size = GetResourceValue(kMaxSubsampleSize);
|
||||
vector<uint8_t> input_buffer(max_size);
|
||||
GetRandBytes(&input_buffer[0], input_buffer.size());
|
||||
vector<uint8_t> output_buffer(kMaxDecryptSize);
|
||||
vector<uint8_t> 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<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> encryptedData(total_size_);
|
||||
vector<uint8_t> encryptionIv(AES_BLOCK_SIZE);
|
||||
vector<uint8_t> 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<uint8_t> unencryptedData(total_size_);
|
||||
vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> signature;
|
||||
|
||||
Reference in New Issue
Block a user