Allow Setting of Session ID

Implements the optional setMediaDrmSession() method. To enble this,
support was added to the core to report if a session ID is valid.

As a consequence of this, in the tests for the CryptoPlugin,
construction of the plugin must be deferred until all gMock
expectations are set, as construction now calls into the CDM core.

This is a merge of two changes from the Widevine CDM repo:
http://go/wvgerrit/14083
  Allow Setting of Session ID
http://go/wvgerrit/14085
  Check If Session ID Is Valid When Changing CryptoPlugin IDs

Bug: 19570317
Change-Id: I7dbd777ce6efebd71fdb5e602663a0e35a48a9c4
This commit is contained in:
John "Juce" Bruce
2015-04-10 15:59:11 -07:00
parent 0540770280
commit c5f576585b
8 changed files with 167 additions and 15 deletions

View File

@@ -36,6 +36,7 @@ class CdmEngine {
WvCdmEventListener* event_listener,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual bool IsOpenSession(const CdmSessionId& session_id);
virtual CdmResponseType OpenKeySetSession(
const CdmKeySetId& key_set_id, const CdmClientPropertySet* property_set,

View File

@@ -164,6 +164,11 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
return sts;
}
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
return iter != sessions_.end();
}
CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const InitializationData& init_data, const CdmLicenseType license_type,

View File

@@ -57,6 +57,7 @@ class WvCdmEngineTest : public testing::Test {
}
ASSERT_EQ(NO_ERROR, status);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
ASSERT_TRUE(cdm_engine_.IsOpenSession(session_id_));
}
virtual void TearDown() { cdm_engine_.CloseSession(session_id_); }

View File

@@ -32,6 +32,7 @@ class WvContentDecryptionModule : public TimerHandler {
WvCdmEventListener* event_listener,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual bool IsOpenSession(const CdmSessionId& session_id);
// Construct a valid license request.
virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,

View File

@@ -58,6 +58,10 @@ CdmResponseType WvContentDecryptionModule::CloseSession(
return sts;
}
bool WvContentDecryptionModule::IsOpenSession(const CdmSessionId& session_id) {
return cdm_engine_->IsOpenSession(session_id);
}
CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const std::string& init_data_type, const CdmInitData& init_data,

View File

@@ -7,6 +7,8 @@
#include <stdint.h>
#include "utils/Vector.h"
#include "media/hardware/CryptoAPI.h"
#include "media/stagefright/foundation/ABase.h"
#include "media/stagefright/foundation/AString.h"
@@ -24,6 +26,9 @@ class WVCryptoPlugin : public android::CryptoPlugin {
virtual void notifyResolution(uint32_t width, uint32_t height);
virtual android::status_t setMediaDrmSession(
const android::Vector<uint8_t>& sessionId);
virtual ssize_t decrypt(bool secure, const uint8_t key[16],
const uint8_t iv[16], Mode mode, const void* srcPtr,
const SubSample* subSamples, size_t numSubSamples,
@@ -35,7 +40,7 @@ class WVCryptoPlugin : public android::CryptoPlugin {
wvcdm::WvContentDecryptionModule* const mCDM;
bool mTestMode;
const wvcdm::CdmSessionId mSessionId;
wvcdm::CdmSessionId mSessionId;
wvcdm::CdmSessionId configureTestMode(const void* data, size_t size);
static void incrementIV(uint64_t increaseBy, std::vector<uint8_t>* ivPtr);

View File

@@ -34,13 +34,18 @@ WVCryptoPlugin::WVCryptoPlugin(const void* data, size_t size,
mTestMode(false),
mSessionId(configureTestMode(data, size)) {}
wvcdm::CdmSessionId WVCryptoPlugin::configureTestMode(const void* data,
size_t size) {
wvcdm::CdmSessionId sessionId(static_cast<const char *>(data), size);
size_t index = sessionId.find("test_mode");
if (index != string::npos) {
CdmSessionId WVCryptoPlugin::configureTestMode(const void* data, size_t size) {
CdmSessionId sessionId;
if (data != NULL) {
sessionId.assign(static_cast<const char *>(data), size);
size_t index = sessionId.find("test_mode");
if (index != string::npos) {
sessionId = sessionId.substr(0, index);
mTestMode = true;
}
}
if (!mCDM->IsOpenSession(sessionId)) {
sessionId.clear();
}
return sessionId;
}
@@ -68,10 +73,20 @@ void WVCryptoPlugin::notifyResolution(uint32_t width, uint32_t height) {
mCDM->NotifyResolution(mSessionId, width, height);
}
// Returns negative values for error code and
// positive values for the size of decrypted data. In theory, the output size
// can be larger than the input size, but in practice this should never happen
// for AES-CTR.
status_t WVCryptoPlugin::setMediaDrmSession(const Vector<uint8_t>& sessionId) {
CdmSessionId cdmSessionId(reinterpret_cast<const char *>(sessionId.array()),
sessionId.size());
if (!mCDM->IsOpenSession(cdmSessionId)) {
return android::ERROR_DRM_SESSION_NOT_OPENED;
} else {
mSessionId = cdmSessionId;
return android::NO_ERROR;
}
}
// Returns negative values for error code and positive values for the size of
// decrypted data. In theory, the output size can be larger than the input
// size, but in practice this should never happen for AES-CTR.
ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE],
const uint8_t iv[KEY_IV_SIZE], Mode mode,
const void* srcPtr, const SubSample* subSamples,

View File

@@ -10,6 +10,8 @@
#include "gtest/gtest.h"
#include "media/stagefright/foundation/ABase.h"
#include "media/stagefright/foundation/AString.h"
#include "media/stagefright/MediaErrors.h"
#include "OEMCryptoCENC.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
@@ -24,6 +26,8 @@ using namespace wvdrm;
class MockCDM : public WvContentDecryptionModule {
public:
MOCK_METHOD1(IsOpenSession, bool(const CdmSessionId&));
MOCK_METHOD3(Decrypt, CdmResponseType(const CdmSessionId&, bool,
const CdmDecryptionParameters&));
@@ -48,20 +52,25 @@ class WVCryptoPluginTest : public Test {
TEST_F(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) {
StrictMock<MockCDM> cdm;
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
CdmQueryMap l1Map;
l1Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
CdmQueryMap l3Map;
l3Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
// Provide the expected behavior for IsOpenSession
EXPECT_CALL(cdm, IsOpenSession(_))
.WillRepeatedly(Return(true));
// Specify the expected calls to QuerySessionStatus
EXPECT_CALL(cdm, QuerySessionStatus(_, _))
.WillOnce(DoAll(SetArgPointee<1>(l1Map),
Return(wvcdm::NO_ERROR)))
.WillOnce(DoAll(SetArgPointee<1>(l3Map),
Return(wvcdm::NO_ERROR)));
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
EXPECT_TRUE(plugin.requiresSecureDecoderComponent("video/mp4")) <<
"WVCryptoPlugin incorrectly allows an insecure video decoder on L1";
EXPECT_FALSE(plugin.requiresSecureDecoderComponent("video/mp4")) <<
@@ -140,7 +149,6 @@ class CDPMatcherFactory {
TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) {
StrictMock<MockCDM> cdm;
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
uint8_t keyId[KEY_ID_SIZE];
uint8_t baseIv[KEY_IV_SIZE];
@@ -181,6 +189,11 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) {
CDPMatcherFactory ParamsAre = CDPMatcherFactory(false, keyId, out, kDataSize);
// Provide the expected behavior for IsOpenSession
EXPECT_CALL(cdm, IsOpenSession(_))
.WillRepeatedly(Return(true));
// Specify the expected calls to Decrypt
{
InSequence calls;
@@ -233,6 +246,7 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) {
.Times(1);
}
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
AString errorDetailMessage;
ssize_t res = plugin.decrypt(false, keyId, iv[0], CryptoPlugin::kMode_AES_CTR,
@@ -247,7 +261,6 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) {
TEST_F(WVCryptoPluginTest, CommunicatesSecureBufferRequest) {
StrictMock<MockCDM> cdm;
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
uint8_t keyId[KEY_ID_SIZE];
uint8_t iv[KEY_IV_SIZE];
@@ -268,6 +281,10 @@ TEST_F(WVCryptoPluginTest, CommunicatesSecureBufferRequest) {
subSamples[0].mNumBytesOfClearData = 16;
subSamples[0].mNumBytesOfEncryptedData = 16;
// Provide the expected behavior for IsOpenSession
EXPECT_CALL(cdm, IsOpenSession(_))
.WillRepeatedly(Return(true));
// Specify the expected calls to Decrypt
{
InSequence calls;
@@ -281,6 +298,7 @@ TEST_F(WVCryptoPluginTest, CommunicatesSecureBufferRequest) {
.Times(2);
}
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
AString errorDetailMessage;
ssize_t res = plugin.decrypt(false, keyId, iv, CryptoPlugin::kMode_AES_CTR,
@@ -301,7 +319,6 @@ TEST_F(WVCryptoPluginTest, CommunicatesSecureBufferRequest) {
TEST_F(WVCryptoPluginTest, SetsFlagsForMinimumSubsampleRuns) {
MockCDM cdm;
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
uint8_t keyId[KEY_ID_SIZE];
uint8_t iv[KEY_IV_SIZE];
@@ -330,6 +347,10 @@ TEST_F(WVCryptoPluginTest, SetsFlagsForMinimumSubsampleRuns) {
mixedSubSamples[0].mNumBytesOfClearData = 8;
mixedSubSamples[0].mNumBytesOfEncryptedData = 8;
// Provide the expected behavior for IsOpenSession
EXPECT_CALL(cdm, IsOpenSession(_))
.WillRepeatedly(Return(true));
// Specify the expected calls to Decrypt
{
InSequence calls;
@@ -350,6 +371,7 @@ TEST_F(WVCryptoPluginTest, SetsFlagsForMinimumSubsampleRuns) {
.Times(1);
}
WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm);
AString errorDetailMessage;
ssize_t res = plugin.decrypt(false, keyId, iv, CryptoPlugin::kMode_AES_CTR,
@@ -376,3 +398,101 @@ TEST_F(WVCryptoPluginTest, SetsFlagsForMinimumSubsampleRuns) {
EXPECT_EQ(0u, errorDetailMessage.size()) <<
"WVCryptoPlugin reported a detailed error message.";
}
TEST_F(WVCryptoPluginTest, AllowsSessionIdChanges) {
StrictMock<MockCDM> cdm;
uint8_t keyId[KEY_ID_SIZE];
uint8_t iv[KEY_IV_SIZE];
uint8_t sessionId2[kSessionIdSize];
static const size_t kDataSize = 32;
uint8_t in[kDataSize];
uint8_t out[kDataSize];
FILE* fp = fopen("/dev/urandom", "r");
fread(keyId, sizeof(uint8_t), KEY_ID_SIZE, fp);
fread(iv, sizeof(uint8_t), KEY_IV_SIZE, fp);
fread(sessionId2, sizeof(uint8_t), kSessionIdSize, fp);
fread(in, sizeof(uint8_t), kDataSize, fp);
fclose(fp);
static const uint32_t kSubSampleCount = 1;
CryptoPlugin::SubSample subSamples[kSubSampleCount];
memset(subSamples, 0, sizeof(subSamples));
subSamples[0].mNumBytesOfClearData = 16;
subSamples[0].mNumBytesOfEncryptedData = 16;
Vector<uint8_t> sessionIdVector;
sessionIdVector.appendArray(sessionId, kSessionIdSize);
Vector<uint8_t> sessionId2Vector;
sessionId2Vector.appendArray(sessionId2, kSessionIdSize);
// Provide the expected behavior for IsOpenSession
EXPECT_CALL(cdm, IsOpenSession(_))
.WillRepeatedly(Return(true));
// Specify the expected calls to Decrypt
{
InSequence calls;
EXPECT_CALL(cdm,
Decrypt(ElementsAreArray(sessionId, kSessionIdSize), _, _))
.Times(2);
EXPECT_CALL(cdm,
Decrypt(ElementsAreArray(sessionId2, kSessionIdSize), _, _))
.Times(2);
}
uint8_t blank[1]; // Some compilers will not accept 0.
WVCryptoPlugin plugin(blank, 0, &cdm);
AString errorDetailMessage;
ssize_t res;
res = plugin.setMediaDrmSession(sessionIdVector);
EXPECT_EQ(android::NO_ERROR, res);
res = plugin.decrypt(false, keyId, iv, CryptoPlugin::kMode_AES_CTR, in,
subSamples, kSubSampleCount, out, &errorDetailMessage);
EXPECT_GE(res, 0) <<
"WVCryptoPlugin returned an error";
EXPECT_EQ(0u, errorDetailMessage.size()) <<
"WVCryptoPlugin reported a detailed error message.";
res = plugin.setMediaDrmSession(sessionId2Vector);
EXPECT_EQ(android::NO_ERROR, res);
res = plugin.decrypt(false, keyId, iv, CryptoPlugin::kMode_AES_CTR, in,
subSamples, kSubSampleCount, out, &errorDetailMessage);
EXPECT_GE(res, 0) <<
"WVCryptoPlugin returned an error";
EXPECT_EQ(0u, errorDetailMessage.size()) <<
"WVCryptoPlugin reported a detailed error message.";
}
TEST_F(WVCryptoPluginTest, DisallowsUnopenedSessionIdChanges) {
StrictMock<MockCDM> cdm;
uint8_t blank[1]; // Some compilers will not accept 0.
Vector<uint8_t> sessionIdVector;
sessionIdVector.appendArray(sessionId, kSessionIdSize);
// Specify the expected calls to IsOpenSession
{
InSequence calls;
EXPECT_CALL(cdm, IsOpenSession(ElementsAreArray(blank, 0)))
.WillOnce(Return(false));
EXPECT_CALL(cdm, IsOpenSession(ElementsAreArray(sessionId, kSessionIdSize)))
.WillOnce(Return(false))
.WillOnce(Return(true));
}
WVCryptoPlugin plugin(blank, 0, &cdm);
ssize_t res;
res = plugin.setMediaDrmSession(sessionIdVector);
EXPECT_EQ(android::ERROR_DRM_SESSION_NOT_OPENED, res);
res = plugin.setMediaDrmSession(sessionIdVector);
EXPECT_EQ(android::NO_ERROR, res);
}