/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "clearkey-DrmPlugin" #include #include #include #include #include #include #include "AidlUtils.h" #include "ClearKeyDrmProperties.h" #include "DrmPlugin.h" #include "Session.h" #include "Utils.h" #include "AidlClearKeryProperties.h" namespace { const std::string kKeySetIdPrefix("ckid"); const int kKeySetIdLength = 16; const int kSecureStopIdStart = 100; const std::string kOfflineLicense("\"type\":\"persistent-license\""); const std::string kStreaming("Streaming"); const std::string kTemporaryLicense("\"type\":\"temporary\""); const std::string kTrue("True"); const std::string kQueryKeyLicenseType("LicenseType"); // Value: "Streaming" or "Offline" const std::string kQueryKeyPlayAllowed("PlayAllowed"); // Value: "True" or "False" const std::string kQueryKeyRenewAllowed("RenewAllowed"); // Value: "True" or "False" const int kSecureStopIdSize = 10; std::vector uint32ToVector(uint32_t value) { // 10 bytes to display max value 4294967295 + one byte null terminator char buffer[kSecureStopIdSize]; memset(buffer, 0, kSecureStopIdSize); snprintf(buffer, kSecureStopIdSize, "%" PRIu32, value); return std::vector(buffer, buffer + sizeof(buffer)); } }; // unnamed namespace namespace aidl { namespace android { namespace hardware { namespace drm { namespace clearkey { using ::android::Mutex; DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) : mSessionLibrary(sessionLibrary), mOpenSessionOkCount(0), mCloseSessionOkCount(0), mCloseSessionNotOpenedCount(0), mNextSecureStopId(kSecureStopIdStart), mMockError(Status::OK) { mPlayPolicy.clear(); initProperties(); mSecureStops.clear(); mReleaseKeysMap.clear(); std::srand(std::time(nullptr)); } void DrmPlugin::initProperties() { mStringProperties.clear(); mStringProperties[kVendorKey] = kAidlVendorValue; mStringProperties[kVersionKey] = kAidlVersionValue; mStringProperties[kPluginDescriptionKey] = kAidlPluginDescriptionValue; mStringProperties[kAlgorithmsKey] = kAidlAlgorithmsValue; mStringProperties[kListenerTestSupportKey] = kAidlListenerTestSupportValue; mStringProperties[kDrmErrorTestKey] = kAidlDrmErrorTestValue; mStringProperties[kAidlVersionKey] = kAidlVersionValue; std::vector valueVector; valueVector.clear(); valueVector.insert(valueVector.end(), kTestDeviceIdData, kTestDeviceIdData + sizeof(kTestDeviceIdData) / sizeof(uint8_t)); mByteArrayProperties[kDeviceIdKey] = valueVector; valueVector.clear(); valueVector.insert(valueVector.end(), kMetricsData, kMetricsData + sizeof(kMetricsData) / sizeof(uint8_t)); mByteArrayProperties[kMetricsKey] = valueVector; } // The secure stop in ClearKey implementation is not installed securely. // This function merely creates a test environment for testing secure stops APIs. // The content in this secure stop is implementation dependent, the clearkey // secureStop does not serve as a reference implementation. void DrmPlugin::installSecureStop(const std::vector& sessionId) { ::android::Mutex::Autolock lock(mSecureStopLock); ClearkeySecureStop clearkeySecureStop; clearkeySecureStop.id = uint32ToVector(++mNextSecureStopId); clearkeySecureStop.data.assign(sessionId.begin(), sessionId.end()); mSecureStops.insert(std::pair, ClearkeySecureStop>(clearkeySecureStop.id, clearkeySecureStop)); } ::ndk::ScopedAStatus DrmPlugin::closeSession(const std::vector& in_sessionId) { if (in_sessionId.size() == 0) { return toNdkScopedAStatus(Status::BAD_VALUE); } ::android::sp session = mSessionLibrary->findSession(in_sessionId); if (session.get()) { mSessionLibrary->destroySession(session); if (session->getMockError() != clearkeydrm::OK) { sendSessionLostState(in_sessionId); return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); } mCloseSessionOkCount++; return toNdkScopedAStatus(Status::OK); } mCloseSessionNotOpenedCount++; return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } ::ndk::ScopedAStatus DrmPlugin::decrypt(const std::vector& in_sessionId, const std::vector& in_keyId, const std::vector& in_input, const std::vector& in_iv, std::vector* _aidl_return) { *_aidl_return = {}; if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 || in_iv.size() == 0) { return toNdkScopedAStatus(Status::BAD_VALUE); } return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::encrypt(const std::vector& in_sessionId, const std::vector& in_keyId, const std::vector& in_input, const std::vector& in_iv, std::vector* _aidl_return) { *_aidl_return = {}; if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 || in_iv.size() == 0) { return toNdkScopedAStatus(Status::BAD_VALUE); } return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::getHdcpLevels( ::aidl::android::hardware::drm::HdcpLevels* _aidl_return) { _aidl_return->connectedLevel = HdcpLevel::HDCP_NONE; _aidl_return->maxLevel = HdcpLevel::HDCP_NO_OUTPUT; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::getKeyRequest( const std::vector& in_scope, const std::vector& in_initData, const std::string& in_mimeType, ::aidl::android::hardware::drm::KeyType in_keyType, const std::vector<::aidl::android::hardware::drm::KeyValue>& in_optionalParameters, ::aidl::android::hardware::drm::KeyRequest* _aidl_return) { UNUSED(in_optionalParameters); KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; std::string defaultUrl("https://default.url"); _aidl_return->request = {}; _aidl_return->requestType = keyRequestType; _aidl_return->defaultUrl = defaultUrl; if (in_scope.size() == 0 || (in_keyType != KeyType::STREAMING && in_keyType != KeyType::OFFLINE && in_keyType != KeyType::RELEASE)) { return toNdkScopedAStatus(Status::BAD_VALUE); } const std::vector scopeId = in_scope; ::android::sp session; std::set init_types{KeyType::STREAMING, KeyType::OFFLINE}; if (init_types.count(in_keyType)) { std::vector sessionId(scopeId.begin(), scopeId.end()); session = mSessionLibrary->findSession(sessionId); if (!session.get()) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } else if (session->getMockError() != clearkeydrm::OK) { return toNdkScopedAStatus(session->getMockError()); } keyRequestType = KeyRequestType::INITIAL; } std::vector request = {}; auto keyType = static_cast(in_keyType); auto status = session->getKeyRequest(in_initData, in_mimeType, keyType, &request); if (in_keyType == KeyType::RELEASE) { std::vector keySetId(scopeId.begin(), scopeId.end()); std::string requestString(request.begin(), request.end()); if (requestString.find(kOfflineLicense) != std::string::npos) { std::string emptyResponse; std::string keySetIdString(keySetId.begin(), keySetId.end()); if (!mFileHandle.StoreLicense(keySetIdString, DeviceFiles::kLicenseStateReleasing, emptyResponse)) { ALOGE("Problem releasing offline license"); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } if (mReleaseKeysMap.find(keySetIdString) == mReleaseKeysMap.end()) { ::android::sp session = mSessionLibrary->createSession(); mReleaseKeysMap[keySetIdString] = session->sessionId(); } else { ALOGI("key is in use, ignore release request"); } } else { ALOGE("Offline license not found, nothing to release"); } keyRequestType = KeyRequestType::RELEASE; } _aidl_return->request = request; _aidl_return->requestType = keyRequestType; _aidl_return->defaultUrl = defaultUrl; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::getLogMessages( std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) { using std::chrono::duration_cast; using std::chrono::milliseconds; using std::chrono::system_clock; auto timeMillis = duration_cast(system_clock::now().time_since_epoch()).count(); std::vector<::aidl::android::hardware::drm::LogMessage> logs = { {timeMillis, ::aidl::android::hardware::drm::LogPriority::ERROR, std::string("Not implemented")}}; *_aidl_return = logs; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::getMetrics( std::vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) { // Set the open session count metric. DrmMetricNamedValue openSessionOkAttribute = {"status", static_cast(Status::OK)}; DrmMetricNamedValue openSessionMetricValue = {"count", mOpenSessionOkCount}; DrmMetric openSessionMetric = { "open_session", {openSessionOkAttribute}, {openSessionMetricValue}}; // Set the close session count metric. DrmMetricNamedValue closeSessionOkAttribute = {"status", static_cast(Status::OK)}; DrmMetricNamedValue closeSessionMetricValue = {"count", mCloseSessionOkCount}; DrmMetric closeSessionMetric = { "close_session", {closeSessionOkAttribute}, {closeSessionMetricValue}}; // Set the close session, not opened metric. DrmMetricNamedValue closeSessionNotOpenedAttribute = {"status", static_cast(Status::ERROR_DRM_SESSION_NOT_OPENED)}; DrmMetricNamedValue closeSessionNotOpenedMetricValue = {"count", mCloseSessionNotOpenedCount}; DrmMetric closeSessionNotOpenedMetric = { "close_session", {closeSessionNotOpenedAttribute}, {closeSessionNotOpenedMetricValue}}; // Set the setPlaybackId metric. std::vector sids = {}; std::vector playbackIds = {}; for (const auto& [key, value] : mPlaybackId) { std::string sid(key.begin(), key.end()); DrmMetricNamedValue sessionIdAttribute = {"sid", sid}; sids.push_back(sessionIdAttribute); DrmMetricNamedValue playbackIdMetricValue = {"playbackId", value}; playbackIds.push_back(playbackIdMetricValue); } DrmMetric setPlaybackIdMetric = {"set_playback_id", sids, playbackIds}; DrmMetricGroup metrics = {{openSessionMetric, closeSessionMetric, closeSessionNotOpenedMetric, setPlaybackIdMetric}}; *_aidl_return = {metrics}; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::getNumberOfSessions( ::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) { _aidl_return->currentSessions = mSessionLibrary->numOpenSessions(); _aidl_return->maxSessions = 10; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseKeySetIds( std::vector<::aidl::android::hardware::drm::KeySetId>* _aidl_return) { std::vector licenseNames = mFileHandle.ListLicenses(); std::vector keySetIds = {}; if (mMockError != Status::OK) { *_aidl_return = keySetIds; return toNdkScopedAStatus(toMockStatus(mMockError)); } for (const auto& name : licenseNames) { std::vector keySetId(name.begin(), name.end()); KeySetId id = {}; id.keySetId = keySetId; keySetIds.push_back(id); } *_aidl_return = keySetIds; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseState( const ::aidl::android::hardware::drm::KeySetId& in_keySetId, ::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) { std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); DeviceFiles::LicenseState state; std::string license; OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN; Status status = Status::OK; if (mMockError != Status::OK) { *_aidl_return = licenseState; return toNdkScopedAStatus(toMockStatus(mMockError)); } else if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) { switch (state) { case DeviceFiles::kLicenseStateActive: licenseState = OfflineLicenseState::USABLE; break; case DeviceFiles::kLicenseStateReleasing: licenseState = OfflineLicenseState::INACTIVE; break; case DeviceFiles::kLicenseStateUnknown: licenseState = OfflineLicenseState::UNKNOWN; break; } } else { status = Status::BAD_VALUE; } *_aidl_return = licenseState; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::getPropertyByteArray(const std::string& in_propertyName, std::vector* _aidl_return) { std::map>::iterator itr = mByteArrayProperties.find(std::string(in_propertyName.c_str())); Status status = Status::OK; if (itr != mByteArrayProperties.end()) { *_aidl_return = itr->second; } else { ALOGE("App requested unknown property: %s", in_propertyName.c_str()); status = Status::BAD_VALUE; *_aidl_return = {}; } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::getPropertyString(const std::string& in_propertyName, std::string* _aidl_return) { std::string name(in_propertyName.c_str()); std::string value; Status status = Status::OK; if (name == kVendorKey) { value = mStringProperties[kVendorKey]; } else if (name == kVersionKey) { value = mStringProperties[kVersionKey]; } else if (name == kPluginDescriptionKey) { value = mStringProperties[kPluginDescriptionKey]; } else if (name == kAlgorithmsKey) { value = mStringProperties[kAlgorithmsKey]; } else if (name == kListenerTestSupportKey) { value = mStringProperties[kListenerTestSupportKey]; } else if (name == kDrmErrorTestKey) { value = mStringProperties[kDrmErrorTestKey]; } else if (name == kAidlVersionKey) { value = mStringProperties[kAidlVersionKey]; } else { ALOGE("App requested unknown string property %s", name.c_str()); status = Status::ERROR_DRM_CANNOT_HANDLE; } *_aidl_return = value; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::getProvisionRequest( const std::string& in_certificateType, const std::string& in_certificateAuthority, ::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) { UNUSED(in_certificateType); UNUSED(in_certificateAuthority); _aidl_return->request = {}; _aidl_return->defaultUrl = {}; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::getSecureStop( const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId, ::aidl::android::hardware::drm::SecureStop* _aidl_return) { std::vector stop = {}; mSecureStopLock.lock(); auto itr = mSecureStops.find(in_secureStopId.secureStopId); if (itr != mSecureStops.end()) { ClearkeySecureStop clearkeyStop = itr->second; stop.insert(stop.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); stop.insert(stop.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); } mSecureStopLock.unlock(); SecureStop secureStop = {}; Status status = Status::OK; if (!stop.empty()) { secureStop.opaqueData = stop; } else { status = Status::BAD_VALUE; } *_aidl_return = secureStop; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::getSecureStopIds( std::vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) { mSecureStopLock.lock(); std::vector<::aidl::android::hardware::drm::SecureStopId> ids; for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { SecureStopId id; id.secureStopId = itr->first; ids.push_back(id); } mSecureStopLock.unlock(); *_aidl_return = ids; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::getSecureStops( std::vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) { mSecureStopLock.lock(); std::vector<::aidl::android::hardware::drm::SecureStop> stops; for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { ClearkeySecureStop clearkeyStop = itr->second; std::vector stop{}; stop.insert(stop.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); stop.insert(stop.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); SecureStop secureStop; secureStop.opaqueData = stop; stops.push_back(secureStop); } mSecureStopLock.unlock(); *_aidl_return = stops; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::getSecurityLevel( const std::vector& in_sessionId, ::aidl::android::hardware::drm::SecurityLevel* _aidl_return) { if (in_sessionId.size() == 0) { *_aidl_return = ::aidl::android::hardware::drm::SecurityLevel::UNKNOWN; return toNdkScopedAStatus(Status::BAD_VALUE); } std::vector sid = in_sessionId; ::android::sp session = mSessionLibrary->findSession(sid); if (!session.get()) { *_aidl_return = SecurityLevel::UNKNOWN; return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } std::map, ::aidl::android::hardware::drm::SecurityLevel>::iterator itr = mSecurityLevel.find(sid); if (itr == mSecurityLevel.end()) { ALOGE("Session id not found"); *_aidl_return = SecurityLevel::UNKNOWN; return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); } *_aidl_return = SecurityLevel::SW_SECURE_CRYPTO; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::openSession( ::aidl::android::hardware::drm::SecurityLevel in_securityLevel, std::vector* _aidl_return) { ::android::sp session = mSessionLibrary->createSession(); processMockError(session); std::vector sessionId = session->sessionId(); Status status = setSecurityLevel(sessionId, in_securityLevel); if (status == Status::OK) { mOpenSessionOkCount++; } else { mSessionLibrary->destroySession(session); sessionId.clear(); } *_aidl_return = sessionId; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::provideKeyResponse( const std::vector& in_scope, const std::vector& in_response, ::aidl::android::hardware::drm::KeySetId* _aidl_return) { if (in_scope.size() == 0 || in_response.size() == 0) { // Returns empty keySetId *_aidl_return = {}; return toNdkScopedAStatus(Status::BAD_VALUE); } std::string responseString(reinterpret_cast(in_response.data()), in_response.size()); const std::vector scopeId = in_scope; std::vector sessionId = {}; std::string keySetId; bool isOfflineLicense = responseString.find(kOfflineLicense) != std::string::npos; if (scopeId.size() < kKeySetIdPrefix.size()) { android_errorWriteLog(0x534e4554, "144507096"); *_aidl_return = {}; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } bool isRelease = (memcmp(scopeId.data(), kKeySetIdPrefix.data(), kKeySetIdPrefix.size()) == 0); if (isRelease) { keySetId.assign(scopeId.begin(), scopeId.end()); auto iter = mReleaseKeysMap.find(std::string(keySetId.begin(), keySetId.end())); if (iter != mReleaseKeysMap.end()) { sessionId.assign(iter->second.begin(), iter->second.end()); } } else { sessionId.assign(scopeId.begin(), scopeId.end()); // non offline license returns empty keySetId keySetId.clear(); } ::android::sp session = mSessionLibrary->findSession(sessionId); if (!session.get()) { *_aidl_return = {}; return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } setPlayPolicy(); auto res = session->provideKeyResponse(in_response); if (res == clearkeydrm::OK) { if (isOfflineLicense) { if (isRelease) { mFileHandle.DeleteLicense(keySetId); mSessionLibrary->destroySession(session); } else { if (!makeKeySetId(&keySetId)) { *_aidl_return = {}; return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } bool ok = mFileHandle.StoreLicense( keySetId, DeviceFiles::kLicenseStateActive, std::string(in_response.begin(), in_response.end())); if (!ok) { ALOGE("Failed to store offline license"); } } } // Test calling AMediaDrm listeners. sendEvent(EventType::VENDOR_DEFINED, sessionId, sessionId); sendExpirationUpdate(sessionId, 100); std::vector keysStatus = {}; KeyStatus keyStatus; std::vector keyId1 = {0xA, 0xB, 0xC}; keyStatus.keyId = keyId1; keyStatus.type = KeyStatusType::USABLE; keysStatus.push_back(keyStatus); std::vector keyId2 = {0xD, 0xE, 0xF}; keyStatus.keyId = keyId2; keyStatus.type = KeyStatusType::EXPIRED; keysStatus.push_back(keyStatus); std::vector keyId3 = {0x0, 0x1, 0x2}; keyStatus.keyId = keyId3; keyStatus.type = KeyStatusType::USABLE_IN_FUTURE; keysStatus.push_back(keyStatus); sendKeysChange(sessionId, keysStatus, true); installSecureStop(sessionId); } else { ALOGE("provideKeyResponse returns error=%d", res); } std::vector keySetIdVec(keySetId.begin(), keySetId.end()); _aidl_return->keySetId = keySetIdVec; return toNdkScopedAStatus(res); } ::ndk::ScopedAStatus DrmPlugin::provideProvisionResponse( const std::vector& in_response, ::aidl::android::hardware::drm::ProvideProvisionResponseResult* _aidl_return) { Status status = Status::ERROR_DRM_CANNOT_HANDLE; _aidl_return->certificate = {}; _aidl_return->wrappedKey = {}; if (in_response.size() == 0) { status = Status::BAD_VALUE; } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::queryKeyStatus( const std::vector& in_sessionId, std::vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) { if (in_sessionId.size() == 0) { // Returns empty key status KeyValue pair *_aidl_return = {}; return toNdkScopedAStatus(Status::BAD_VALUE); } std::vector infoMap = {}; mPlayPolicyLock.lock(); KeyValue keyValuePair; for (size_t i = 0; i < mPlayPolicy.size(); ++i) { keyValuePair.key = mPlayPolicy[i].key; keyValuePair.value = mPlayPolicy[i].value; infoMap.push_back(keyValuePair); } mPlayPolicyLock.unlock(); *_aidl_return = infoMap; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::releaseAllSecureStops() { Status status = Status::OK; const auto res = removeAllSecureStops(); if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { status = static_cast(res.getServiceSpecificError()); } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::releaseSecureStop( const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { Status status = Status::OK; const auto res = removeSecureStop(in_secureStopId); if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { status = static_cast(res.getServiceSpecificError()); } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::releaseSecureStops( const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) { // OpaqueData starts with 4 byte decimal integer string const size_t kFourBytesOffset = 4; if (in_ssRelease.opaqueData.size() < kFourBytesOffset) { ALOGE("Invalid secureStopRelease length"); return toNdkScopedAStatus(Status::BAD_VALUE); } Status status = Status::OK; std::vector input = in_ssRelease.opaqueData; if (input.size() < kSecureStopIdSize + kFourBytesOffset) { // The minimum size of secure stop has to contain // a 4 bytes count and one secureStop id ALOGE("Total size of secureStops is too short"); return toNdkScopedAStatus(Status::BAD_VALUE); } // The format of opaqueData is shared between the server // and the drm service. The clearkey implementation consists of: // count - number of secure stops // list of fixed length secure stops size_t countBufferSize = sizeof(uint32_t); if (input.size() < countBufferSize) { // SafetyNet logging android_errorWriteLog(0x534e4554, "144766455"); return toNdkScopedAStatus(Status::BAD_VALUE); } uint32_t count = 0; sscanf(reinterpret_cast(input.data()), "%04" PRIu32, &count); // Avoid divide by 0 below. if (count == 0) { ALOGE("Invalid 0 secureStop count"); return toNdkScopedAStatus(Status::BAD_VALUE); } // Computes the fixed length secureStop size size_t secureStopSize = (input.size() - kFourBytesOffset) / count; if (secureStopSize < kSecureStopIdSize) { // A valid secureStop contains the id plus data ALOGE("Invalid secureStop size"); return toNdkScopedAStatus(Status::BAD_VALUE); } uint8_t* buffer = new uint8_t[secureStopSize]; size_t offset = kFourBytesOffset; // skip the count for (size_t i = 0; i < count; ++i, offset += secureStopSize) { memcpy(buffer, input.data() + offset, secureStopSize); // A secureStop contains id+data, we only use the id for removal std::vector id(buffer, buffer + kSecureStopIdSize); ::aidl::android::hardware::drm::SecureStopId secureStopId{id}; const auto res = removeSecureStop(secureStopId); if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { status = static_cast(res.getServiceSpecificError()); } if (Status::OK != status) break; } delete[] buffer; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::removeAllSecureStops() { Mutex::Autolock lock(mSecureStopLock); mSecureStops.clear(); mNextSecureStopId = kSecureStopIdStart; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::removeKeys(const std::vector& in_sessionId) { Status status = Status::ERROR_DRM_CANNOT_HANDLE; if (in_sessionId.size() == 0) { status = Status::BAD_VALUE; } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::removeOfflineLicense( const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { if (mMockError != Status::OK) { return toNdkScopedAStatus(toMockStatus(mMockError)); } Status status = Status::BAD_VALUE; std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); if (mFileHandle.DeleteLicense(licenseName)) { status = Status::OK; } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::removeSecureStop( const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { Mutex::Autolock lock(mSecureStopLock); Status status = Status::OK; if (1 != mSecureStops.erase(in_secureStopId.secureStopId)) { status = Status::BAD_VALUE; } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus DrmPlugin::requiresSecureDecoder( const std::string& in_mime, ::aidl::android::hardware::drm::SecurityLevel in_level, bool* _aidl_return) { UNUSED(in_mime); UNUSED(in_level); *_aidl_return = false; return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus DrmPlugin::restoreKeys( const std::vector& in_sessionId, const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { if (in_sessionId.size() == 0 || in_keySetId.keySetId.size() == 0) { return toNdkScopedAStatus(Status::BAD_VALUE); } DeviceFiles::LicenseState licenseState; std::string offlineLicense; if (!mFileHandle.RetrieveLicense( std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()), &licenseState, &offlineLicense)) { ALOGE("Failed to restore offline license"); return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE); } if (DeviceFiles::kLicenseStateUnknown == licenseState || DeviceFiles::kLicenseStateReleasing == licenseState) { ALOGE("Invalid license state=%d", licenseState); return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE); } ::android::sp session = mSessionLibrary->findSession(in_sessionId); if (!session.get()) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } auto res = session->provideKeyResponse( std::vector(offlineLicense.begin(), offlineLicense.end())); if (res != clearkeydrm::OK) { ALOGE("Failed to restore keys"); } return toNdkScopedAStatus(res); } void DrmPlugin::sendEvent(::aidl::android::hardware::drm::EventType in_eventType, const std::vector& in_sessionId, const std::vector& in_data) { if (mListener != nullptr) { mListener->onEvent(in_eventType, in_sessionId, in_data); } else { ALOGE("Null event listener, event not sent"); } return; } void DrmPlugin::sendExpirationUpdate(const std::vector& in_sessionId, int64_t in_expiryTimeInMS) { if (mListener != nullptr) { mListener->onExpirationUpdate(in_sessionId, in_expiryTimeInMS); } else { ALOGE("Null event listener, event not sent"); } return; } void DrmPlugin::sendKeysChange( const std::vector& in_sessionId, const std::vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList, bool in_hasNewUsableKey) { if (mListener != nullptr) { mListener->onKeysChange(in_sessionId, in_keyStatusList, in_hasNewUsableKey); } else { ALOGE("Null event listener, event not sent"); } return; } void DrmPlugin::sendSessionLostState(const std::vector& in_sessionId) { if (mListener != nullptr) { mListener->onSessionLostState(in_sessionId); } return; } ::ndk::ScopedAStatus DrmPlugin::setCipherAlgorithm(const std::vector& /*in_sessionId*/, const std::string& /*in_algorithm*/) { return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::setListener( const std::shared_ptr< ::aidl::android::hardware::drm::IDrmPluginListener>& in_listener) { mListener = in_listener; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::setMacAlgorithm(const std::vector& /*in_sessionId*/, const std::string& /*in_algorithm*/) { return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::setPlaybackId(const std::vector& in_sessionId, const std::string& in_playbackId) { if (in_sessionId.size() == 0) { ALOGE("Invalid empty session id"); return toNdkScopedAStatus(Status::BAD_VALUE); } std::vector sid = in_sessionId; mPlaybackId[sid] = in_playbackId; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::setPropertyByteArray(const std::string& in_propertyName, const std::vector& in_value) { if (in_propertyName == kDeviceIdKey) { ALOGD("Cannot set immutable property: %s", in_propertyName.c_str()); return toNdkScopedAStatus(Status::BAD_VALUE); } else if (in_propertyName == kClientIdKey) { mByteArrayProperties[kClientIdKey] = in_value; return toNdkScopedAStatus(Status::OK); } // Setting of undefined properties is not supported ALOGE("Failed to set property byte array, key=%s", in_propertyName.c_str()); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::setPropertyString(const std::string& in_propertyName, const std::string& in_value) { std::string immutableKeys; immutableKeys.append(kAlgorithmsKey + ","); immutableKeys.append(kPluginDescriptionKey + ","); immutableKeys.append(kVendorKey + ","); immutableKeys.append(kVersionKey + ","); std::string key = std::string(in_propertyName.c_str()); if (immutableKeys.find(key) != std::string::npos) { ALOGD("Cannot set immutable property: %s", key.c_str()); return toNdkScopedAStatus(Status::BAD_VALUE); } std::map::iterator itr = mStringProperties.find(key); if (itr == mStringProperties.end()) { ALOGE("Cannot set undefined property string, key=%s", key.c_str()); return toNdkScopedAStatus(Status::BAD_VALUE); } if (in_propertyName == kDrmErrorTestKey) { if (in_value == kResourceContentionValue) { mMockError = Status::ERROR_DRM_RESOURCE_CONTENTION; } else if (in_value == kLostStateValue) { mMockError = Status::ERROR_DRM_SESSION_LOST_STATE; } else if (in_value == kFrameTooLargeValue) { mMockError = Status::ERROR_DRM_FRAME_TOO_LARGE; } else if (in_value == kInvalidStateValue) { mMockError = Status::ERROR_DRM_INVALID_STATE; } else { mMockError = Status::ERROR_DRM_UNKNOWN; } } mStringProperties[key] = std::string(in_value.c_str()); return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus DrmPlugin::sign(const std::vector& /*in_sessionId*/, const std::vector& /*in_keyId*/, const std::vector& /*in_message*/, std::vector* _aidl_return) { *_aidl_return = {}; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::signRSA(const std::vector& /*in_sessionId*/, const std::string& /*in_algorithm*/, const std::vector& /*in_message*/, const std::vector& /*in_wrappedkey*/, std::vector* _aidl_return) { *_aidl_return = {}; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus DrmPlugin::verify(const std::vector& /*in_sessionId*/, const std::vector& /*in_keyId*/, const std::vector& /*in_message*/, const std::vector& /*in_signature*/, bool* _aidl_return) { *_aidl_return = false; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } // Private methods below. void DrmPlugin::setPlayPolicy() { ::android::Mutex::Autolock lock(mPlayPolicyLock); mPlayPolicy.clear(); KeyValue policy; policy.key = kQueryKeyLicenseType; policy.value = kStreaming; mPlayPolicy.push_back(policy); policy.key = kQueryKeyPlayAllowed; policy.value = kTrue; mPlayPolicy.push_back(policy); policy.key = kQueryKeyRenewAllowed; mPlayPolicy.push_back(policy); } bool DrmPlugin::makeKeySetId(std::string* keySetId) { if (!keySetId) { ALOGE("keySetId destination not provided"); return false; } std::vector ksid(kKeySetIdPrefix.begin(), kKeySetIdPrefix.end()); ksid.resize(kKeySetIdLength); std::vector randomData((kKeySetIdLength - kKeySetIdPrefix.size()) / 2, 0); while (keySetId->empty()) { for (auto itr = randomData.begin(); itr != randomData.end(); ++itr) { *itr = std::rand() % 0xff; } auto id = reinterpret_cast(randomData.data()); *keySetId = kKeySetIdPrefix + ::android::ByteArrayToHexString(id, randomData.size()); if (mFileHandle.LicenseExists(*keySetId)) { // collision, regenerate ALOGV("Retry generating KeySetId"); keySetId->clear(); } } return true; } Status DrmPlugin::setSecurityLevel(const std::vector& sessionId, SecurityLevel level) { if (sessionId.size() == 0) { ALOGE("Invalid empty session id"); return Status::BAD_VALUE; } if (level != SecurityLevel::DEFAULT && level != SecurityLevel::SW_SECURE_CRYPTO) { ALOGE("Cannot set security level > max"); return Status::ERROR_DRM_CANNOT_HANDLE; } std::vector sid = sessionId; ::android::sp session = mSessionLibrary->findSession(sid); if (!session.get()) { return Status::ERROR_DRM_SESSION_NOT_OPENED; } std::map, SecurityLevel>::iterator itr = mSecurityLevel.find(sid); if (itr != mSecurityLevel.end()) { mSecurityLevel[sid] = level; } else { if (!mSecurityLevel.insert(std::pair, SecurityLevel>(sid, level)) .second) { ALOGE("Failed to set security level"); return Status::ERROR_DRM_INVALID_STATE; } } return Status::OK; } } // namespace clearkey } // namespace drm } // namespace hardware } // namespace android } // namespace aidl