Add decrypt hash support
[ Merge of http://go/wvgerrit/68083 ] Add ability to query decrypt hash support, set a hash computed over a frame and retrieve the last error at a later point. Bug: 34080802 Test: WV unit/integration tests. New tests added to cdm_engine_test, libwvdrmdrmplugin_hidl_test and request_license_test. Change-Id: I7548c8798c873a6af3e1cfc0df57c117e1e474a6
This commit is contained in:
@@ -75,6 +75,7 @@ using wvcdm::kLicenseTypeRelease;
|
||||
using wvcdm::kLicenseTypeStreaming;
|
||||
using wvcdm::kSecurityLevelL1;
|
||||
using wvcdm::kSecurityLevelL3;
|
||||
using wvcdm::Base64Encode;
|
||||
using wvcdm::CdmAppParameterMap;
|
||||
using wvcdm::CdmCertificateType;
|
||||
using wvcdm::CdmClientPropertySet;
|
||||
@@ -100,6 +101,7 @@ using wvcdm::KEY_SET_ID_PREFIX;
|
||||
using wvcdm::NEVER_EXPIRES;
|
||||
using wvcdm::QUERY_KEY_CURRENT_SRM_VERSION;
|
||||
using wvcdm::QUERY_KEY_DEVICE_ID;
|
||||
using wvcdm::QUERY_KEY_DECRYPT_HASH_SUPPORT;
|
||||
using wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS;
|
||||
using wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS;
|
||||
using wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION;
|
||||
@@ -208,6 +210,8 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
|
||||
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||
drm_metrics::WvCdmMetrics*));
|
||||
|
||||
MOCK_METHOD2(GetDecryptHashError, CdmResponseType(const CdmSessionId&, std::string*));
|
||||
};
|
||||
|
||||
class MockCrypto : public WVGenericCryptoInterface {
|
||||
@@ -344,7 +348,7 @@ TEST_F(WVDrmPluginTest, OpensSessions_1_1) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
// Provide expected mock behavior
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||
@@ -655,7 +659,7 @@ TEST_F(WVDrmPluginTest, HandlesPrivacyCertCaseOfAddKey) {
|
||||
sp<StrictMock<MockDrmPluginListener> > listener =
|
||||
new StrictMock<MockDrmPluginListener>();
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
// Provide expected behavior in response to OpenSession and store the
|
||||
// property set
|
||||
@@ -1138,6 +1142,10 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
static const std::string cdmVersion = "Infinity Minus 1";
|
||||
static const std::string resourceRatingTier = "1";
|
||||
static const std::string oemCryptoBuildInformation = "Mostly Harmless";
|
||||
static const std::string oemCryptoHashNotSupported = "0";
|
||||
static const std::string oemCryptoCrcClearBuffer = "1";
|
||||
static const std::string oemCryptoPartnerDefinedHash = "2";
|
||||
static const std::string decryptHashErrorBadHashAndFrameNumber = "53, 1";
|
||||
drm_metrics::WvCdmMetrics expected_metrics;
|
||||
std::string serialized_metrics = wvcdm::a2bs_hex(kSerializedMetricsHex);
|
||||
ASSERT_TRUE(expected_metrics.ParseFromString(serialized_metrics));
|
||||
@@ -1188,10 +1196,18 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
.WillOnce(DoAll(SetArgPointee<2>(resourceRatingTier),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_DECRYPT_HASH_SUPPORT, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>("1"),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(oemCryptoBuildInformation),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, GetDecryptHashError(_, _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(decryptHashErrorBadHashAndFrameNumber),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, GetMetrics(_, _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(expected_metrics),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
@@ -1311,6 +1327,24 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
||||
EXPECT_STREQ(oemCryptoBuildInformation.c_str(), stringResult.c_str());
|
||||
});
|
||||
|
||||
std::stringstream ss;
|
||||
ss << oemCryptoHashNotSupported << " " << oemCryptoCrcClearBuffer << " "
|
||||
<< oemCryptoPartnerDefinedHash;
|
||||
std::string validResults = ss.str();
|
||||
plugin.getPropertyString(
|
||||
hidl_string("decryptHashSupport"),
|
||||
[&](Status status, hidl_string stringResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
EXPECT_EQ(oemCryptoCrcClearBuffer, stringResult);
|
||||
});
|
||||
|
||||
plugin.getPropertyString(
|
||||
hidl_string("decryptHashError"),
|
||||
[&](Status status, hidl_string stringResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
EXPECT_EQ(decryptHashErrorBadHashAndFrameNumber, stringResult);
|
||||
});
|
||||
|
||||
// This call occurs before any open session or other call. This means
|
||||
// that the cdm identifer is not yet sealed, and metrics return empty
|
||||
// metrics data.
|
||||
@@ -2065,7 +2099,7 @@ TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
// Provide expected mock behavior
|
||||
{
|
||||
@@ -2104,7 +2138,7 @@ TEST_F(WVDrmPluginTest, CanSetAppId) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
// Provide expected mock behavior
|
||||
{
|
||||
@@ -2205,7 +2239,7 @@ TEST_F(WVDrmPluginTest, CanSetSecurityLevel) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L3),
|
||||
@@ -2335,7 +2369,7 @@ TEST_F(WVDrmPluginTest, CanSetPrivacyMode) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
// Provide expected mock behavior
|
||||
{
|
||||
@@ -2386,7 +2420,7 @@ TEST_F(WVDrmPluginTest, CanSetServiceCertificate) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
static const size_t kPrivacyCertSize = 256;
|
||||
uint8_t privacyCertRaw[kPrivacyCertSize];
|
||||
@@ -2459,7 +2493,7 @@ TEST_F(WVDrmPluginTest, CanSetSessionSharing) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const CdmClientPropertySet* propertySet = NULL;
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
// Provide expected mock behavior
|
||||
{
|
||||
@@ -2534,7 +2568,7 @@ TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
CdmClientPropertySet* propertySet = NULL;
|
||||
CdmClientPropertySet* propertySet = nullptr;
|
||||
|
||||
uint32_t sharingId;
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
@@ -2570,6 +2604,126 @@ TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) {
|
||||
EXPECT_EQ(sharingId, propertySet->session_sharing_id());
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CanSetDecryptHashProperties) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
// Provide expected mock behavior
|
||||
{
|
||||
// Provide expected behavior in response to OpenSession and store the
|
||||
// property set
|
||||
EXPECT_CALL(*cdm, OpenSession(_, _, _, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior when plugin requests session control info
|
||||
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm, CloseSession(_))
|
||||
.Times(AtLeast(0));
|
||||
}
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
hidl_vec<uint8_t> hSessionId;
|
||||
hSessionId.setToExternal(sessionIdRaw, kSessionIdSize);
|
||||
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(), hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
// CDM expects the string property value to be in the following format:
|
||||
// "<sessionId>,<frameNumber>,<base64encodedHash>"
|
||||
static const std::string frameNumber = ",1";
|
||||
uint32_t hash = 0xbeef; // crc32 hash
|
||||
std::vector<uint8_t> hashVector(
|
||||
reinterpret_cast<uint8_t*>(&hash),
|
||||
reinterpret_cast<uint8_t*>(&hash) + sizeof(uint32_t));
|
||||
std::string base64EncodedHash = Base64Encode(hashVector);
|
||||
std::string computedHash(sessionId.begin(), sessionId.end());
|
||||
computedHash.append(frameNumber.c_str());
|
||||
computedHash.append(base64EncodedHash.c_str());
|
||||
Status status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||
hidl_string(computedHash));
|
||||
ASSERT_NE(Status::OK, status);
|
||||
|
||||
status = plugin.closeSession(toHidlVec(sessionId));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, DoesNotSetDecryptHashProperties) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
// Provide expected mock behavior
|
||||
{
|
||||
// Provide expected behavior in response to OpenSession and store the
|
||||
// property set
|
||||
EXPECT_CALL(*cdm, OpenSession(_, _, _, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior when plugin requests session control info
|
||||
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm, CloseSession(_))
|
||||
.Times(AtLeast(0));
|
||||
}
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
hidl_vec<uint8_t> hSessionId;
|
||||
hSessionId.setToExternal(sessionIdRaw, kSessionIdSize);
|
||||
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(), hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
// CDM expects the string property value to be in the following format:
|
||||
// "<sessionId>,<frameNumber>,<base64encodedHash>"
|
||||
static const std::string frameNumber = ",1";
|
||||
static const std::string hash = ",AZaz0+,/";
|
||||
std::string value(sessionId.begin(), sessionId.end());
|
||||
value.append(frameNumber.c_str());
|
||||
|
||||
// Tests for missing token handling
|
||||
Status status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||
hidl_string(value));
|
||||
EXPECT_NE(Status::OK, status);
|
||||
|
||||
// Tests for empty token
|
||||
value.append(",");
|
||||
status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||
hidl_string(value));
|
||||
EXPECT_NE(Status::OK, status);
|
||||
|
||||
// Tests for invalid sessionId
|
||||
value.clear();
|
||||
value.append("bad session id");
|
||||
value.append(",1");
|
||||
value.append(hash.c_str());
|
||||
status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||
hidl_string(value));
|
||||
EXPECT_NE(Status::OK, status);
|
||||
|
||||
// Tests for malformed Base64encode hash, with a ","
|
||||
std::string computedHash(sessionId.begin(), sessionId.end());
|
||||
computedHash.append(frameNumber.c_str());
|
||||
computedHash.append(hash.c_str());
|
||||
status = plugin.setPropertyString(hidl_string("decryptHash"),
|
||||
hidl_string(computedHash));
|
||||
EXPECT_NE(Status::OK, status);
|
||||
|
||||
status = plugin.closeSession(toHidlVec(sessionId));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
} // namespace V1_2
|
||||
} // namespace drm
|
||||
|
||||
Reference in New Issue
Block a user