(This is a merge of go/wvgerrit/23182)

This patch adds the framework for Stable Per-Origin Identifiers to the
CDM. Calculating SPOIDs will be done on the client-side, and they are
sent as part of the provisioning request. SPOIDs are also available to
the app as the Device Unique ID, replacing the previous method of
returning the actual Device Unique ID from the keybox / OEM certificate.

Different SPOIDs must use separate storage, just as different origins
already do. Support for this has been added to the Android adapter to the
CDM Core. However, the code in the Android glue layer that would drive
this behavior will be checked in in a separate change. As such, all
Android devices will continue using the legacy behavior even after this
patch goes in, until the glue layer code can be updated.

Bug: 27101531
Test: CE CDM Unit Tests
Test: Linux Jenkins Unit Tests
Test: Android Unit Tests (with and without SPOIDs forced on)
Test: Android GTS Tests
Change-Id: Ia0caf890381cbcb97504d08b19aeab8b29bd07ae
This commit is contained in:
John W. Bruce
2017-01-25 14:35:50 -08:00
parent 5249221e3a
commit c85351682f
18 changed files with 511 additions and 290 deletions

View File

@@ -9,6 +9,7 @@
#include <map>
#include "cdm_client_property_set.h"
#include "cdm_identifier.h"
#include "media/drm/DrmAPI.h"
#include "media/stagefright/foundation/ABase.h"
#include "media/stagefright/foundation/AString.h"
@@ -31,6 +32,7 @@ using android::status_t;
using android::String8;
using android::Vector;
using std::map;
using wvcdm::CdmIdentifier;
using wvcdm::CdmKeyStatusMap;
using wvcdm::CdmSessionId;
using wvcdm::CdmResponseType;
@@ -263,9 +265,10 @@ class WVDrmPlugin : public android::DrmPlugin,
android::sp<wvcdm::WvContentDecryptionModule> const mCDM;
WVGenericCryptoInterface* mCrypto;
std::string mOrigin;
map<CdmSessionId, CryptoSession> mCryptoSessions;
CdmIdentifier mCdmIdentifier;
status_t queryProperty(const std::string& property,
std::string& stringValue) const;
@@ -289,9 +292,7 @@ class WVDrmPlugin : public android::DrmPlugin,
bool initDataResemblesPSSH(const Vector<uint8_t>& initData);
status_t unprovision(const std::string& origin);
const char* determineOrigin() const;
status_t unprovision(const CdmIdentifier& identifier);
};
} // namespace wvdrm

View File

@@ -76,7 +76,10 @@ DrmPlugin::KeyStatusType ConvertFromCdmKeyStatus(CdmKeyStatus keyStatus) {
WVDrmPlugin::WVDrmPlugin(const sp<WvContentDecryptionModule>& cdm,
WVGenericCryptoInterface* crypto)
: mCDM(cdm), mCrypto(crypto), mOrigin(), mCryptoSessions() {}
: mCDM(cdm),
mCrypto(crypto),
mCryptoSessions(),
mCdmIdentifier(kDefaultCdmIdentifier) {}
WVDrmPlugin::~WVDrmPlugin() {
typedef map<CdmSessionId, CryptoSession>::iterator mapIterator;
@@ -94,7 +97,7 @@ WVDrmPlugin::~WVDrmPlugin() {
status_t WVDrmPlugin::openSession(Vector<uint8_t>& sessionId) {
CdmSessionId cdmSessionId;
CdmResponseType res =
mCDM->OpenSession("com.widevine", &mPropertySet, determineOrigin(), this,
mCDM->OpenSession("com.widevine", &mPropertySet, mCdmIdentifier, this,
&cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
@@ -222,7 +225,7 @@ status_t WVDrmPlugin::getKeyRequest(
CdmKeyRequest keyRequest;
CdmResponseType res = mCDM->GenerateKeyRequest(
cdmSessionId, cdmKeySetId, cdmInitDataType, processedInitData,
cdmLicenseType, cdmParameters, &mPropertySet, determineOrigin(),
cdmLicenseType, cdmParameters, &mPropertySet, mCdmIdentifier,
&keyRequest);
*keyRequestType = ConvertFromCdmKeyRequestType(keyRequest.type);
@@ -346,7 +349,7 @@ status_t WVDrmPlugin::getProvisionRequest(const String8& cert_type,
CdmResponseType res = mCDM->GetProvisioningRequest(cdmCertType,
cdmCertAuthority,
determineOrigin(),
mCdmIdentifier,
&cdmProvisionRequest,
&cdmDefaultUrl);
@@ -365,13 +368,14 @@ status_t WVDrmPlugin::provideProvisionResponse(
Vector<uint8_t>& wrapped_key) {
CdmProvisioningResponse cdmResponse(response.begin(), response.end());
if (cdmResponse == kSpecialUnprovisionResponse) {
const std::string origin = determineOrigin();
if (origin == EMPTY_ORIGIN) return kErrorNoOriginSpecified;
return unprovision(origin);
if (mCdmIdentifier == kDefaultCdmIdentifier) {
return kErrorNoOriginSpecified;
}
return unprovision(mCdmIdentifier);
} else {
string cdmCertificate;
string cdmWrappedKey;
CdmResponseType res = mCDM->HandleProvisioningResponse(determineOrigin(),
CdmResponseType res = mCDM->HandleProvisioningResponse(mCdmIdentifier,
cdmResponse,
&cdmCertificate,
&cdmWrappedKey);
@@ -385,7 +389,7 @@ status_t WVDrmPlugin::provideProvisionResponse(
}
status_t WVDrmPlugin::unprovisionDevice() {
return unprovision(EMPTY_ORIGIN);
return unprovision(kDefaultCdmIdentifier);
}
status_t WVDrmPlugin::getSecureStop(const Vector<uint8_t>& ssid,
@@ -483,7 +487,7 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
} else if (name == "appId") {
value = mPropertySet.app_id().c_str();
} else if (name == "origin") {
value = mOrigin.c_str();
value = mCdmIdentifier.origin.c_str();
} else {
ALOGE("App requested unknown string property %s", name.string());
return android::ERROR_DRM_CANNOT_HANDLE;
@@ -570,11 +574,11 @@ status_t WVDrmPlugin::setPropertyString(const String8& name,
return kErrorSessionIsOpen;
}
} else if (name == "origin") {
if (mCryptoSessions.size() == 0) {
mOrigin = value.string();
} else {
if (mCryptoSessions.size() != 0) {
ALOGE("App tried to set the origin while sessions are opened.");
return kErrorSessionIsOpen;
} else {
mCdmIdentifier.origin = value.string();
}
} else {
ALOGE("App set unknown string property %s", name.string());
@@ -977,9 +981,9 @@ bool WVDrmPlugin::initDataResemblesPSSH(const Vector<uint8_t>& initData) {
return id == kPsshTag;
}
status_t WVDrmPlugin::unprovision(const std::string& origin) {
CdmResponseType res1 = mCDM->Unprovision(kSecurityLevelL1, origin);
CdmResponseType res3 = mCDM->Unprovision(kSecurityLevelL3, origin);
status_t WVDrmPlugin::unprovision(const CdmIdentifier& identifier) {
CdmResponseType res1 = mCDM->Unprovision(kSecurityLevelL1, identifier);
CdmResponseType res3 = mCDM->Unprovision(kSecurityLevelL3, identifier);
if (!isCdmResponseTypeSuccess(res1))
{
return mapCdmResponseType(res1);
@@ -990,8 +994,4 @@ status_t WVDrmPlugin::unprovision(const std::string& origin) {
}
}
const char* WVDrmPlugin::determineOrigin() const {
return mOrigin.empty() ? EMPTY_ORIGIN : mOrigin.c_str();
}
} // namespace wvdrm

View File

@@ -38,7 +38,7 @@ class MockCDM : public WvContentDecryptionModule {
public:
MOCK_METHOD5(OpenSession, CdmResponseType(const CdmKeySystem&,
CdmClientPropertySet*,
const std::string&,
const CdmIdentifier&,
WvCdmEventListener*,
CdmSessionId*));
@@ -48,7 +48,7 @@ class MockCDM : public WvContentDecryptionModule {
CdmResponseType(const CdmSessionId&, const CdmKeySetId&,
const std::string&, const CdmInitData&,
const CdmLicenseType, CdmAppParameterMap&,
CdmClientPropertySet*, const std::string&,
CdmClientPropertySet*, const CdmIdentifier&,
CdmKeyRequest*));
MOCK_METHOD3(AddKey, CdmResponseType(const CdmSessionId&,
@@ -71,16 +71,16 @@ class MockCDM : public WvContentDecryptionModule {
MOCK_METHOD5(GetProvisioningRequest, CdmResponseType(CdmCertificateType,
const std::string&,
const std::string&,
const CdmIdentifier&,
CdmProvisioningRequest*,
std::string*));
MOCK_METHOD4(HandleProvisioningResponse,
CdmResponseType(const std::string&, CdmProvisioningResponse&,
CdmResponseType(const CdmIdentifier&, CdmProvisioningResponse&,
std::string*, std::string*));
MOCK_METHOD2(Unprovision, CdmResponseType(CdmSecurityLevel,
const std::string&));
const CdmIdentifier&));
MOCK_METHOD2(GetUsageInfo, CdmResponseType(const std::string&,
CdmUsageInfo*));
@@ -142,6 +142,10 @@ CdmResponseType setSessionIdOnMap(Unused, CdmQueryMap* map) {
return wvcdm::NO_ERROR;
}
MATCHER_P(HasOrigin, origin, "") {
return arg.origin == origin;
}
class WVDrmPluginTest : public Test {
protected:
static const uint32_t kSessionIdSize = 16;
@@ -192,7 +196,7 @@ TEST_F(WVDrmPluginTest, OpensSessions) {
WVDrmPlugin plugin(cdm.get(), &crypto);
EXPECT_CALL(*cdm,
OpenSession(StrEq("com.widevine"), _, StrEq(EMPTY_ORIGIN), _, _))
OpenSession(StrEq("com.widevine"), _, HasOrigin(EMPTY_ORIGIN), _, _))
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
@@ -338,20 +342,23 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) {
};
EXPECT_CALL(*cdm, GenerateKeyRequest(cdmSessionId, "", mimeType, initData,
kLicenseTypeOffline, cdmParameters, _,
_, _))
kLicenseTypeOffline, cdmParameters,
NotNull(), HasOrigin(EMPTY_ORIGIN),
_))
.WillOnce(DoAll(SetArgPointee<8>(initialRequest),
Return(wvcdm::KEY_MESSAGE)));
EXPECT_CALL(*cdm, GenerateKeyRequest(cdmSessionId, "", mimeType, initData,
kLicenseTypeStreaming, cdmParameters,
_, _, _))
NotNull(), HasOrigin(EMPTY_ORIGIN),
_))
.WillOnce(DoAll(SetArgPointee<8>(renewalRequest),
Return(wvcdm::KEY_MESSAGE)));
EXPECT_CALL(*cdm, GenerateKeyRequest("", cdmKeySetId, mimeType, initData,
kLicenseTypeRelease, cdmParameters,
NotNull(), StrEq(EMPTY_ORIGIN), _))
NotNull(), HasOrigin(EMPTY_ORIGIN),
_))
.WillOnce(DoAll(SetArgPointee<8>(releaseRequest),
Return(wvcdm::KEY_MESSAGE)));
@@ -576,7 +583,7 @@ TEST_F(WVDrmPluginTest, GetsProvisioningRequests) {
static const char* kDefaultUrl = "http://google.com/";
EXPECT_CALL(*cdm, GetProvisioningRequest(kCertificateWidevine, IsEmpty(),
EMPTY_ORIGIN, _, _))
HasOrigin(EMPTY_ORIGIN), _, _))
.WillOnce(DoAll(SetArgPointee<3>(cdmRequest),
SetArgPointee<4>(kDefaultUrl),
Return(wvcdm::NO_ERROR)));
@@ -606,10 +613,10 @@ TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
Vector<uint8_t> response;
response.appendArray(responseRaw, kResponseSize);
EXPECT_CALL(*cdm, HandleProvisioningResponse(EMPTY_ORIGIN,
ElementsAreArray(responseRaw,
kResponseSize),
_, _))
EXPECT_CALL(*cdm, HandleProvisioningResponse(HasOrigin(EMPTY_ORIGIN),
ElementsAreArray(responseRaw,
kResponseSize),
_, _))
.Times(1);
Vector<uint8_t> cert;
@@ -625,9 +632,9 @@ TEST_F(WVDrmPluginTest, UnprovisionsDevice) {
StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(cdm.get(), &crypto);
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL1, EMPTY_ORIGIN))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL1, HasOrigin(EMPTY_ORIGIN)))
.Times(1);
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, EMPTY_ORIGIN))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, HasOrigin(EMPTY_ORIGIN)))
.Times(1);
status_t res = plugin.unprovisionDevice();
@@ -641,11 +648,11 @@ TEST_F(WVDrmPluginTest, MuxesUnprovisioningErrors) {
// 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, EMPTY_ORIGIN))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL1, HasOrigin(EMPTY_ORIGIN)))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR))
.WillOnce(Return(wvcdm::NO_ERROR))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR));
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, EMPTY_ORIGIN))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, HasOrigin(EMPTY_ORIGIN)))
.WillOnce(Return(wvcdm::NO_ERROR))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR));
@@ -668,9 +675,9 @@ TEST_F(WVDrmPluginTest, UnprovisionsOrigin) {
Vector<uint8_t> specialResponse;
specialResponse.appendArray(kUnprovisionResponse, kUnprovisionResponseSize);
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL1, StrEq(kOrigin.string())))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL1, HasOrigin(kOrigin.string())))
.Times(1);
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, StrEq(kOrigin.string())))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, HasOrigin(kOrigin.string())))
.Times(1);
status_t res = plugin.setPropertyString(String8("origin"), kOrigin);
@@ -708,11 +715,11 @@ TEST_F(WVDrmPluginTest, MuxesOriginUnprovisioningErrors) {
// 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, StrEq(kOrigin.string())))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL1, HasOrigin(kOrigin.string())))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR))
.WillOnce(Return(wvcdm::NO_ERROR))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR));
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, StrEq(kOrigin.string())))
EXPECT_CALL(*cdm, Unprovision(kSecurityLevelL3, HasOrigin(kOrigin.string())))
.WillOnce(Return(wvcdm::NO_ERROR))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR))
.WillOnce(Return(wvcdm::UNKNOWN_ERROR));
@@ -1544,7 +1551,7 @@ TEST_P(WVDrmPluginOriginTest, CanSetOrigin) {
}
// Note which mock calls we expect
EXPECT_CALL(*cdm, OpenSession(_, _, StrEq(params.expectedOrigin), _, _))
EXPECT_CALL(*cdm, OpenSession(_, _, HasOrigin(params.expectedOrigin), _, _))
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
Return(wvcdm::NO_ERROR)));