Files
android/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp
Rahul Frias 0419b55222 Merges to android Pi release (part: 1)
Below are a set of CLs being merged from the wv cdm repo to the android repo.

* Fix handling of OEM Cert public key.

  Author: Srujan Gaddam <srujzs@google.com>

  [ Merge of http://go/wvgerrit/27921 ]

  This is a potential fix for b/36656190. Set aside public
  key on first call to get the public key, and use it afterwards.
  This gets rid of extra calls to OEMCrypto_GetOEMPublicCertificate(),
  which has side-effect of staging the OEM private key.

  This also fixes a problem where the public cert string was
  not being trimmed to match the size returned by
  OEMCrypto_GetOEMPublicCertificate().

* Complete provisioning request/response for Provisioning 3.0

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27780 ]

  Fix bug on provisioning request path where GenerateDerivedKeys()
  was being called when preparing to generate the signature.

  Add message signature verification, and call correct OEMCrypto
  routine to rewrap the private key (OEMCrypto_RewrapDeviceRSAKey30).

* Implement Cdm::deleteAllUsageRecords()

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27780 ]

  Delete all usage records for current origin.  Removes usage
  records from file system and retains the PSTs.  The deletes
  any usage entries matching those PSTs held by OEMCrypto.

  BUG: 35319024

* Remove stringencoders library from third_party.

  Author: Jacob Trimble <modmaker@google.com>

  [ Merge of http://go/wvgerrit/27585 ]

  We have a fork of the stringencoders library that we use for base64
  encoding.  This reimplements base64 encoding to remove the extra
  dependency and to reduce the amount of code.

* Add Cdm::deleteUsageRecord() based on key_set_id.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27605 ]

  Delete specified usage record from file system usage info and
  from OEMCrypto.

  BUG: 35319024

* Modifiable OEMCrypto

  Author: Fred Gylys-Colwell <fredgc@google.com>

  [ Merge of http://go/wvgerrit/24729 ]

  This CL adds a new variant of the OEMCrypto mock code that adjusts its
  behavior based on a configuration file.  This is intended for
  testing.

  For example, a tester can set current_hdcp to 2 in the options.txt
  file, push it to the device, and verify that a license is granted for
  HDCP 2.0.  Then the tester can edit the value of current_hdcp to 1 and
  push the file to the device.  Playback should stop because the license
  is no longer valid.

  This variant uses a real level 1 liboemcrypto.so to push data to a
  secure buffer.  That means we can test playback for a license that
  requires secure buffers on an Android device with real secure buffers.

  BUG: 35141278
  BUG: 37353534

BUG: 71650075
Test: Not currently passing. Will be addressed in a subsequent
      commit in the chain.

Change-Id: I58443c510919e992bb455192e70373490a00e2b6
2018-01-16 19:21:54 -08:00

1450 lines
46 KiB
C++

//
// Copyright 2017 Google Inc. All Rights Reserved.
//
//#define LOG_NDEBUG 0
#define LOG_TAG "WVCdm"
#include <utils/Log.h>
#include <list>
#include "WVDrmPlugin.h"
#include "TypeConvert.h"
#include "mapErrors-inl.h"
#include "media/stagefright/MediaErrors.h"
#include "openssl/sha.h"
#include "wv_cdm_constants.h"
namespace {
static const char* const kResetSecurityLevel = "";
static const char* const kEnable = "enable";
static const char* const kDisable = "disable";
static const std::string kPsshTag = "pssh";
static const char* const kSpecialUnprovisionResponse = "unprovision";
} // namespace
namespace wvdrm {
namespace hardware {
namespace drm {
namespace V1_0 {
namespace widevine {
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::KeyRequestType;
using ::android::hardware::drm::V1_0::KeyStatusType;
using ::android::hardware::drm::V1_0::KeyType;
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_0::widevine::toHidlVec;
using ::android::hardware::drm::V1_0::widevine::toVector;
using ::android::hardware::Void;
using wvcdm::kDefaultCdmIdentifier;
using wvcdm::CdmAppParameterMap;
using wvcdm::CdmCertificateType;
using wvcdm::CdmInitData;
using wvcdm::CdmKeyStatus;
using wvcdm::CdmKeyRequest;
using wvcdm::CdmKeyRequestType;
using wvcdm::CdmKeyResponse;
using wvcdm::CdmKeySetId;
using wvcdm::CdmLicenseType;
using wvcdm::CdmProvisioningRequest;
using wvcdm::CdmProvisioningResponse;
using wvcdm::CdmQueryMap;
using wvcdm::CdmSecureStopId;
using wvcdm::CdmUsageInfo;
using wvcdm::CdmUsageInfoReleaseMessage;
using wvcdm::KeyId;
using wvcdm::SecurityLevel;
namespace {
std::vector<uint8_t> StrToVector(const std::string& str) {
std::vector<uint8_t> vec(str.begin(), str.end());
return vec;
}
KeyRequestType ConvertFromCdmKeyRequestType(
CdmKeyRequestType keyRequestType) {
switch (keyRequestType) {
case wvcdm::kKeyRequestTypeInitial:
return KeyRequestType::INITIAL;
case wvcdm::kKeyRequestTypeRenewal:
return KeyRequestType::RENEWAL;
case wvcdm::kKeyRequestTypeRelease:
return KeyRequestType::RELEASE;
default:
return KeyRequestType::UNKNOWN;
}
}
KeyStatusType ConvertFromCdmKeyStatus(CdmKeyStatus keyStatus) {
switch (keyStatus) {
case wvcdm::kKeyStatusUsable:
return KeyStatusType::USABLE;
case wvcdm::kKeyStatusExpired:
return KeyStatusType::EXPIRED;
case wvcdm::kKeyStatusOutputNotAllowed:
return KeyStatusType::OUTPUTNOTALLOWED;
case wvcdm::kKeyStatusPending:
return KeyStatusType::STATUSPENDING;
case wvcdm::kKeyStatusInternalError:
default:
return KeyStatusType::INTERNALERROR;
}
}
} // namespace
WVDrmPlugin::WVDrmPlugin(const sp<WvContentDecryptionModule>& cdm,
const std::string& appPackageName,
WVGenericCryptoInterface* crypto,
bool useSpoid)
: mCdmIdentifierBuilder(useSpoid, *this, appPackageName),
mCDM(cdm),
mCrypto(crypto),
mCryptoSessions() {}
WVDrmPlugin::~WVDrmPlugin() {
typedef map<CdmSessionId, CryptoSession>::iterator mapIterator;
for (mapIterator iter = mCryptoSessions.begin();
iter != mCryptoSessions.end();
++iter) {
CdmResponseType res = mCDM->CloseSession(iter->first);
if (!isCdmResponseTypeSuccess(res)) {
ALOGE("Failed to close session while destroying WVDrmPlugin");
}
}
mCryptoSessions.clear();
}
Return<void> WVDrmPlugin::openSession(openSession_cb _hidl_cb) {
Status status = Status::OK;
std::vector<uint8_t> sessionId;
CdmIdentifier identifier;
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_hidl_cb(status, toHidlVec(sessionId));
return Void();
}
CdmSessionId cdmSessionId;
CdmResponseType res =
mCDM->OpenSession("com.widevine", &mPropertySet, identifier, this,
&cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
status = mapAndNotifyOfCdmResponseType(sessionId, res);
_hidl_cb(status, toHidlVec(sessionId));
return Void();
}
bool success = false;
// Construct a CryptoSession
CdmQueryMap info;
res = mCDM->QueryOemCryptoSessionId(cdmSessionId, &info);
if (isCdmResponseTypeSuccess(res) &&
info.count(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID)) {
OEMCrypto_SESSION oecSessionId;
std::istringstream(
info[wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID]) >> oecSessionId;
mCryptoSessions[cdmSessionId] = CryptoSession(oecSessionId);
success = true;
} else {
ALOGE("Unable to query key control info.");
}
if (success) {
// Marshal Session ID
sessionId = StrToVector(cdmSessionId);
_hidl_cb(Status::OK, toHidlVec(sessionId));
return Void();
} else {
mCDM->CloseSession(cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
// We got an error code we can return.
status = mapAndNotifyOfCdmResponseType(sessionId, res);
} else {
// We got a failure that did not give us an error code, such as a failure
// of AttachEventListener() or the key being missing from the map.
ALOGW("Returns UNKNOWN error for legacy status kErrorCDMGeneric");
status = Status::ERROR_DRM_UNKNOWN;
}
}
_hidl_cb(status, toHidlVec(sessionId));
return Void();
}
Return<Status> WVDrmPlugin::closeSession(const hidl_vec<uint8_t>& sessionId) {
if (!sessionId.size()) {
return Status::BAD_VALUE;
}
const std::vector<uint8_t> sId = toVector(sessionId);
CdmSessionId cdmSessionId(sId.begin(), sId.end());
CdmResponseType res = mCDM->CloseSession(cdmSessionId);
mCryptoSessions.erase(cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
return Status::ERROR_DRM_SESSION_NOT_OPENED;
}
return Status::OK;
}
Return<void> WVDrmPlugin::getKeyRequest(
const hidl_vec<uint8_t>& scope,
const hidl_vec<uint8_t>& initData,
const hidl_string& mimeType,
KeyType keyType,
const hidl_vec<KeyValue>& optionalParameters,
getKeyRequest_cb _hidl_cb) {
if (!scope.size()) {
_hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(),
KeyRequestType::UNKNOWN, "");
return Void();
}
KeyRequestType requestType = KeyRequestType::UNKNOWN;
Status status = Status::OK;
std::string defaultUrl;
std::vector<uint8_t> request;
const std::vector<uint8_t> scopeId = toVector(scope);
CdmIdentifier identifier;
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_hidl_cb(status, toHidlVec(request), requestType,
defaultUrl.c_str());
return Void();
}
CdmLicenseType cdmLicenseType;
CdmSessionId cdmSessionId;
CdmKeySetId cdmKeySetId;
if (keyType == KeyType::OFFLINE) {
cdmLicenseType = wvcdm::kLicenseTypeOffline;
cdmSessionId.assign(scopeId.begin(), scopeId.end());
} else if (keyType == KeyType::STREAMING) {
cdmLicenseType = wvcdm::kLicenseTypeStreaming;
cdmSessionId.assign(scopeId.begin(), scopeId.end());
} else if (keyType == KeyType::RELEASE) {
cdmLicenseType = wvcdm::kLicenseTypeRelease;
cdmKeySetId.assign(scopeId.begin(), scopeId.end());
} else {
_hidl_cb(Status::BAD_VALUE, toHidlVec(request), KeyRequestType::UNKNOWN,
defaultUrl.c_str());
return Void();
}
std::string cdmInitDataType = mimeType;
// Provide backwards-compatibility for apps that pass non-EME-compatible MIME
// types.
if (!WvContentDecryptionModule::IsSupported(cdmInitDataType)) {
cdmInitDataType = wvcdm::ISO_BMFF_VIDEO_MIME_TYPE;
}
CdmInitData processedInitData;
if (initData.size() > 0 &&
WvContentDecryptionModule::IsCenc(cdmInitDataType) &&
!initDataResemblesPSSH(toVector(initData))) {
// This data was passed in the old format, pre-unwrapped. We need to wrap
// the init data in a new PSSH header.
static const uint8_t psshPrefix[] = {
0, 0, 0, 0, // 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, 0 // Size of initData
};
processedInitData.assign(reinterpret_cast<const char*>(psshPrefix),
sizeof(psshPrefix) / sizeof(uint8_t));
processedInitData.append(reinterpret_cast<const char*>(initData.data()),
initData.size());
const size_t kPsshBoxSizeLocation = 0;
const size_t kInitDataSizeLocation =
sizeof(psshPrefix) - sizeof(uint32_t);
uint32_t psshBoxSize = htonl(processedInitData.size());
uint32_t initDataSize = htonl(initData.size());
memcpy(&processedInitData[kPsshBoxSizeLocation], &psshBoxSize,
sizeof(uint32_t));
memcpy(&processedInitData[kInitDataSizeLocation], &initDataSize,
sizeof(uint32_t));
} else {
// For other formats, we can pass the init data through unmodified.
processedInitData.assign(reinterpret_cast<const char*>(initData.data()),
initData.size());
}
CdmAppParameterMap cdmParameters;
for (size_t i = 0; i < optionalParameters.size(); ++i) {
const std::string& key(optionalParameters[i].key);
const std::string& value(optionalParameters[i].value);
std::string cdmKey(key.c_str(), key.size());
std::string cdmValue(value.c_str(), value.size());
cdmParameters[cdmKey] = cdmValue;
}
CdmKeyRequest keyRequest;
CdmResponseType res = mCDM->GenerateKeyRequest(
cdmSessionId, cdmKeySetId, cdmInitDataType, processedInitData,
cdmLicenseType, cdmParameters, &mPropertySet, identifier, &keyRequest);
requestType = ConvertFromCdmKeyRequestType(keyRequest.type);
if (isCdmResponseTypeSuccess(res)) {
defaultUrl.clear();
defaultUrl.assign(keyRequest.url.data(), keyRequest.url.size());
request = StrToVector(keyRequest.message);
}
if (keyType == KeyType::RELEASE) {
// When releasing keys, we do not have a session ID.
status = mapCdmResponseType(res);
} else {
// For all other requests, we have a session ID.
status = mapAndNotifyOfCdmResponseType(scopeId, res);
}
_hidl_cb(status, toHidlVec(request), requestType,
defaultUrl.c_str());
return Void();
}
Return<void> WVDrmPlugin::provideKeyResponse(
const hidl_vec<uint8_t>& scope,
const hidl_vec<uint8_t>& response,
provideKeyResponse_cb _hidl_cb) {
if (scope.size() == 0 || response.size() == 0) {
_hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>());
return Void();
}
const std::vector<uint8_t> resp = toVector(response);
const std::vector<uint8_t> scopeId = toVector(scope);
CdmKeySetId cdmKeySetId;
CdmSessionId cdmSessionId;
CdmKeyResponse cdmResponse(resp.begin(), resp.end());
bool isRequest = (memcmp(scopeId.data(), wvcdm::SESSION_ID_PREFIX,
sizeof(wvcdm::SESSION_ID_PREFIX) - 1) == 0);
bool isRelease = (memcmp(scopeId.data(), wvcdm::KEY_SET_ID_PREFIX,
sizeof(wvcdm::KEY_SET_ID_PREFIX) - 1) == 0);
std::vector<uint8_t> keySetId;
if (isRequest) {
cdmSessionId.assign(scopeId.begin(), scopeId.end());
} else if (isRelease) {
cdmKeySetId.assign(scopeId.begin(), scopeId.end());
} else {
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(keySetId));
return Void();
}
CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, &cdmKeySetId);
if (isRequest && isCdmResponseTypeSuccess(res)) {
keySetId = StrToVector(cdmKeySetId);
}
Status status = Status::OK;
if (isRelease) {
// When releasing keys, we do not have a session ID.
status = mapCdmResponseType(res);
} else {
// For all other requests, we have a session ID.
status = mapAndNotifyOfCdmResponseType(scopeId, res);
// For "NEED_KEY," we still want to send the notifcation, but then we don't
// return the error. This is because "NEED_KEY" from AddKey() is an
// expected behavior when sending a privacy certificate.
if (res == wvcdm::NEED_KEY && mPropertySet.use_privacy_mode()) {
status = Status::OK;
}
}
_hidl_cb(status, toHidlVec(keySetId));
return Void();
}
Return<Status> WVDrmPlugin::removeKeys(const hidl_vec<uint8_t>& sessionId) {
if (!sessionId.size()) {
return Status::BAD_VALUE;
}
const std::vector<uint8_t> sId = toVector(sessionId);
CdmSessionId cdmSessionId(sId.begin(), sId.end());
CdmResponseType res = mCDM->RemoveKeys(cdmSessionId);
return mapAndNotifyOfCdmResponseType(sId, res);
}
Return<Status> WVDrmPlugin::restoreKeys(const hidl_vec<uint8_t>& sessionId,
const hidl_vec<uint8_t>& keySetId) {
if (!sessionId.size() || !keySetId.size()) {
return Status::BAD_VALUE;
}
const std::vector<uint8_t> kId = toVector(keySetId);
const std::vector<uint8_t> sId = toVector(sessionId);
CdmSessionId cdmSessionId(sId.begin(), sId.end());
CdmKeySetId cdmKeySetId(kId.begin(), kId.end());
CdmResponseType res = mCDM->RestoreKey(cdmSessionId, cdmKeySetId);
return mapAndNotifyOfCdmResponseType(sId, res);
}
Return<void> WVDrmPlugin::queryKeyStatus(const hidl_vec<uint8_t>& sessionId,
queryKeyStatus_cb _hidl_cb) {
if (sessionId.size() == 0) {
_hidl_cb(Status::BAD_VALUE, hidl_vec<KeyValue>());
return Void();
}
const std::vector<uint8_t> sId = toVector(sessionId);
CdmSessionId cdmSessionId(sId.begin(), sId.end());
CdmQueryMap cdmLicenseInfo;
CdmResponseType res = mCDM->QueryKeyStatus(cdmSessionId, &cdmLicenseInfo);
std::vector<KeyValue> infoMapVec;
if (isCdmResponseTypeSuccess(res)) {
infoMapVec.clear();
KeyValue keyValuePair;
for (CdmQueryMap::const_iterator iter = cdmLicenseInfo.begin();
iter != cdmLicenseInfo.end();
++iter) {
const std::string& cdmKey = iter->first;
const std::string& cdmValue = iter->second;
keyValuePair.key = std::string(cdmKey.data(), cdmKey.size());
keyValuePair.value = std::string(cdmValue.data(), cdmValue.size());
infoMapVec.push_back(keyValuePair);
}
}
_hidl_cb(mapCdmResponseType(res), toHidlVec(infoMapVec));
return Void();
}
Return<void> WVDrmPlugin::getProvisionRequest(
const hidl_string& certificateType,
const hidl_string& certificateAuthority,
getProvisionRequest_cb _hidl_cb) {
Status status = Status::OK;
std::string defaultUrl;
std::vector<uint8_t> request;
CdmIdentifier identifier;
status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_hidl_cb(status, toHidlVec(request), hidl_string(defaultUrl));
return Void();
}
CdmProvisioningRequest cdmProvisionRequest;
std::string cdmDefaultUrl;
CdmCertificateType cdmCertType = wvcdm::kCertificateWidevine;
if (certificateType == "X.509") {
cdmCertType = wvcdm::kCertificateX509;
}
std::string cdmCertAuthority = certificateAuthority;
CdmResponseType res = mCDM->GetProvisioningRequest(
cdmCertType, cdmCertAuthority, identifier, &cdmProvisionRequest,
&cdmDefaultUrl);
if (isCdmResponseTypeSuccess(res)) {
request = StrToVector(cdmProvisionRequest);
defaultUrl.clear();
defaultUrl.assign(cdmDefaultUrl.data(), cdmDefaultUrl.size());
}
_hidl_cb(mapCdmResponseType(res), toHidlVec(request),
hidl_string(defaultUrl));
return Void();
}
Return<void> WVDrmPlugin::provideProvisionResponse(
const hidl_vec<uint8_t>& response,
provideProvisionResponse_cb _hidl_cb) {
if (!response.size()) {
_hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(), hidl_vec<uint8_t>());
return Void();
}
const std::vector<uint8_t> resp = toVector(response);
std::vector<uint8_t> certificate;
std::vector<uint8_t> wrappedKey;
CdmIdentifier identifier;
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_hidl_cb(status, toHidlVec(certificate), toHidlVec(wrappedKey));
}
CdmProvisioningResponse cdmResponse(resp.begin(), resp.end());
if (cdmResponse == kSpecialUnprovisionResponse) {
if (identifier == kDefaultCdmIdentifier) {
ALOGW("Returns UNKNOWN error for legacy status kErrorNoOriginSpecified");
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(certificate),
toHidlVec(wrappedKey));
return Void();
}
_hidl_cb(unprovision(identifier),
toHidlVec(certificate),
toHidlVec(wrappedKey));
return Void();
} else {
std::string cdmCertificate;
std::string cdmWrappedKey;
CdmResponseType res = mCDM->HandleProvisioningResponse(
identifier, cdmResponse, &cdmCertificate, &cdmWrappedKey);
if (isCdmResponseTypeSuccess(res)) {
certificate = StrToVector(cdmCertificate);
wrappedKey = StrToVector(cdmWrappedKey);
}
_hidl_cb(mapCdmResponseType(res), toHidlVec(certificate),
toHidlVec(wrappedKey));
return Void();
}
}
Status WVDrmPlugin::unprovisionDevice() {
return unprovision(kDefaultCdmIdentifier);
}
Return<void> WVDrmPlugin::getSecureStop(
const hidl_vec<uint8_t>& secureStopId,
getSecureStop_cb _hidl_cb) {
if (!secureStopId.size()) {
_hidl_cb(Status::BAD_VALUE, SecureStop());
return Void();
}
const std::vector<uint8_t> id = toVector(secureStopId);
SecureStop secureStop;
CdmIdentifier identifier;
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_hidl_cb(status, secureStop);
return Void();
}
CdmUsageInfo cdmUsageInfo;
CdmSecureStopId cdmSsId(id.begin(), id.end());
CdmResponseType res = mCDM->GetUsageInfo(
mPropertySet.app_id(), cdmSsId, &cdmUsageInfo);
if (isCdmResponseTypeSuccess(res)) {
std::vector<uint8_t> cdmStopVec;
for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
iter != cdmUsageInfo.end();
++iter) {
const std::string& cdmStop = *iter;
cdmStopVec = StrToVector(cdmStop);
}
secureStop.opaqueData = toHidlVec(cdmStopVec);
}
_hidl_cb(mapCdmResponseType(res), secureStop);
return Void();
}
Return<void> WVDrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) {
std::list<std::vector<uint8_t> > secureStops;
std::vector<SecureStop> secureStopsVec;
CdmIdentifier identifier;
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
_hidl_cb(status, toHidlVec(secureStopsVec));
return Void();
}
CdmUsageInfo cdmUsageInfo;
CdmResponseType res =
mCDM->GetUsageInfo(mPropertySet.app_id(), &cdmUsageInfo);
if (isCdmResponseTypeSuccess(res)) {
secureStops.clear();
for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
iter != cdmUsageInfo.end();
++iter) {
const std::string& cdmStop = *iter;
secureStops.push_back(StrToVector(cdmStop));
}
}
std::list<std::vector<uint8_t> >::iterator iter = secureStops.begin();
while (iter != secureStops.end()) {
SecureStop secureStop;
secureStop.opaqueData = toHidlVec(*iter++);
secureStopsVec.push_back(secureStop);
}
_hidl_cb(mapCdmResponseType(res), toHidlVec(secureStopsVec));
return Void();
}
Return<Status> WVDrmPlugin::releaseAllSecureStops() {
CdmIdentifier identifier;
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return status;
}
CdmResponseType res = mCDM->ReleaseAllUsageInfo(mPropertySet.app_id());
return mapCdmResponseType(res);
}
Return<Status> WVDrmPlugin::releaseSecureStop(
const hidl_vec<uint8_t>& secureStopId) {
if (!secureStopId.size()) {
return Status::BAD_VALUE;
}
CdmIdentifier identifier;
Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return status;
}
const std::vector<uint8_t> ssRelease = toVector(secureStopId);
CdmUsageInfoReleaseMessage cdmMessage(ssRelease.begin(), ssRelease.end());
CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage);
return mapCdmResponseType(res);
}
Return<void> WVDrmPlugin::getPropertyString(const hidl_string& propertyName,
getPropertyString_cb _hidl_cb) {
Status status = Status::OK;
std::string name(propertyName.c_str());
std::string value;
if (name == "vendor") {
value = "Google";
} else if (name == "version") {
value = "1.0";
} else if (name == "description") {
value = "Widevine CDM";
} else if (name == "algorithms") {
value = "AES/CBC/NoPadding,HmacSHA256";
} else if (name == "securityLevel") {
std::string requestedLevel = mPropertySet.security_level();
if (requestedLevel.length() > 0) {
value = requestedLevel.c_str();
} else {
status = queryProperty(wvcdm::QUERY_KEY_SECURITY_LEVEL, value);
}
} else if (name == "systemId") {
status = queryProperty(wvcdm::QUERY_KEY_SYSTEM_ID, value);
} else if (name == "privacyMode") {
if (mPropertySet.use_privacy_mode()) {
value = kEnable;
} else {
value = kDisable;
}
} else if (name == "sessionSharing") {
if (mPropertySet.is_session_sharing_enabled()) {
value = kEnable;
} else {
value = kDisable;
}
} else if (name == "hdcpLevel") {
status = queryProperty(wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, value);
} else if (name == "maxHdcpLevel") {
status = queryProperty(wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, value);
} else if (name == "usageReportingSupport") {
status = queryProperty(wvcdm::QUERY_KEY_USAGE_SUPPORT, value);
} else if (name == "numberOfOpenSessions") {
status = queryProperty(wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, value);
} else if (name == "maxNumberOfSessions") {
status = queryProperty(wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, value);
} else if (name == "oemCryptoApiVersion") {
status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, value);
} else if (name == "appId") {
value = mPropertySet.app_id().c_str();
} else if (name == "origin") {
value = mCdmIdentifierBuilder.origin().c_str();
} else {
ALOGE("App requested unknown string property %s", name.c_str());
status = Status::ERROR_DRM_CANNOT_HANDLE;
}
_hidl_cb(status, value.c_str());
return Void();
}
Return<void> WVDrmPlugin::getPropertyByteArray(
const hidl_string& propertyName,
getPropertyByteArray_cb _hidl_cb) {
Status status = Status::OK;
std::string name(propertyName.c_str());
std::vector<uint8_t> value;
if (name == "deviceUniqueId") {
std::string id;
status = mCdmIdentifierBuilder.getDeviceUniqueId(&id);
if (status == Status::OK) {
value = StrToVector(id);
}
} else if (name == "provisioningUniqueId") {
std::string id;
status = mCdmIdentifierBuilder.getProvisioningUniqueId(&id);
if (status == Status::OK) {
value = StrToVector(id);
}
} else if (name == "serviceCertificate") {
value = StrToVector(mPropertySet.service_certificate());
} else {
ALOGE("App requested unknown byte array property %s", name.c_str());
status = Status::ERROR_DRM_CANNOT_HANDLE;
}
_hidl_cb(status, toHidlVec(value));
return Void();
}
Return<Status> WVDrmPlugin::setPropertyString(const hidl_string& propertyName,
const hidl_string& value) {
std::string name(propertyName.c_str());
std::string _value(value.c_str());
if (name == "securityLevel") {
if (mCryptoSessions.size() == 0) {
if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.c_str()) {
mPropertySet.set_security_level(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3);
} else if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1.c_str()) {
// We must be sure we CAN set the security level to L1.
std::string current_security_level;
Status status = queryProperty(
wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
current_security_level);
if (status != Status::OK) {
return status;
}
if (current_security_level != wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) {
ALOGE("App requested L1 security on a non-L1 device.");
return Status::BAD_VALUE;
} else {
mPropertySet.set_security_level(kResetSecurityLevel);
}
} else if (_value == kResetSecurityLevel) {
mPropertySet.set_security_level(kResetSecurityLevel);
} else {
ALOGE("App requested invalid security level %s", _value.c_str());
return Status::BAD_VALUE;
}
} else {
ALOGE("App tried to change security level while sessions are open.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return Status::ERROR_DRM_UNKNOWN;
}
} else if (name == "privacyMode") {
if (_value == kEnable) {
mPropertySet.set_use_privacy_mode(true);
} else if (_value == kDisable) {
mPropertySet.set_use_privacy_mode(false);
} else {
ALOGE("App requested unknown privacy mode %s", _value.c_str());
return Status::BAD_VALUE;
}
} else if (name == "sessionSharing") {
if (mCryptoSessions.size() == 0) {
if (_value == kEnable) {
mPropertySet.set_is_session_sharing_enabled(true);
} else if (_value == kDisable) {
mPropertySet.set_is_session_sharing_enabled(false);
} else {
ALOGE("App requested unknown sharing type %s", _value.c_str());
return Status::BAD_VALUE;
}
} else {
ALOGE("App tried to change key sharing while sessions are open.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return Status::ERROR_DRM_UNKNOWN;
}
} else if (name == "appId") {
if (mCryptoSessions.size() == 0) {
mPropertySet.set_app_id(_value.c_str());
} else {
ALOGE("App tried to set the application id while sessions are opened.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return Status::ERROR_DRM_UNKNOWN;
}
} else if (name == "origin") {
if (mCryptoSessions.size() != 0) {
ALOGE("App tried to set the origin while sessions are opened.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return Status::ERROR_DRM_UNKNOWN;
} else {
if (!mCdmIdentifierBuilder.set_origin(_value.c_str())) {
return Status::BAD_VALUE;
}
}
} else {
ALOGE("App set unknown string property %s", name.c_str());
return Status::ERROR_DRM_CANNOT_HANDLE;
}
return Status::OK;
}
Return<Status> WVDrmPlugin::setPropertyByteArray(
const hidl_string& propertyName, const hidl_vec<uint8_t>& value) {
std::string name(propertyName.c_str());
std::vector<uint8_t> _value = toVector(value);
if (name == "serviceCertificate") {
std::string cert(_value.begin(), _value.end());
if (_value.empty() || mCDM->IsValidServiceCertificate(cert)) {
mPropertySet.set_service_certificate(cert);
} else {
return Status::BAD_VALUE;
}
} else {
ALOGE("App set unknown byte array property %s", name.c_str());
return Status::ERROR_DRM_CANNOT_HANDLE;
}
return Status::OK;
}
Return<Status> WVDrmPlugin::setCipherAlgorithm(
const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {
if (sessionId.size() == 0 || algorithm.size() == 0) {
return Status::BAD_VALUE;
}
std::string algo(algorithm.c_str());
std::vector<uint8_t> sId = toVector(sessionId);
CdmSessionId cdmSessionId(sId.begin(), sId.end());
if (!mCryptoSessions.count(cdmSessionId)) {
return Status::ERROR_DRM_SESSION_NOT_OPENED;
}
CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (algo == "AES/CBC/NoPadding") {
cryptoSession.setCipherAlgorithm(OEMCrypto_AES_CBC_128_NO_PADDING);
} else {
return Status::ERROR_DRM_CANNOT_HANDLE;
}
return Status::OK;
}
Return<Status> WVDrmPlugin::setMacAlgorithm(
const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {
if (sessionId.size() == 0 || algorithm.size() == 0) {
return Status::BAD_VALUE;
}
std::string algo(algorithm.c_str());
std::vector<uint8_t> sId = toVector(sessionId);
CdmSessionId cdmSessionId(sId.begin(), sId.end());
if (!mCryptoSessions.count(cdmSessionId)) {
return Status::ERROR_DRM_SESSION_NOT_OPENED;
}
CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (algo == "HmacSHA256") {
cryptoSession.setMacAlgorithm(OEMCrypto_HMAC_SHA256);
} else {
return Status::ERROR_DRM_CANNOT_HANDLE;
}
return Status::OK;
}
Return<void> WVDrmPlugin::encrypt(
const hidl_vec<uint8_t>& sessionId,
const hidl_vec<uint8_t>& keyId,
const hidl_vec<uint8_t>& input,
const hidl_vec<uint8_t>& iv,
encrypt_cb _hidl_cb) {
const std::vector<uint8_t> sId = toVector(sessionId);
std::vector<uint8_t> output;
CdmSessionId cdmSessionId(sId.begin(), sId.end());
if (!mCryptoSessions.count(cdmSessionId)) {
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(output));
return Void();
}
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(output));
return Void();
}
const std::vector<uint8_t> _keyId = toVector(keyId);
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
_keyId.data(), _keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
toHidlVec(output));
return Void();
}
const std::vector<uint8_t> _input = toVector(input);
const std::vector<uint8_t> _iv = toVector(iv);
output.resize(_input.size());
res = mCrypto->encrypt(cryptoSession.oecSessionId(), _input.data(),
_input.size(), _iv.data(),
cryptoSession.cipherAlgorithm(), output.data());
if (res == OEMCrypto_SUCCESS) {
_hidl_cb(Status::OK, toHidlVec(output));
} else {
ALOGE("OEMCrypto_Generic_Encrypt failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
toHidlVec(output));
}
return Void();
}
Return<void> WVDrmPlugin::decrypt(
const hidl_vec<uint8_t>& sessionId,
const hidl_vec<uint8_t>& keyId,
const hidl_vec<uint8_t>& input,
const hidl_vec<uint8_t>& iv,
decrypt_cb _hidl_cb) {
const std::vector<uint8_t> sId = toVector(sessionId);
std::vector<uint8_t> output;
CdmSessionId cdmSessionId(sId.begin(), sId.end());
if (!mCryptoSessions.count(cdmSessionId)) {
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(output));
return Void();
}
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(output));
return Void();
}
const std::vector<uint8_t> _keyId = toVector(keyId);
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
_keyId.data(), _keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
toHidlVec(output));
return Void();
}
const std::vector<uint8_t> _input = toVector(input);
const std::vector<uint8_t> _iv = toVector(iv);
output.resize(_input.size());
res = mCrypto->decrypt(cryptoSession.oecSessionId(), _input.data(),
_input.size(), _iv.data(),
cryptoSession.cipherAlgorithm(), output.data());
if (res == OEMCrypto_SUCCESS) {
_hidl_cb(Status::OK, toHidlVec(output));
} else {
ALOGE("OEMCrypto_Generic_Decrypt failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
toHidlVec(output));
}
return Void();
}
Return<void> WVDrmPlugin::sign(
const hidl_vec<uint8_t>& sessionId,
const hidl_vec<uint8_t>& keyId,
const hidl_vec<uint8_t>& message,
sign_cb _hidl_cb) {
const std::vector<uint8_t> sId = toVector(sessionId);
std::vector<uint8_t> signature;
CdmSessionId cdmSessionId(sId.begin(), sId.end());
if (!mCryptoSessions.count(cdmSessionId)) {
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(signature));
return Void();
}
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(signature));
return Void();
}
const std::vector<uint8_t> _keyId = toVector(keyId);
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
_keyId.data(), _keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
toHidlVec(signature));
return Void();
}
size_t signatureSize = 0;
const std::vector<uint8_t> msg = toVector(message);
res = mCrypto->sign(cryptoSession.oecSessionId(), msg.data(),
msg.size(), cryptoSession.macAlgorithm(),
NULL, &signatureSize);
if (res != OEMCrypto_ERROR_SHORT_BUFFER) {
ALOGE("OEMCrypto_Generic_Sign failed with %u when requesting signature "
"size", res);
if (res != OEMCrypto_SUCCESS) {
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
toHidlVec(signature));
} else {
_hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(signature));
}
return Void();
}
signature.resize(signatureSize);
res = mCrypto->sign(cryptoSession.oecSessionId(), msg.data(),
msg.size(), cryptoSession.macAlgorithm(),
signature.data(), &signatureSize);
if (res == OEMCrypto_SUCCESS) {
_hidl_cb(Status::OK, toHidlVec(signature));
} else {
ALOGE("OEMCrypto_Generic_Sign failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
toHidlVec(signature));
}
return Void();
}
Return<void> WVDrmPlugin::verify(
const hidl_vec<uint8_t>& sessionId,
const hidl_vec<uint8_t>& keyId,
const hidl_vec<uint8_t>& message,
const hidl_vec<uint8_t>& signature,
verify_cb _hidl_cb) {
bool match = false;
const std::vector<uint8_t> sId = toVector(sessionId);
CdmSessionId cdmSessionId(sId.begin(), sId.end());
if (!mCryptoSessions.count(cdmSessionId)) {
_hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, match);
return Void();
}
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
_hidl_cb(Status::ERROR_DRM_UNKNOWN, match);
return Void();
}
const std::vector<uint8_t> _keyId = toVector(keyId);
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
_keyId.data(), _keyId.size());
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_SelectKey failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res), match);
return Void();
}
const std::vector<uint8_t> _message = toVector(message);
const std::vector<uint8_t> _signature = toVector(signature);
res = mCrypto->verify(cryptoSession.oecSessionId(), _message.data(),
_message.size(), cryptoSession.macAlgorithm(),
_signature.data(), _signature.size());
if (res == OEMCrypto_SUCCESS) {
match = true;
_hidl_cb(Status::OK, match);
} else if (res == OEMCrypto_ERROR_SIGNATURE_FAILURE) {
match = false;
_hidl_cb(Status::OK, match);
} else {
ALOGE("OEMCrypto_Generic_Verify failed with %u", res);
_hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res), match);
}
return Void();
}
Return<void> WVDrmPlugin::signRSA(
const hidl_vec<uint8_t>& sessionId,
const hidl_string& algorithm,
const hidl_vec<uint8_t>& message,
const hidl_vec<uint8_t>& wrappedKey,
signRSA_cb _hidl_cb) {
if (sessionId.size() == 0 || algorithm.size() == 0 ||
message.size() == 0 || wrappedKey.size() == 0) {
_hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>());
return Void();
}
const std::string algo(algorithm.c_str());
std::vector<uint8_t> signature;
RSA_Padding_Scheme padding_scheme;
if (algo == "RSASSA-PSS-SHA1") {
padding_scheme = kSign_RSASSA_PSS;
} else if (algo == "PKCS1-BlockType1") {
padding_scheme = kSign_PKCS1_Block1;
} else {
ALOGE("Unknown RSA Algorithm %s", algo.c_str());
_hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(signature));
return Void();
}
const std::vector<uint8_t> msg = toVector(message);
const std::vector<uint8_t> _wrappedKey = toVector(wrappedKey);
OEMCryptoResult res = mCrypto->signRSA(_wrappedKey.data(),
_wrappedKey.size(),
msg.data(), msg.size(),
signature,
padding_scheme);
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_GenerateRSASignature failed with %u", res);
_hidl_cb(mapOEMCryptoResult(res), toHidlVec(signature));
return Void();
}
_hidl_cb(Status::OK, toHidlVec(signature));
return Void();
}
Return<void> WVDrmPlugin::setListener(const sp<IDrmPluginListener>& listener) {
mListener = listener;
return Void();
}
Return<void> WVDrmPlugin::sendEvent(
EventType eventType,
const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& data) {
if (mListener != NULL) {
mListener->sendEvent(eventType, sessionId, data);
} else {
ALOGE("Null event listener, event not sent");
}
return Void();
}
Return<void> WVDrmPlugin::sendExpirationUpdate(
const hidl_vec<uint8_t>& sessionId,
int64_t expiryTimeInMS) {
if (mListener != NULL) {
mListener->sendExpirationUpdate(sessionId, expiryTimeInMS);
} else {
ALOGE("Null event listener, event not sent");
}
return Void();
}
Return<void> WVDrmPlugin::sendKeysChange(
const hidl_vec<uint8_t>& sessionId,
const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
if (mListener != NULL) {
mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
} else {
ALOGE("Null event listener, event not sent");
}
return Void();
}
void WVDrmPlugin::OnSessionRenewalNeeded(const CdmSessionId& cdmSessionId) {
const std::vector<uint8_t> sessionId = StrToVector(cdmSessionId);
const hidl_vec<uint8_t> data; // data is ignored
const hidl_vec<uint8_t> sid = toHidlVec(sessionId);
sendEvent(EventType::KEY_NEEDED, sid, data);
}
void WVDrmPlugin::OnSessionKeysChange(const CdmSessionId& cdmSessionId,
const CdmKeyStatusMap& cdmKeysStatus,
bool hasNewUsableKey) {
bool expired = false;
std::vector<KeyStatus> keyStatusList;
for (CdmKeyStatusMap::const_iterator it = cdmKeysStatus.begin();
it != cdmKeysStatus.end(); ++it) {
const KeyId& keyId = it->first;
const CdmKeyStatus cdmKeyStatus = it->second;
if (cdmKeyStatus == wvcdm::kKeyStatusExpired) expired = true;
KeyStatus keyStatus;
keyStatus.keyId = toHidlVec(StrToVector(keyId));
keyStatus.type = ConvertFromCdmKeyStatus(cdmKeyStatus);
keyStatusList.push_back(keyStatus);
}
const std::vector<uint8_t> sessionId = StrToVector(cdmSessionId);
const hidl_vec<uint8_t> data; // data is ignored
const hidl_vec<uint8_t> sid = toHidlVec(sessionId);
sendKeysChange(sid, toHidlVec(keyStatusList), hasNewUsableKey);
// For backward compatibility.
if (expired) {
sendEvent(EventType::KEY_EXPIRED, sid, data);
}
}
void WVDrmPlugin::OnExpirationUpdate(const CdmSessionId& cdmSessionId,
int64_t newExpiryTimeSeconds) {
const std::vector<uint8_t> sessionId = StrToVector(cdmSessionId);
int64_t newExpiryTimeMilliseconds =
newExpiryTimeSeconds == wvcdm::NEVER_EXPIRES
? newExpiryTimeSeconds : newExpiryTimeSeconds * 1000;
sendExpirationUpdate(toHidlVec(sessionId), newExpiryTimeMilliseconds);
}
Status WVDrmPlugin::queryProperty(const std::string& property,
std::string& stringValue) const {
SecurityLevel securityLevel =
mPropertySet.security_level().compare(
wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0
? wvcdm::kLevel3
: wvcdm::kLevelDefault;
return queryProperty(securityLevel, property, stringValue);
}
Status WVDrmPlugin::queryProperty(SecurityLevel securityLevel,
const std::string& property,
std::string& stringValue) const {
CdmResponseType res =
mCDM->QueryStatus(securityLevel, property, &stringValue);
if (res != wvcdm::NO_ERROR) {
ALOGE("Error querying CDM status: %u", res);
}
return mapCdmResponseType(res);
}
Status WVDrmPlugin::queryProperty(const std::string& property,
std::vector<uint8_t>& vector_value) const {
std::string string_value;
Status status = queryProperty(property, string_value);
if (status != Status::OK) return status;
vector_value = StrToVector(string_value);
return Status::OK;
}
Status WVDrmPlugin::mapAndNotifyOfCdmResponseType(
const std::vector<uint8_t>& sessionId,
CdmResponseType res) {
const hidl_vec<uint8_t> data; // data is ignored
if (res == wvcdm::NEED_PROVISIONING) {
sendEvent(EventType::PROVISION_REQUIRED, toHidlVec(sessionId), data);
} else if (res == wvcdm::NEED_KEY) {
sendEvent(EventType::KEY_NEEDED, toHidlVec(sessionId), data);
}
return mapCdmResponseType(res);
}
Status WVDrmPlugin::mapAndNotifyOfOEMCryptoResult(
const std::vector<uint8_t>& sessionId,
OEMCryptoResult res) {
const hidl_vec<uint8_t> data; // data is ignored
if (res == OEMCrypto_ERROR_NO_DEVICE_KEY) {
sendEvent(EventType::PROVISION_REQUIRED, toHidlVec(sessionId), data);
}
return mapOEMCryptoResult(res);
}
Status WVDrmPlugin::mapOEMCryptoResult(OEMCryptoResult res) {
switch (res) {
case OEMCrypto_SUCCESS:
return Status::OK;
case OEMCrypto_ERROR_SIGNATURE_FAILURE:
return Status::ERROR_DRM_INVALID_STATE;
case OEMCrypto_ERROR_NO_DEVICE_KEY:
return Status::ERROR_DRM_NOT_PROVISIONED;
case OEMCrypto_ERROR_INVALID_SESSION:
return Status::ERROR_DRM_SESSION_NOT_OPENED;
case OEMCrypto_ERROR_TOO_MANY_SESSIONS:
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return Status::ERROR_DRM_RESOURCE_BUSY;
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
return Status::ERROR_DRM_CANNOT_HANDLE;
case OEMCrypto_ERROR_INVALID_RSA_KEY:
case OEMCrypto_ERROR_SHORT_BUFFER:
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
case OEMCrypto_ERROR_OPEN_SESSION_FAILED:
default:
return Status::ERROR_DRM_UNKNOWN;
}
}
bool WVDrmPlugin::initDataResemblesPSSH(const std::vector<uint8_t>& initData) {
const uint8_t* const initDataArray = initData.data();
if (sizeof(uint32_t) + kPsshTag.size() > initData.size()) {
// The init data is so small that it couldn't contain a size and PSSH tag.
return false;
}
// Extract the size field
const uint8_t* const sizeField = &initDataArray[0];
uint32_t nboSize;
memcpy(&nboSize, sizeField, sizeof(nboSize));
uint32_t size = ntohl(nboSize);
if (size > initData.size()) {
return false;
}
// Extract the ID field
const char* const idField =
reinterpret_cast<const char* const>(&initDataArray[sizeof(nboSize)]);
std::string id(idField, kPsshTag.size());
return id == kPsshTag;
}
Status WVDrmPlugin::unprovision(const CdmIdentifier& identifier) {
CdmResponseType res1 = mCDM->Unprovision(wvcdm::kSecurityLevelL1, identifier);
CdmResponseType res3 = mCDM->Unprovision(wvcdm::kSecurityLevelL3, identifier);
if (!isCdmResponseTypeSuccess(res1))
{
return mapCdmResponseType(res1);
}
else
{
return mapCdmResponseType(res3);
}
}
// Implementation for the CdmIdentifierBuilder inner class
WVDrmPlugin::CdmIdentifierBuilder::CdmIdentifierBuilder(
bool useSpoid, const WVDrmPlugin& parent, const std::string& appPackageName)
: mCdmIdentifier(),
mIsIdentifierSealed(false),
mUseSpoid(useSpoid),
mAppPackageName(appPackageName),
mParent(parent) {}
Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
CdmIdentifier* identifier) {
if (!mIsIdentifierSealed) {
Status res = calculateSpoid();
if (res != Status::OK) return res;
mIsIdentifierSealed = true;
}
*identifier = mCdmIdentifier;
return Status::OK;
}
Status WVDrmPlugin::CdmIdentifierBuilder::getDeviceUniqueId(std::string* id) {
if (mUseSpoid) {
CdmIdentifier identifier;
Status res = getCdmIdentifier(&identifier);
if (res != Status::OK) return res;
*id = identifier.spoid;
return Status::OK;
} else {
return getOemcryptoDeviceId(id);
}
}
Status WVDrmPlugin::CdmIdentifierBuilder::getProvisioningUniqueId(std::string* id) {
if (mUseSpoid) {
// To fake a provisioning-unique ID on SPOID devices where we can't expose
// the real provisioning-unique ID, we just use the SPOID and invert all the
// bits.
Status res = getDeviceUniqueId(id);
if (res != Status::OK) return res;
for (char& c : *id) {
c = ~c;
}
return Status::OK;
} else {
return mParent.queryProperty(wvcdm::QUERY_KEY_PROVISIONING_ID, *id);
}
}
bool WVDrmPlugin::CdmIdentifierBuilder::set_origin(const std::string& id) {
if (mIsIdentifierSealed) return false;
mCdmIdentifier.origin = id;
return true;
}
Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
if (mUseSpoid) {
std::string deviceId;
Status res = getOemcryptoDeviceId(&deviceId);
if (res != Status::OK) return res;
uint8_t hash[SHA256_DIGEST_LENGTH];
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, deviceId.data(), deviceId.length());
SHA256_Update(&ctx, mAppPackageName.data(), mAppPackageName.length());
SHA256_Update(&ctx, origin().data(), origin().length());
SHA256_Final(hash, &ctx);
mCdmIdentifier.spoid =
std::string(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
}
return Status::OK;
}
Status WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
std::string* id) {
return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
}
} // namespace widevine
} // namespace V1_0
} // namespace drm
} // namespace hardware
} // namespace wvdrm