Also removes OnSessionExpiration which is no longer needed with OnSessionKeysChange. Bug: 19771612 Bug: 19771431 Merged from Widevine CDM repo: https://widevine-internal-review.googlesource.com/#/c/13951/ Change-Id: I0603e808e8d50ff7bb1fb1d5e44fabd8d268ee8a
1651 lines
55 KiB
C++
1651 lines
55 KiB
C++
//
|
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
//
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
|
|
#include "cdm_client_property_set.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "media/stagefright/foundation/ABase.h"
|
|
#include "media/stagefright/foundation/AString.h"
|
|
#include "media/stagefright/MediaErrors.h"
|
|
#include "wv_cdm_constants.h"
|
|
#include "wv_cdm_types.h"
|
|
#include "wv_content_decryption_module.h"
|
|
#include "WVDrmPlugin.h"
|
|
#include "WVErrors.h"
|
|
|
|
using namespace android;
|
|
using namespace std;
|
|
using namespace testing;
|
|
using namespace wvcdm;
|
|
using namespace wvdrm;
|
|
|
|
class MockCDM : public WvContentDecryptionModule {
|
|
public:
|
|
MOCK_METHOD4(OpenSession, CdmResponseType(const CdmKeySystem&,
|
|
CdmClientPropertySet*,
|
|
WvCdmEventListener*,
|
|
CdmSessionId*));
|
|
|
|
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
|
|
|
|
MOCK_METHOD10(GenerateKeyRequest,
|
|
CdmResponseType(const CdmSessionId&, const CdmKeySetId&,
|
|
const std::string&, const CdmInitData&,
|
|
const CdmLicenseType, CdmAppParameterMap&,
|
|
CdmClientPropertySet*, CdmKeyMessage*,
|
|
CdmKeyRequestType*, string*));
|
|
|
|
MOCK_METHOD3(AddKey, CdmResponseType(const CdmSessionId&,
|
|
const CdmKeyResponse&,
|
|
CdmKeySetId*));
|
|
|
|
MOCK_METHOD1(RemoveKeys, CdmResponseType(const CdmSessionId&));
|
|
|
|
MOCK_METHOD2(RestoreKey, CdmResponseType(const CdmSessionId&,
|
|
const CdmKeySetId&));
|
|
|
|
MOCK_METHOD1(QueryStatus, CdmResponseType(CdmQueryMap*));
|
|
|
|
MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&,
|
|
CdmQueryMap*));
|
|
|
|
MOCK_METHOD2(QueryKeyControlInfo, CdmResponseType(const CdmSessionId&,
|
|
CdmQueryMap*));
|
|
|
|
MOCK_METHOD4(GetProvisioningRequest, CdmResponseType(CdmCertificateType,
|
|
const std::string&,
|
|
CdmProvisioningRequest*,
|
|
std::string*));
|
|
|
|
MOCK_METHOD3(HandleProvisioningResponse,
|
|
CdmResponseType(CdmProvisioningResponse&, std::string*, std::string*));
|
|
|
|
MOCK_METHOD2(GetUsageInfo, CdmResponseType(const std::string&,
|
|
CdmUsageInfo*));
|
|
|
|
MOCK_METHOD3(GetUsageInfo, CdmResponseType(const std::string&,
|
|
const CdmSecureStopId&,
|
|
CdmUsageInfo*));
|
|
|
|
MOCK_METHOD1(Unprovision, CdmResponseType(CdmSecurityLevel));
|
|
|
|
MOCK_METHOD1(ReleaseAllUsageInfo, CdmResponseType(const std::string&));
|
|
|
|
MOCK_METHOD1(ReleaseUsageInfo,
|
|
CdmResponseType(const CdmUsageInfoReleaseMessage&));
|
|
};
|
|
|
|
class MockCrypto : public WVGenericCryptoInterface {
|
|
public:
|
|
MOCK_METHOD3(selectKey, OEMCryptoResult(const OEMCrypto_SESSION,
|
|
const uint8_t*, size_t));
|
|
|
|
MOCK_METHOD6(encrypt, OEMCryptoResult(OEMCrypto_SESSION, const uint8_t*,
|
|
size_t, const uint8_t*,
|
|
OEMCrypto_Algorithm, uint8_t*));
|
|
|
|
MOCK_METHOD6(decrypt, OEMCryptoResult(OEMCrypto_SESSION, const uint8_t*,
|
|
size_t, const uint8_t*,
|
|
OEMCrypto_Algorithm, uint8_t*));
|
|
|
|
MOCK_METHOD6(sign, OEMCryptoResult(OEMCrypto_SESSION, const uint8_t*, size_t,
|
|
OEMCrypto_Algorithm, uint8_t*, size_t*));
|
|
|
|
MOCK_METHOD6(verify, OEMCryptoResult(OEMCrypto_SESSION, const uint8_t*,
|
|
size_t, OEMCrypto_Algorithm,
|
|
const uint8_t*, size_t));
|
|
|
|
MOCK_METHOD3(loadDeviceRSAKey, OEMCryptoResult(OEMCrypto_SESSION,
|
|
const uint8_t*, size_t));
|
|
|
|
MOCK_METHOD6(generateRSASignature, OEMCryptoResult(OEMCrypto_SESSION,
|
|
const uint8_t*, size_t,
|
|
uint8_t*, size_t*,
|
|
RSA_Padding_Scheme));
|
|
};
|
|
|
|
class MockDrmPluginListener : public DrmPluginListener {
|
|
public:
|
|
MOCK_METHOD4(sendEvent, void(DrmPlugin::EventType, int,
|
|
const Vector<uint8_t>*, const Vector<uint8_t>*));
|
|
MOCK_METHOD2(sendExpirationUpdate, void(const Vector<uint8_t>*, int64_t));
|
|
MOCK_METHOD3(sendKeysChange, void(const Vector<uint8_t>*,
|
|
const Vector<DrmPlugin::KeyStatus>*, bool));
|
|
};
|
|
|
|
template <uint8_t DIGIT>
|
|
CdmResponseType setSessionIdOnMap(Unused, CdmQueryMap* map) {
|
|
static const char oecId[] = {DIGIT + '0', '\0'};
|
|
(*map)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = oecId;
|
|
return wvcdm::NO_ERROR;
|
|
}
|
|
|
|
class WVDrmPluginTest : public Test {
|
|
protected:
|
|
static const uint32_t kSessionIdSize = 16;
|
|
uint8_t sessionIdRaw[kSessionIdSize];
|
|
Vector<uint8_t> sessionId;
|
|
CdmSessionId cdmSessionId;
|
|
|
|
virtual void SetUp() {
|
|
// Fill the session ID
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp);
|
|
fclose(fp);
|
|
|
|
memcpy(sessionIdRaw, SESSION_ID_PREFIX, sizeof(SESSION_ID_PREFIX) - 1);
|
|
sessionId.appendArray(sessionIdRaw, kSessionIdSize);
|
|
cdmSessionId.assign(sessionId.begin(), sessionId.end());
|
|
|
|
// Set default return values for gMock
|
|
DefaultValue<CdmResponseType>::Set(wvcdm::NO_ERROR);
|
|
DefaultValue<OEMCryptoResult>::Set(OEMCrypto_SUCCESS);
|
|
DefaultValue<bool>::Set(true);
|
|
}
|
|
};
|
|
|
|
TEST_F(WVDrmPluginTest, OpensSessions) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.WillOnce(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(sessionId, ElementsAreArray(sessionIdRaw, kSessionIdSize));
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, ClosesSessions) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
EXPECT_CALL(cdm, CloseSession(cdmSessionId))
|
|
.Times(1);
|
|
|
|
status_t res = plugin.closeSession(sessionId);
|
|
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const size_t kInitDataSize = 128;
|
|
uint8_t initDataRaw[kInitDataSize];
|
|
static const size_t kRequestSize = 256;
|
|
uint8_t requestRaw[kRequestSize];
|
|
static const uint32_t kKeySetIdSize = 32;
|
|
uint8_t keySetIdRaw[kKeySetIdSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(initDataRaw, sizeof(uint8_t), kInitDataSize, fp);
|
|
fread(requestRaw, sizeof(uint8_t), kRequestSize, fp);
|
|
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
|
fclose(fp);
|
|
|
|
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX) - 1);
|
|
CdmKeySetId cdmKeySetId(reinterpret_cast<char*>(keySetIdRaw), kKeySetIdSize);
|
|
Vector<uint8_t> keySetId;
|
|
keySetId.appendArray(keySetIdRaw, kKeySetIdSize);
|
|
|
|
CdmInitData cdmInitData(reinterpret_cast<char*>(initDataRaw), kInitDataSize);
|
|
Vector<uint8_t> initData;
|
|
initData.appendArray(initDataRaw, kInitDataSize);
|
|
|
|
static const uint8_t psshPrefix[] = {
|
|
0, 0, 0, 32 + kInitDataSize, // Total size
|
|
'p', 's', 's', 'h', // "PSSH"
|
|
0, 0, 0, 0, // Flags - must be zero
|
|
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, // Widevine UUID
|
|
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
|
|
0, 0, 0, kInitDataSize // Size of initData
|
|
};
|
|
static const size_t kPsshPrefixSize = sizeof(psshPrefix);
|
|
static const size_t kPsshBoxSize = kPsshPrefixSize + kInitDataSize;
|
|
uint8_t psshBoxRaw[kPsshBoxSize];
|
|
memcpy(psshBoxRaw, psshPrefix, kPsshPrefixSize);
|
|
memcpy(psshBoxRaw + kPsshPrefixSize, initDataRaw, kInitDataSize);
|
|
CdmInitData cdmPsshBox(reinterpret_cast<char*>(psshBoxRaw), kPsshBoxSize);
|
|
Vector<uint8_t> psshBox;
|
|
psshBox.appendArray(psshBoxRaw, kPsshBoxSize);
|
|
|
|
CdmKeyMessage cdmRequest(requestRaw, requestRaw + kRequestSize);
|
|
|
|
KeyedVector<String8, String8> parameters;
|
|
CdmAppParameterMap cdmParameters;
|
|
parameters.add(String8("paddingScheme"), String8("BUBBLE WRAP"));
|
|
cdmParameters["paddingScheme"] = "BUBBLE WRAP";
|
|
parameters.add(String8("favorite-particle"), String8("tetraquark"));
|
|
cdmParameters["favorite-particle"] = "tetraquark";
|
|
parameters.add(String8("answer"), String8("6 * 9"));
|
|
cdmParameters["answer"] = "6 * 9";
|
|
|
|
static const char* kDefaultUrl = "http://google.com/";
|
|
static const char* kIsoBmffMimeType = "cenc";
|
|
static const char* kWebmMimeType = "webm";
|
|
|
|
struct TestSet {
|
|
const char* mimeType;
|
|
const Vector<uint8_t>& initDataIn;
|
|
const CdmInitData& initDataOut;
|
|
};
|
|
|
|
// We run the same set of operations on several sets of data, simulating
|
|
// different valid calling patterns.
|
|
TestSet testSets[] = {
|
|
{kIsoBmffMimeType, psshBox, cdmPsshBox}, // ISO-BMFF, EME passing style
|
|
{kIsoBmffMimeType, initData, cdmPsshBox}, // ISO-BMFF, old passing style
|
|
{kWebmMimeType, initData, cdmInitData} // WebM
|
|
};
|
|
size_t testSetCount = sizeof(testSets) / sizeof(TestSet);
|
|
|
|
// Set up the expected calls. Per gMock rules, this must be done for all test
|
|
// sets prior to testing any of them.
|
|
{
|
|
InSequence calls;
|
|
|
|
for (size_t i = 0; i < testSetCount; ++i)
|
|
{
|
|
const char* mimeType = testSets[i].mimeType;
|
|
const CdmInitData& initData = testSets[i].initDataOut;
|
|
|
|
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", mimeType, initData,
|
|
kLicenseTypeOffline, cdmParameters, _,
|
|
_, _, _))
|
|
.WillOnce(DoAll(SetArgPointee<7>(cdmRequest),
|
|
SetArgPointee<8>(kKeyRequestTypeInitial),
|
|
SetArgPointee<9>(kDefaultUrl),
|
|
Return(wvcdm::KEY_MESSAGE)));
|
|
|
|
EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", mimeType, initData,
|
|
kLicenseTypeStreaming, cdmParameters,
|
|
_, _, _, _))
|
|
.WillOnce(DoAll(SetArgPointee<7>(cdmRequest),
|
|
SetArgPointee<8>(kKeyRequestTypeRenewal),
|
|
SetArgPointee<9>(kDefaultUrl),
|
|
Return(wvcdm::KEY_MESSAGE)));
|
|
|
|
EXPECT_CALL(cdm, GenerateKeyRequest("", cdmKeySetId, mimeType, initData,
|
|
kLicenseTypeRelease, cdmParameters,
|
|
NotNull(), _, _, _))
|
|
.WillOnce(DoAll(SetArgPointee<7>(cdmRequest),
|
|
SetArgPointee<8>(kKeyRequestTypeRelease),
|
|
SetArgPointee<9>(kDefaultUrl),
|
|
Return(wvcdm::KEY_MESSAGE)));
|
|
}
|
|
}
|
|
|
|
// Performs the actual tests
|
|
for (size_t i = 0; i < testSetCount; ++i)
|
|
{
|
|
const String8 mimeType(testSets[i].mimeType);
|
|
const Vector<uint8_t>& initData = testSets[i].initDataIn;
|
|
|
|
Vector<uint8_t> request;
|
|
String8 defaultUrl;
|
|
DrmPlugin::KeyRequestType keyRequestType;
|
|
|
|
status_t res = plugin.getKeyRequest(sessionId, initData, mimeType,
|
|
DrmPlugin::kKeyType_Offline,
|
|
parameters, request, defaultUrl,
|
|
&keyRequestType);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
|
EXPECT_EQ(DrmPlugin::kKeyRequestType_Initial, keyRequestType);
|
|
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
|
|
|
res = plugin.getKeyRequest(sessionId, initData, mimeType,
|
|
DrmPlugin::kKeyType_Streaming, parameters,
|
|
request, defaultUrl, &keyRequestType);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
|
EXPECT_EQ(DrmPlugin::kKeyRequestType_Renewal, keyRequestType);
|
|
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
|
|
|
res = plugin.getKeyRequest(keySetId, initData, mimeType,
|
|
DrmPlugin::kKeyType_Release, parameters,
|
|
request, defaultUrl, &keyRequestType);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
|
EXPECT_EQ(DrmPlugin::kKeyRequestType_Release, keyRequestType);
|
|
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
|
}
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, AddsKeys) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const uint32_t kResponseSize = 256;
|
|
uint8_t responseRaw[kResponseSize];
|
|
static const uint32_t kKeySetIdSize = 32;
|
|
uint8_t keySetIdRaw[kKeySetIdSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(responseRaw, sizeof(uint8_t), kResponseSize, fp);
|
|
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> response;
|
|
response.appendArray(responseRaw, kResponseSize);
|
|
|
|
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX) - 1);
|
|
CdmKeySetId cdmKeySetId(reinterpret_cast<char *>(keySetIdRaw), kKeySetIdSize);
|
|
Vector<uint8_t> keySetId;
|
|
|
|
Vector<uint8_t> emptyKeySetId;
|
|
|
|
EXPECT_CALL(cdm, AddKey(cdmSessionId,
|
|
ElementsAreArray(responseRaw, kResponseSize), _))
|
|
.WillOnce(DoAll(SetArgPointee<2>(cdmKeySetId),
|
|
Return(wvcdm::KEY_ADDED)));
|
|
|
|
EXPECT_CALL(cdm, AddKey("", ElementsAreArray(responseRaw, kResponseSize),
|
|
Pointee(cdmKeySetId)))
|
|
.Times(1);
|
|
|
|
status_t res = plugin.provideKeyResponse(sessionId, response, keySetId);
|
|
ASSERT_EQ(OK, res);
|
|
ASSERT_THAT(keySetId, ElementsAreArray(keySetIdRaw, kKeySetIdSize));
|
|
|
|
res = plugin.provideKeyResponse(keySetId, response, emptyKeySetId);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_EQ(0u, emptyKeySetId.size());
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, HandlesPrivacyCertCaseOfAddKey) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
sp<StrictMock<MockDrmPluginListener> > listener =
|
|
new StrictMock<MockDrmPluginListener>();
|
|
|
|
const CdmClientPropertySet* propertySet = NULL;
|
|
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
static const uint32_t kResponseSize = 256;
|
|
uint8_t responseRaw[kResponseSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(responseRaw, sizeof(uint8_t), kResponseSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> response;
|
|
response.appendArray(responseRaw, kResponseSize);
|
|
Vector<uint8_t> keySetId;
|
|
|
|
EXPECT_CALL(*listener, sendEvent(DrmPlugin::kDrmPluginEventKeyNeeded, 0,
|
|
Pointee(ElementsAreArray(sessionIdRaw,
|
|
kSessionIdSize)),
|
|
NULL))
|
|
.Times(1);
|
|
|
|
EXPECT_CALL(cdm, AddKey(_, _, _))
|
|
.WillRepeatedly(Return(wvcdm::NEED_KEY));
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
|
|
status_t res = plugin.setListener(listener);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.setPropertyString(String8("privacyMode"), String8("enable"));
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_TRUE(propertySet->use_privacy_mode());
|
|
|
|
res = plugin.provideKeyResponse(sessionId, response, keySetId);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, RemovesKeys) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
EXPECT_CALL(cdm, RemoveKeys(cdmSessionId))
|
|
.Times(1);
|
|
|
|
status_t res = plugin.removeKeys(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, RestoresKeys) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const size_t kKeySetIdSize = 32;
|
|
uint8_t keySetIdRaw[kKeySetIdSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> keySetId;
|
|
keySetId.appendArray(keySetIdRaw, kKeySetIdSize);
|
|
|
|
EXPECT_CALL(cdm, RestoreKey(cdmSessionId,
|
|
ElementsAreArray(keySetIdRaw, kKeySetIdSize)))
|
|
.Times(1);
|
|
|
|
status_t res = plugin.restoreKeys(sessionId, keySetId);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, QueriesKeyStatus) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
KeyedVector<String8, String8> expectedLicenseStatus;
|
|
CdmQueryMap cdmLicenseStatus;
|
|
|
|
expectedLicenseStatus.add(String8("areTheKeysAllRight"), String8("yes"));
|
|
cdmLicenseStatus["areTheKeysAllRight"] = "yes";
|
|
expectedLicenseStatus.add(String8("isGMockAwesome"), String8("ohhhhhhYeah"));
|
|
cdmLicenseStatus["isGMockAwesome"] = "ohhhhhhYeah";
|
|
expectedLicenseStatus.add(String8("answer"), String8("42"));
|
|
cdmLicenseStatus["answer"] = "42";
|
|
|
|
EXPECT_CALL(cdm, QueryKeyStatus(cdmSessionId, _))
|
|
.WillOnce(DoAll(SetArgPointee<1>(cdmLicenseStatus),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
KeyedVector<String8, String8> licenseStatus;
|
|
|
|
status_t res = plugin.queryKeyStatus(sessionId, licenseStatus);
|
|
|
|
ASSERT_EQ(OK, res);
|
|
|
|
ASSERT_EQ(expectedLicenseStatus.size(), licenseStatus.size());
|
|
for (size_t i = 0; i < expectedLicenseStatus.size(); ++i) {
|
|
const String8& key = expectedLicenseStatus.keyAt(i);
|
|
EXPECT_NE(android::NAME_NOT_FOUND, licenseStatus.indexOfKey(key));
|
|
EXPECT_EQ(expectedLicenseStatus.valueFor(key), licenseStatus.valueFor(key));
|
|
}
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, GetsProvisioningRequests) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const uint32_t kRequestSize = 256;
|
|
uint8_t requestRaw[kRequestSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(requestRaw, sizeof(uint8_t), kRequestSize, fp);
|
|
fclose(fp);
|
|
|
|
CdmProvisioningRequest cdmRequest(requestRaw, requestRaw + kRequestSize);
|
|
|
|
static const char* kDefaultUrl = "http://google.com/";
|
|
|
|
EXPECT_CALL(cdm, GetProvisioningRequest(kCertificateWidevine, IsEmpty(),
|
|
_, _))
|
|
.WillOnce(DoAll(SetArgPointee<2>(cdmRequest),
|
|
SetArgPointee<3>(kDefaultUrl),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
Vector<uint8_t> request;
|
|
String8 defaultUrl;
|
|
|
|
status_t res = plugin.getProvisionRequest(String8(""), String8(""), request,
|
|
defaultUrl);
|
|
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize));
|
|
EXPECT_STREQ(kDefaultUrl, defaultUrl.string());
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const uint32_t kResponseSize = 512;
|
|
uint8_t responseRaw[kResponseSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(responseRaw, sizeof(uint8_t), kResponseSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> response;
|
|
response.appendArray(responseRaw, kResponseSize);
|
|
|
|
EXPECT_CALL(cdm, HandleProvisioningResponse(ElementsAreArray(responseRaw,
|
|
kResponseSize),
|
|
_, _))
|
|
.Times(1);
|
|
|
|
Vector<uint8_t> cert;
|
|
Vector<uint8_t> key;
|
|
|
|
status_t res = plugin.provideProvisionResponse(response, cert, key);
|
|
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, UnprovisionsDevice) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
EXPECT_CALL(cdm, Unprovision(kSecurityLevelL1))
|
|
.Times(1);
|
|
EXPECT_CALL(cdm, Unprovision(kSecurityLevelL3))
|
|
.Times(1);
|
|
|
|
status_t res = plugin.unprovisionDevice();
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, MuxesUnprovisioningErrors) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
// Tests that both Unprovisions are called even if one fails. Also tests that
|
|
// no matter which fails, the function always propagates the error.
|
|
EXPECT_CALL(cdm, Unprovision(kSecurityLevelL1))
|
|
.WillOnce(Return(wvcdm::UNKNOWN_ERROR))
|
|
.WillOnce(Return(wvcdm::NO_ERROR))
|
|
.WillOnce(Return(wvcdm::UNKNOWN_ERROR));
|
|
EXPECT_CALL(cdm, Unprovision(kSecurityLevelL3))
|
|
.WillOnce(Return(wvcdm::NO_ERROR))
|
|
.WillOnce(Return(wvcdm::UNKNOWN_ERROR))
|
|
.WillOnce(Return(wvcdm::UNKNOWN_ERROR));
|
|
|
|
status_t res = plugin.unprovisionDevice();
|
|
ASSERT_NE(OK, res);
|
|
res = plugin.unprovisionDevice();
|
|
ASSERT_NE(OK, res);
|
|
res = plugin.unprovisionDevice();
|
|
ASSERT_NE(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
const char* app_id = "my_app_id";
|
|
plugin.setPropertyString(String8("appId"), String8(app_id));
|
|
|
|
static const uint32_t kStopSize = 53;
|
|
static const uint32_t kStopCount = 7;
|
|
uint8_t stopsRaw[kStopCount][kStopSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
for (uint32_t i = 0; i < kStopCount; ++i) {
|
|
fread(stopsRaw[i], sizeof(uint8_t), kStopSize, fp);
|
|
}
|
|
fclose(fp);
|
|
|
|
CdmUsageInfo cdmStops;
|
|
for (uint32_t i = 0; i < kStopCount; ++i) {
|
|
cdmStops.push_back(string(stopsRaw[i], stopsRaw[i] + kStopSize));
|
|
}
|
|
|
|
EXPECT_CALL(cdm, GetUsageInfo(StrEq(app_id), _))
|
|
.WillOnce(DoAll(SetArgPointee<1>(cdmStops),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
List<Vector<uint8_t> > stops;
|
|
|
|
status_t res = plugin.getSecureStops(stops);
|
|
|
|
ASSERT_EQ(OK, res);
|
|
|
|
List<Vector<uint8_t> >::iterator iter = stops.begin();
|
|
uint32_t rawIter = 0;
|
|
while (rawIter < kStopCount && iter != stops.end()) {
|
|
EXPECT_THAT(*iter, ElementsAreArray(stopsRaw[rawIter], kStopSize));
|
|
|
|
++iter;
|
|
++rawIter;
|
|
}
|
|
// Assert that both lists are the same length
|
|
EXPECT_EQ(kStopCount, rawIter);
|
|
EXPECT_EQ(stops.end(), iter);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, ReleasesAllSecureStops) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
status_t res = plugin.setPropertyString(String8("appId"), String8(""));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
EXPECT_CALL(cdm, ReleaseAllUsageInfo(StrEq("")))
|
|
.Times(1);
|
|
|
|
res = plugin.releaseAllSecureStops();
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, ReleasesSecureStops) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const uint32_t kMessageSize = 128;
|
|
uint8_t messageRaw[kMessageSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(messageRaw, sizeof(uint8_t), kMessageSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> message;
|
|
message.appendArray(messageRaw, kMessageSize);
|
|
|
|
EXPECT_CALL(cdm, ReleaseUsageInfo(ElementsAreArray(messageRaw,
|
|
kMessageSize)))
|
|
.Times(1);
|
|
|
|
status_t res = plugin.releaseSecureStops(message);
|
|
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
CdmQueryMap l1Map;
|
|
l1Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
|
|
|
|
CdmQueryMap l3Map;
|
|
l3Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
|
|
|
|
static const string uniqueId = "The Universe";
|
|
CdmQueryMap deviceIDMap;
|
|
deviceIDMap[QUERY_KEY_DEVICE_ID] = uniqueId;
|
|
|
|
static const string systemId = "42";
|
|
CdmQueryMap systemIDMap;
|
|
systemIDMap[QUERY_KEY_SYSTEM_ID] = systemId;
|
|
|
|
static const string provisioningId("Life\0&Everything", 16);
|
|
CdmQueryMap provisioningIDMap;
|
|
provisioningIDMap[QUERY_KEY_PROVISIONING_ID] = provisioningId;
|
|
|
|
static const string maxSessions = "18";
|
|
CdmQueryMap maxSessionsMap;
|
|
maxSessionsMap[QUERY_KEY_MAX_NUMBER_OF_SESSIONS] = maxSessions;
|
|
|
|
EXPECT_CALL(cdm, QueryStatus(_))
|
|
.WillOnce(DoAll(SetArgPointee<0>(l1Map),
|
|
Return(wvcdm::NO_ERROR)))
|
|
.WillOnce(DoAll(SetArgPointee<0>(l3Map),
|
|
Return(wvcdm::NO_ERROR)))
|
|
.WillOnce(DoAll(SetArgPointee<0>(deviceIDMap),
|
|
Return(wvcdm::NO_ERROR)))
|
|
.WillOnce(DoAll(SetArgPointee<0>(systemIDMap),
|
|
Return(wvcdm::NO_ERROR)))
|
|
.WillOnce(DoAll(SetArgPointee<0>(provisioningIDMap),
|
|
Return(wvcdm::NO_ERROR)))
|
|
.WillOnce(DoAll(SetArgPointee<0>(maxSessionsMap),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
String8 stringResult;
|
|
Vector<uint8_t> vectorResult;
|
|
|
|
status_t res = plugin.getPropertyString(String8("vendor"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_STREQ("Google", stringResult.string());
|
|
|
|
res = plugin.getPropertyString(String8("version"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_STREQ("1.0", stringResult.string());
|
|
|
|
res = plugin.getPropertyString(String8("description"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_STREQ("Widevine CDM", stringResult.string());
|
|
|
|
res = plugin.getPropertyString(String8("algorithms"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_STREQ("AES/CBC/NoPadding,HmacSHA256", stringResult.string());
|
|
|
|
res = plugin.getPropertyString(String8("securityLevel"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_STREQ(QUERY_VALUE_SECURITY_LEVEL_L1.c_str(), stringResult.string());
|
|
|
|
res = plugin.getPropertyString(String8("securityLevel"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_STREQ(QUERY_VALUE_SECURITY_LEVEL_L3.c_str(), stringResult.string());
|
|
|
|
res = plugin.getPropertyByteArray(String8("deviceUniqueId"), vectorResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(vectorResult, ElementsAreArray(uniqueId.data(), uniqueId.size()));
|
|
|
|
res = plugin.getPropertyString(String8("systemId"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_STREQ(systemId.c_str(), stringResult.string());
|
|
|
|
res = plugin.getPropertyByteArray(String8("provisioningUniqueId"), vectorResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(vectorResult, ElementsAreArray(provisioningId.data(),
|
|
provisioningId.size()));
|
|
|
|
res = plugin.getPropertyString(String8("maxNumberOfSessions"), stringResult);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_EQ(maxSessions, stringResult.string());
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
String8 stringResult;
|
|
Vector<uint8_t> vectorResult;
|
|
|
|
status_t res = plugin.getPropertyString(String8("unknownProperty"),
|
|
stringResult);
|
|
ASSERT_NE(OK, res);
|
|
EXPECT_TRUE(stringResult.isEmpty());
|
|
|
|
res = plugin.getPropertyByteArray(String8("unknownProperty"),
|
|
vectorResult);
|
|
ASSERT_NE(OK, res);
|
|
EXPECT_TRUE(vectorResult.isEmpty());
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, DoesNotSetUnknownProperties) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const uint32_t kValueSize = 32;
|
|
uint8_t valueRaw[kValueSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(valueRaw, sizeof(uint8_t), kValueSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> value;
|
|
value.appendArray(valueRaw, kValueSize);
|
|
|
|
status_t res = plugin.setPropertyString(String8("unknownProperty"),
|
|
String8("ignored"));
|
|
ASSERT_NE(OK, res);
|
|
|
|
res = plugin.setPropertyByteArray(String8("unknownProperty"), value);
|
|
ASSERT_NE(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
Vector<uint8_t> keyId;
|
|
Vector<uint8_t> input;
|
|
Vector<uint8_t> iv;
|
|
Vector<uint8_t> output;
|
|
bool match;
|
|
|
|
// Provide expected behavior to support session creation
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
// Note that we do not set the algorithms. This should cause these methods
|
|
// to fail.
|
|
|
|
res = plugin.encrypt(sessionId, keyId, input, iv, output);
|
|
EXPECT_EQ(NO_INIT, res);
|
|
|
|
res = plugin.decrypt(sessionId, keyId, input, iv, output);
|
|
EXPECT_EQ(NO_INIT, res);
|
|
|
|
res = plugin.sign(sessionId, keyId, input, output);
|
|
EXPECT_EQ(NO_INIT, res);
|
|
|
|
res = plugin.verify(sessionId, keyId, input, output, match);
|
|
EXPECT_EQ(NO_INIT, res);
|
|
}
|
|
|
|
MATCHER_P(IsIV, iv, "") {
|
|
for (size_t i = 0; i < KEY_IV_SIZE; ++i) {
|
|
if (iv[i] != arg[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CallsGenericEncrypt) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const size_t kDataSize = 256;
|
|
uint8_t keyIdRaw[KEY_ID_SIZE];
|
|
uint8_t inputRaw[kDataSize];
|
|
uint8_t ivRaw[KEY_IV_SIZE];
|
|
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(keyIdRaw, sizeof(uint8_t), KEY_ID_SIZE, fp);
|
|
fread(inputRaw, sizeof(uint8_t), kDataSize, fp);
|
|
fread(ivRaw, sizeof(uint8_t), KEY_IV_SIZE, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> keyId;
|
|
keyId.appendArray(keyIdRaw, KEY_ID_SIZE);
|
|
Vector<uint8_t> input;
|
|
input.appendArray(inputRaw, kDataSize);
|
|
Vector<uint8_t> iv;
|
|
iv.appendArray(ivRaw, KEY_IV_SIZE);
|
|
Vector<uint8_t> output;
|
|
|
|
{
|
|
InSequence calls;
|
|
|
|
EXPECT_CALL(crypto, selectKey(4, _, KEY_ID_SIZE))
|
|
.With(Args<1, 2>(ElementsAreArray(keyIdRaw, KEY_ID_SIZE)))
|
|
.Times(1);
|
|
|
|
EXPECT_CALL(crypto, encrypt(4, _, kDataSize, IsIV(ivRaw),
|
|
OEMCrypto_AES_CBC_128_NO_PADDING, _))
|
|
.With(Args<1, 2>(ElementsAreArray(inputRaw, kDataSize)))
|
|
.Times(1);
|
|
}
|
|
|
|
// Provide expected behavior to support session creation
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.setCipherAlgorithm(sessionId, String8("AES/CBC/NoPadding"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.encrypt(sessionId, keyId, input, iv, output);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CallsGenericDecrypt) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const size_t kDataSize = 256;
|
|
uint8_t keyIdRaw[KEY_ID_SIZE];
|
|
uint8_t inputRaw[kDataSize];
|
|
uint8_t ivRaw[KEY_IV_SIZE];
|
|
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(keyIdRaw, sizeof(uint8_t), KEY_ID_SIZE, fp);
|
|
fread(inputRaw, sizeof(uint8_t), kDataSize, fp);
|
|
fread(ivRaw, sizeof(uint8_t), KEY_IV_SIZE, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> keyId;
|
|
keyId.appendArray(keyIdRaw, KEY_ID_SIZE);
|
|
Vector<uint8_t> input;
|
|
input.appendArray(inputRaw, kDataSize);
|
|
Vector<uint8_t> iv;
|
|
iv.appendArray(ivRaw, KEY_IV_SIZE);
|
|
Vector<uint8_t> output;
|
|
|
|
{
|
|
InSequence calls;
|
|
|
|
EXPECT_CALL(crypto, selectKey(4, _, KEY_ID_SIZE))
|
|
.With(Args<1, 2>(ElementsAreArray(keyIdRaw, KEY_ID_SIZE)))
|
|
.Times(1);
|
|
|
|
EXPECT_CALL(crypto, decrypt(4, _, kDataSize, IsIV(ivRaw),
|
|
OEMCrypto_AES_CBC_128_NO_PADDING, _))
|
|
.With(Args<1, 2>(ElementsAreArray(inputRaw, kDataSize)))
|
|
.Times(1);
|
|
}
|
|
|
|
// Provide expected behavior to support session creation
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.setCipherAlgorithm(sessionId, String8("AES/CBC/NoPadding"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.decrypt(sessionId, keyId, input, iv, output);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CallsGenericSign) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const size_t kDataSize = 256;
|
|
uint8_t keyIdRaw[KEY_ID_SIZE];
|
|
uint8_t messageRaw[kDataSize];
|
|
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(keyIdRaw, sizeof(uint8_t), KEY_ID_SIZE, fp);
|
|
fread(messageRaw, sizeof(uint8_t), kDataSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> keyId;
|
|
keyId.appendArray(keyIdRaw, KEY_ID_SIZE);
|
|
Vector<uint8_t> message;
|
|
message.appendArray(messageRaw, kDataSize);
|
|
Vector<uint8_t> signature;
|
|
|
|
{
|
|
InSequence calls;
|
|
|
|
EXPECT_CALL(crypto, selectKey(4, _, KEY_ID_SIZE))
|
|
.With(Args<1, 2>(ElementsAreArray(keyIdRaw, KEY_ID_SIZE)))
|
|
.Times(1);
|
|
|
|
EXPECT_CALL(crypto, sign(4, _, kDataSize, OEMCrypto_HMAC_SHA256, _,
|
|
Pointee(0)))
|
|
.With(Args<1, 2>(ElementsAreArray(messageRaw, kDataSize)))
|
|
.WillOnce(DoAll(SetArgPointee<5>(64),
|
|
Return(OEMCrypto_ERROR_SHORT_BUFFER)));
|
|
|
|
EXPECT_CALL(crypto, sign(4, _, kDataSize, OEMCrypto_HMAC_SHA256, _,
|
|
Pointee(64)))
|
|
.With(Args<1, 2>(ElementsAreArray(messageRaw, kDataSize)))
|
|
.Times(1);
|
|
}
|
|
|
|
// Provide expected behavior to support session creation
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.setMacAlgorithm(sessionId, String8("HmacSHA256"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.sign(sessionId, keyId, message, signature);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CallsGenericVerify) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
static const size_t kDataSize = 256;
|
|
static const size_t kSignatureSize = 16;
|
|
uint8_t keyIdRaw[KEY_ID_SIZE];
|
|
uint8_t messageRaw[kDataSize];
|
|
uint8_t signatureRaw[kSignatureSize];
|
|
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(keyIdRaw, sizeof(uint8_t), KEY_ID_SIZE, fp);
|
|
fread(messageRaw, sizeof(uint8_t), kDataSize, fp);
|
|
fread(signatureRaw, sizeof(uint8_t), kSignatureSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> keyId;
|
|
keyId.appendArray(keyIdRaw, KEY_ID_SIZE);
|
|
Vector<uint8_t> message;
|
|
message.appendArray(messageRaw, kDataSize);
|
|
Vector<uint8_t> signature;
|
|
signature.appendArray(signatureRaw, kSignatureSize);
|
|
bool match;
|
|
|
|
{
|
|
InSequence calls;
|
|
|
|
EXPECT_CALL(crypto, selectKey(4, _, KEY_ID_SIZE))
|
|
.With(Args<1, 2>(ElementsAreArray(keyIdRaw, KEY_ID_SIZE)))
|
|
.Times(1);
|
|
|
|
EXPECT_CALL(crypto, verify(4, _, kDataSize, OEMCrypto_HMAC_SHA256, _,
|
|
kSignatureSize))
|
|
.With(AllOf(Args<1, 2>(ElementsAreArray(messageRaw, kDataSize)),
|
|
Args<4, 5>(ElementsAreArray(signatureRaw, kSignatureSize))))
|
|
.WillOnce(Return(OEMCrypto_SUCCESS));
|
|
|
|
EXPECT_CALL(crypto, selectKey(4, _, KEY_ID_SIZE))
|
|
.With(Args<1, 2>(ElementsAreArray(keyIdRaw, KEY_ID_SIZE)))
|
|
.Times(1);
|
|
|
|
EXPECT_CALL(crypto, verify(4, _, kDataSize, OEMCrypto_HMAC_SHA256, _,
|
|
kSignatureSize))
|
|
.With(AllOf(Args<1, 2>(ElementsAreArray(messageRaw, kDataSize)),
|
|
Args<4, 5>(ElementsAreArray(signatureRaw, kSignatureSize))))
|
|
.WillOnce(Return(OEMCrypto_ERROR_SIGNATURE_FAILURE));
|
|
}
|
|
|
|
// Provide expected behavior to support session creation
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.setMacAlgorithm(sessionId, String8("HmacSHA256"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.verify(sessionId, keyId, message, signature, match);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_TRUE(match);
|
|
|
|
res = plugin.verify(sessionId, keyId, message, signature, match);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_FALSE(match);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, RegistersForEvents) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
// Provide expected behavior to support session creation
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, &plugin, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
|
|
{
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
uint8_t sessionIdRaw1[kSessionIdSize];
|
|
uint8_t sessionIdRaw2[kSessionIdSize];
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(sessionIdRaw1, sizeof(uint8_t), kSessionIdSize, fp);
|
|
fread(sessionIdRaw2, sizeof(uint8_t), kSessionIdSize, fp);
|
|
fclose(fp);
|
|
|
|
CdmSessionId cdmSessionId1(sessionIdRaw1, sessionIdRaw1 + kSessionIdSize);
|
|
CdmSessionId cdmSessionId2(sessionIdRaw2, sessionIdRaw2 + kSessionIdSize);
|
|
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.WillOnce(DoAll(SetArgPointee<3>(cdmSessionId1),
|
|
Return(wvcdm::NO_ERROR)))
|
|
.WillOnce(DoAll(SetArgPointee<3>(cdmSessionId2),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId1, _))
|
|
.WillOnce(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId2, _))
|
|
.WillOnce(Invoke(setSessionIdOnMap<5>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(OK, res);
|
|
}
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, MarshalsEvents) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
sp<StrictMock<MockDrmPluginListener> > listener =
|
|
new StrictMock<MockDrmPluginListener>();
|
|
|
|
const int64_t kExpiryTimeInSeconds = 123456789012LL;
|
|
const char kKeyId1[] = "Testing Key1 Id ";
|
|
const char kKeyId2[] = "Testing Key2 Id ";
|
|
const char kKeyId3[] = "Testing Key3 Id ";
|
|
const char kKeyId4[] = "Testing Key4 Id ";
|
|
|
|
{
|
|
InSequence calls;
|
|
|
|
EXPECT_CALL(*listener,
|
|
sendKeysChange(
|
|
Pointee(ElementsAreArray(sessionIdRaw, kSessionIdSize)),
|
|
Pointee(UnorderedElementsAre(AllOf(
|
|
Field(&DrmPlugin::KeyStatus::mKeyId,
|
|
ElementsAreArray(kKeyId1, sizeof(kKeyId1) - 1)),
|
|
Field(&DrmPlugin::KeyStatus::mType,
|
|
DrmPlugin::kKeyStatusType_Expired)))),
|
|
false));
|
|
EXPECT_CALL(
|
|
*listener,
|
|
sendEvent(DrmPlugin::kDrmPluginEventKeyExpired, 0,
|
|
Pointee(ElementsAreArray(sessionIdRaw, kSessionIdSize)),
|
|
NULL));
|
|
EXPECT_CALL(
|
|
*listener,
|
|
sendEvent(DrmPlugin::kDrmPluginEventKeyNeeded, 0,
|
|
Pointee(ElementsAreArray(sessionIdRaw, kSessionIdSize)),
|
|
NULL));
|
|
EXPECT_CALL(*listener,
|
|
sendExpirationUpdate(
|
|
Pointee(ElementsAreArray(sessionIdRaw, kSessionIdSize)),
|
|
kExpiryTimeInSeconds * 1000));
|
|
EXPECT_CALL(
|
|
*listener,
|
|
sendKeysChange(
|
|
Pointee(ElementsAreArray(sessionIdRaw, kSessionIdSize)),
|
|
Pointee(UnorderedElementsAre(
|
|
AllOf(Field(&DrmPlugin::KeyStatus::mKeyId,
|
|
ElementsAreArray(kKeyId1, sizeof(kKeyId1) - 1)),
|
|
Field(&DrmPlugin::KeyStatus::mType,
|
|
DrmPlugin::kKeyStatusType_Usable)),
|
|
AllOf(Field(&DrmPlugin::KeyStatus::mKeyId,
|
|
ElementsAreArray(kKeyId2, sizeof(kKeyId2) - 1)),
|
|
Field(&DrmPlugin::KeyStatus::mType,
|
|
DrmPlugin::kKeyStatusType_OutputNotAllowed)),
|
|
AllOf(Field(&DrmPlugin::KeyStatus::mKeyId,
|
|
ElementsAreArray(kKeyId3, sizeof(kKeyId3) - 1)),
|
|
Field(&DrmPlugin::KeyStatus::mType,
|
|
DrmPlugin::kKeyStatusType_InternalError)),
|
|
AllOf(Field(&DrmPlugin::KeyStatus::mKeyId,
|
|
ElementsAreArray(kKeyId4, sizeof(kKeyId4) - 1)),
|
|
Field(&DrmPlugin::KeyStatus::mType,
|
|
DrmPlugin::kKeyStatusType_StatusPending)))),
|
|
true));
|
|
}
|
|
|
|
status_t res = plugin.setListener(listener);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
CdmKeyStatusMap cdmKeysStatus;
|
|
cdmKeysStatus[kKeyId1] = kKeyStatusExpired;
|
|
plugin.OnSessionKeysChange(cdmSessionId, cdmKeysStatus, false);
|
|
|
|
plugin.OnSessionRenewalNeeded(cdmSessionId);
|
|
plugin.OnExpirationUpdate(cdmSessionId, kExpiryTimeInSeconds);
|
|
|
|
cdmKeysStatus[kKeyId1] = kKeyStatusUsable;
|
|
cdmKeysStatus[kKeyId2] = kKeyStatusOutputNotAllowed;
|
|
cdmKeysStatus[kKeyId3] = kKeyStatusInternalError;
|
|
cdmKeysStatus[kKeyId4] = kKeyStatusPending;
|
|
plugin.OnSessionKeysChange(cdmSessionId, cdmKeysStatus, true);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, GeneratesProvisioningNeededEvent) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
sp<StrictMock<MockDrmPluginListener> > listener =
|
|
new StrictMock<MockDrmPluginListener>();
|
|
|
|
EXPECT_CALL(*listener, sendEvent(DrmPlugin::kDrmPluginEventProvisionRequired, 0,
|
|
Pointee(ElementsAreArray(sessionIdRaw,
|
|
kSessionIdSize)),
|
|
NULL))
|
|
.Times(1);
|
|
|
|
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _, _))
|
|
.Times(AtLeast(1))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
Return(wvcdm::NEED_PROVISIONING)));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
|
|
status_t res = plugin.setListener(listener);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
res = plugin.openSession(sessionId);
|
|
ASSERT_EQ(ERROR_DRM_NOT_PROVISIONED, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
const CdmClientPropertySet* propertySet = NULL;
|
|
|
|
// Provide expected mock behavior
|
|
{
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
}
|
|
|
|
plugin.openSession(sessionId);
|
|
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_STREQ("", propertySet->security_level().c_str());
|
|
EXPECT_FALSE(propertySet->use_privacy_mode());
|
|
EXPECT_EQ(0u, propertySet->service_certificate().size());
|
|
EXPECT_FALSE(propertySet->is_session_sharing_enabled());
|
|
EXPECT_EQ(0u, propertySet->session_sharing_id());
|
|
EXPECT_STREQ("", propertySet->app_id().c_str());
|
|
}
|
|
TEST_F(WVDrmPluginTest, CanSetAppId) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
const CdmClientPropertySet* propertySet = NULL;
|
|
|
|
// Provide expected mock behavior
|
|
{
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
}
|
|
|
|
status_t res;
|
|
|
|
// Test setting an empty string
|
|
res = plugin.setPropertyString(String8("appId"), String8(""));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
// Test setting an application id before a session is opened.
|
|
const String8 kAppId("com.unittest.mock.app.id");
|
|
res = plugin.setPropertyString(String8("appId"), kAppId);
|
|
ASSERT_EQ(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
|
|
// Verify application id is set correctly.
|
|
EXPECT_STREQ(kAppId, propertySet->app_id().c_str());
|
|
|
|
// Test setting application id while session is opened, this should fail.
|
|
res = plugin.setPropertyString(String8("appId"), kAppId);
|
|
ASSERT_EQ(kErrorSessionIsOpen, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CanSetSecurityLevel) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
const CdmClientPropertySet* propertySet = NULL;
|
|
|
|
CdmQueryMap l1Map;
|
|
l1Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
|
|
|
|
CdmQueryMap l3Map;
|
|
l3Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
|
|
|
|
EXPECT_CALL(cdm, QueryStatus(_))
|
|
.WillOnce(DoAll(SetArgPointee<0>(l3Map),
|
|
Return(wvcdm::NO_ERROR)))
|
|
.WillOnce(DoAll(SetArgPointee<0>(l1Map),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected mock behavior
|
|
{
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
}
|
|
|
|
status_t res;
|
|
|
|
// Test forcing L3
|
|
res = plugin.setPropertyString(String8("securityLevel"), String8("L3"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_STREQ("L3", propertySet->security_level().c_str());
|
|
plugin.closeSession(sessionId);
|
|
|
|
// Test returning to L1 on an L3 device (Should Fail)
|
|
res = plugin.setPropertyString(String8("securityLevel"), String8("L1"));
|
|
ASSERT_NE(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_STREQ("L3", propertySet->security_level().c_str());
|
|
plugin.closeSession(sessionId);
|
|
|
|
// Test returning to L1 on an L1 device
|
|
res = plugin.setPropertyString(String8("securityLevel"), String8("L1"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_STREQ("", propertySet->security_level().c_str());
|
|
plugin.closeSession(sessionId);
|
|
|
|
// Test un-forcing a level (first forcing to L3 so we have something to reset)
|
|
res = plugin.setPropertyString(String8("securityLevel"), String8("L3"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_STREQ("L3", propertySet->security_level().c_str());
|
|
plugin.closeSession(sessionId);
|
|
|
|
res = plugin.setPropertyString(String8("securityLevel"), String8(""));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_STREQ("", propertySet->security_level().c_str());
|
|
plugin.closeSession(sessionId);
|
|
|
|
// Test nonsense (Should Fail)
|
|
res = plugin.setPropertyString(String8("securityLevel"), String8("nonsense"));
|
|
ASSERT_NE(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_STREQ("", propertySet->security_level().c_str());
|
|
plugin.closeSession(sessionId);
|
|
|
|
// Test attempting to force a level with a session open (Should Fail)
|
|
plugin.openSession(sessionId);
|
|
res = plugin.setPropertyString(String8("securityLevel"), String8("L3"));
|
|
ASSERT_NE(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CanSetPrivacyMode) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
const CdmClientPropertySet* propertySet = NULL;
|
|
|
|
// Provide expected mock behavior
|
|
{
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
}
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
|
|
status_t res;
|
|
|
|
// Test turning on privacy mode
|
|
res = plugin.setPropertyString(String8("privacyMode"), String8("enable"));
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_TRUE(propertySet->use_privacy_mode());
|
|
|
|
// Test turning off privacy mode
|
|
res = plugin.setPropertyString(String8("privacyMode"), String8("disable"));
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_FALSE(propertySet->use_privacy_mode());
|
|
|
|
// Test nonsense (Should Fail)
|
|
res = plugin.setPropertyString(String8("privacyMode"), String8("nonsense"));
|
|
ASSERT_NE(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CanSetServiceCertificate) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
const CdmClientPropertySet* propertySet = NULL;
|
|
|
|
static const size_t kPrivacyCertSize = 256;
|
|
uint8_t privacyCertRaw[kPrivacyCertSize];
|
|
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(privacyCertRaw, sizeof(uint8_t), kPrivacyCertSize, fp);
|
|
fclose(fp);
|
|
|
|
Vector<uint8_t> privacyCert;
|
|
privacyCert.appendArray(privacyCertRaw, kPrivacyCertSize);
|
|
Vector<uint8_t> emptyVector;
|
|
|
|
// Provide expected mock behavior
|
|
{
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
}
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
|
|
status_t res;
|
|
|
|
// Test setting a certificate
|
|
res = plugin.setPropertyByteArray(String8("serviceCertificate"), privacyCert);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_THAT(propertySet->service_certificate(),
|
|
ElementsAreArray(privacyCertRaw, kPrivacyCertSize));
|
|
|
|
// Test clearing a certificate
|
|
res = plugin.setPropertyByteArray(String8("serviceCertificate"), emptyVector);
|
|
ASSERT_EQ(OK, res);
|
|
EXPECT_EQ(0u, propertySet->service_certificate().size());
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, CanSetSessionSharing) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
const CdmClientPropertySet* propertySet = NULL;
|
|
|
|
// Provide expected mock behavior
|
|
{
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
}
|
|
|
|
status_t res;
|
|
|
|
// Test turning on session sharing
|
|
res = plugin.setPropertyString(String8("sessionSharing"), String8("enable"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_TRUE(propertySet->is_session_sharing_enabled());
|
|
plugin.closeSession(sessionId);
|
|
|
|
// Test turning off session sharing
|
|
res = plugin.setPropertyString(String8("sessionSharing"), String8("disable"));
|
|
ASSERT_EQ(OK, res);
|
|
|
|
plugin.openSession(sessionId);
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
EXPECT_FALSE(propertySet->is_session_sharing_enabled());
|
|
plugin.closeSession(sessionId);
|
|
|
|
// Test nonsense (Should Fail)
|
|
res = plugin.setPropertyString(String8("sessionSharing"), String8("nonsense"));
|
|
ASSERT_NE(OK, res);
|
|
|
|
// Test changing sharing with a session open (Should Fail)
|
|
plugin.openSession(sessionId);
|
|
res = plugin.setPropertyString(String8("sessionSharing"), String8("enable"));
|
|
ASSERT_NE(OK, res);
|
|
}
|
|
|
|
TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) {
|
|
StrictMock<MockCDM> cdm;
|
|
StrictMock<MockCrypto> crypto;
|
|
WVDrmPlugin plugin(&cdm, &crypto);
|
|
|
|
CdmClientPropertySet* propertySet = NULL;
|
|
|
|
uint32_t sharingId;
|
|
FILE* fp = fopen("/dev/urandom", "r");
|
|
fread(&sharingId, sizeof(uint32_t), 1, fp);
|
|
fclose(fp);
|
|
|
|
// Provide expected mock behavior
|
|
{
|
|
// Provide expected behavior in response to OpenSession and store the
|
|
// property set
|
|
EXPECT_CALL(cdm, OpenSession(_, _, _, _))
|
|
.WillRepeatedly(DoAll(SetArgPointee<3>(cdmSessionId),
|
|
SaveArg<1>(&propertySet),
|
|
Return(wvcdm::NO_ERROR)));
|
|
|
|
// Provide expected behavior when plugin requests session control info
|
|
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
|
|
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
|
|
|
EXPECT_CALL(cdm, CloseSession(_))
|
|
.Times(AtLeast(0));
|
|
}
|
|
|
|
plugin.openSession(sessionId);
|
|
|
|
ASSERT_THAT(propertySet, NotNull());
|
|
propertySet->set_session_sharing_id(sharingId);
|
|
EXPECT_EQ(sharingId, propertySet->session_sharing_id());
|
|
}
|