// // Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. // // #define LOG_NDEBUG 0 #define LOG_TAG "WVCdm" #include "WVDrmPlugin.h" #include #include #include #include #include #include #include #include #include #include #include "Utils.h" #include "android-base/macros.h" #include "log.h" #include "mapErrors-inl.h" #include "media/stagefright/MediaErrors.h" #include "openssl/sha.h" #include "string_conversions.h" #include "widevine_apex_info.h" #include "wv_cdm_constants.h" #include "wv_metrics.pb.h" #include "wv_metrics_adapter.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"; static const std::string kKeyAppPackageName = "application_name"; static const std::string kKeyOrigin = "origin"; } // namespace namespace wvdrm { namespace hardware { namespace drm { namespace widevine { using std::shared_ptr; using std::string; using std::vector; using ::aidl::android::hardware::drm::DrmMetric; using ::aidl::android::hardware::drm::DrmMetricGroup; using ::aidl::android::hardware::drm::DrmMetricNamedValue; using ::aidl::android::hardware::drm::DrmMetricValue; using ::aidl::android::hardware::drm::EventType; using ::aidl::android::hardware::drm::HdcpLevel; using ::aidl::android::hardware::drm::KeyRequestType; using ::aidl::android::hardware::drm::KeySetId; using ::aidl::android::hardware::drm::KeyStatus; using ::aidl::android::hardware::drm::KeyStatusType; using ::aidl::android::hardware::drm::KeyType; using ::aidl::android::hardware::drm::KeyValue; using ::aidl::android::hardware::drm::OfflineLicenseState; using ::aidl::android::hardware::drm::SecureStop; using ::aidl::android::hardware::drm::SecureStopId; using ::aidl::android::hardware::drm::SecurityLevel; using ::aidl::android::hardware::drm::Status; using wvcdm::CdmAppParameterMap; using wvcdm::CdmCertificateType; using wvcdm::CdmInitData; using wvcdm::CdmKeyRequest; using wvcdm::CdmKeyRequestType; using wvcdm::CdmKeyResponse; using wvcdm::CdmKeySetId; using wvcdm::CdmKeyStatus; using wvcdm::CdmLicenseType; using wvcdm::CdmProvisioningRequest; using wvcdm::CdmProvisioningResponse; using wvcdm::CdmQueryMap; using wvcdm::CdmSecureStopId; using wvcdm::CdmSecurityLevel; using wvcdm::CdmUsageReport; using wvcdm::CdmUsageReportList; using wvcdm::kDefaultCdmIdentifier; using wvcdm::KeyId; using wvcdm::RequestedSecurityLevel; namespace { vector StrToVector(const std::string& str) { vector 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; case wvcdm::kKeyRequestTypeNone: return KeyRequestType::NONE; case wvcdm::kKeyRequestTypeUpdate: return KeyRequestType::UPDATE; 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::OUTPUT_NOT_ALLOWED; case wvcdm::kKeyStatusPending: case wvcdm::kKeyStatusUsableInFuture: return KeyStatusType::USABLE_IN_FUTURE; case wvcdm::kKeyStatusInternalError: default: return KeyStatusType::INTERNAL_ERROR; } } HdcpLevel mapHdcpLevel(const std::string& level) { if (level == wvcdm::QUERY_VALUE_HDCP_V1_X || level == wvcdm::QUERY_VALUE_HDCP_V1_0 || level == wvcdm::QUERY_VALUE_HDCP_V1_1 || level == wvcdm::QUERY_VALUE_HDCP_V1_2 || level == wvcdm::QUERY_VALUE_HDCP_V1_3 || level == wvcdm::QUERY_VALUE_HDCP_V1_4) return HdcpLevel::HDCP_V1; else if (level == wvcdm::QUERY_VALUE_HDCP_V2_0) return HdcpLevel::HDCP_V2; else if (level == wvcdm::QUERY_VALUE_HDCP_V2_1) return HdcpLevel::HDCP_V2_1; else if (level == wvcdm::QUERY_VALUE_HDCP_V2_2) return HdcpLevel::HDCP_V2_2; else if (level == wvcdm::QUERY_VALUE_HDCP_V2_3) return HdcpLevel::HDCP_V2_3; else if (level == wvcdm::QUERY_VALUE_HDCP_NONE) return HdcpLevel::HDCP_NONE; else if (level == wvcdm::QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT) return HdcpLevel::HDCP_NO_OUTPUT; else { ALOGE("Invalid HDCP level=%s", level.c_str()); return HdcpLevel::HDCP_NONE; } } bool isCsrAccessAllowed() { const uid_t AID_ROOT = 0; const uid_t AID_SYSTEM = 1000; const uid_t AID_SHELL = 2000; const uid_t uid = AIBinder_getCallingUid(); return (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL); } bool IsAtscKeySetId(const CdmKeySetId& keySetId) { if (keySetId.empty()) return false; // Pre-installed licenses might not perfectly match ATSC_KEY_SET_ID_PREFIX. // If "atsc" is in the license name, then it is safe to assume // it is an ATSC license. return keySetId.find("atsc") != std::string::npos || keySetId.find("ATSC") != std::string::npos; } bool IsNotAtscKeySetId(const CdmKeySetId& keySetId) { return !IsAtscKeySetId(keySetId); } } // namespace WVDrmPlugin::WVDrmPlugin(const android::sp& cdm, const std::string& appPackageName, WVGenericCryptoInterface* crypto, bool useSpoid) : mCdmIdentifierBuilder(useSpoid, *this, appPackageName), mCDM(cdm), mCrypto(crypto), mAppPackageName(appPackageName) { Terminator::Register(this); } WVDrmPlugin::~WVDrmPlugin() { wvutil::SetLoggingUid(mCdmIdentifierBuilder.user_id()); Terminator::Forget(this); Close(); } void WVDrmPlugin::Close() { const auto sessionKeys = mSessionInfoMap.getKeysAndClear(); for (const auto& sessionKey : sessionKeys) { const CdmResponseType res = mCDM->CloseSession(sessionKey); if (!isCdmResponseTypeSuccess(res)) { ALOGE("Failed to close session while destroying WVDrmPlugin: sid = %s", sessionKey.c_str()); } } if (mCdmIdentifierBuilder.is_sealed()) { CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { ALOGE("Failed to get cdm identifier %d", status.get()); } else { status = mapCdmResponseType(mCDM->CloseCdm(identifier)); if (status != Status::OK) { ALOGE("Failed to close cdm. status %d", status.get()); } } } } WvStatus WVDrmPlugin::openSessionCommon(vector& sessionId) { WvStatus status(Status::OK); CdmIdentifier identifier; status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return status; } CdmSessionId cdmSessionId; CdmResponseType res = mCDM->OpenSession("com.widevine", &mPropertySet, identifier, this, &cdmSessionId); if (!isCdmResponseTypeSuccess(res)) { return mapAndNotifyOfCdmResponseType(sessionId, res); } bool success = false; // Construct a SessionInfo CdmQueryMap info; res = mCDM->QueryOemCryptoSessionId(cdmSessionId, &info); if (isCdmResponseTypeSuccess(res) && info.count(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID)) { const OEMCrypto_SESSION oecSessionId = std::stoul(info[wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID]); mSessionInfoMap.insert(cdmSessionId, oecSessionId); success = true; } else { ALOGE("Unable to query key control info."); } if (success) { // Marshal Session ID sessionId = StrToVector(cdmSessionId); return WvStatus(Status::OK); } else { mCDM->CloseSession(cdmSessionId); if (!isCdmResponseTypeSuccess(res)) { // We got an error code we can return. return 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"); return WvStatus(Status::ERROR_DRM_UNKNOWN); } } } SecurityLevel WVDrmPlugin::mapSecurityLevel(const std::string& level) { SecurityLevel securityLevel = SecurityLevel::UNKNOWN; if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1 == level) { securityLevel = SecurityLevel::HW_SECURE_ALL; } else if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L2 == level) { securityLevel = SecurityLevel::HW_SECURE_CRYPTO; } else if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 == level) { securityLevel = SecurityLevel::SW_SECURE_CRYPTO; } // else QUERY_VALUE_SECURITY_LEVEL_UNKNOWN returns Security::UNKNOWN return securityLevel; } ::ndk::ScopedAStatus WVDrmPlugin::openSession(SecurityLevel in_securityLevel, vector* _aidl_return) { vector sessionId; if (SecurityLevel::DEFAULT == in_securityLevel) { auto err = openSessionCommon(sessionId); *_aidl_return = sessionId; return toNdkScopedAStatus(err); } if (SecurityLevel::UNKNOWN == in_securityLevel) { *_aidl_return = sessionId; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } std::string native_security_level; auto status = queryProperty(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL, native_security_level); if (status != Status::OK) { *_aidl_return = sessionId; return toNdkScopedAStatus(status); } if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 == native_security_level && in_securityLevel >= SecurityLevel::SW_SECURE_DECODE) { *_aidl_return = sessionId; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } std::string wvcdm_security_level = (SecurityLevel::SW_SECURE_CRYPTO == in_securityLevel) ? wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 : wvcdm::QUERY_VALUE_SECURITY_LEVEL_DEFAULT; setPropertyString("securityLevel", std::string(wvcdm_security_level)); status = openSessionCommon(sessionId); if (status == Status::OK) { SecurityLevel currentSecurityLevel = SecurityLevel::UNKNOWN; const auto ret = getSecurityLevel(sessionId, ¤tSecurityLevel); if (!ret.isOk() || in_securityLevel != currentSecurityLevel) { ALOGE("Failed to open session with the requested security level=%d", in_securityLevel); closeSession(sessionId); sessionId.clear(); status = WvStatus(Status::ERROR_DRM_INVALID_STATE); } } *_aidl_return = sessionId; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus WVDrmPlugin::closeSession( const vector& in_sessionId) { if (!in_sessionId.size()) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmResponseType res = mCDM->CloseSession(cdmSessionId); mSessionInfoMap.erase(cdmSessionId); if (!isCdmResponseTypeSuccess(res)) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::getKeyRequest( const vector& in_scope, const vector& in_initData, const std::string& in_mimeType, ::aidl::android::hardware::drm::KeyType in_keyType, const vector<::aidl::android::hardware::drm::KeyValue>& in_optionalParameters, ::aidl::android::hardware::drm::KeyRequest* _aidl_return) { if (!in_scope.size()) { _aidl_return->request = vector(); _aidl_return->requestType = KeyRequestType::UNKNOWN; _aidl_return->defaultUrl = ""; return toNdkScopedAStatus(Status::BAD_VALUE); } KeyRequestType requestType = KeyRequestType::UNKNOWN; WvStatus status(Status::OK); std::string defaultUrl; vector request; CdmIdentifier identifier; status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { _aidl_return->request = request; _aidl_return->requestType = KeyRequestType::UNKNOWN; _aidl_return->defaultUrl = ""; return toNdkScopedAStatus(status); } CdmLicenseType cdmLicenseType; CdmSessionId cdmSessionId; CdmKeySetId cdmKeySetId; if (in_keyType == KeyType::OFFLINE) { cdmLicenseType = wvcdm::kLicenseTypeOffline; cdmSessionId.assign(in_scope.begin(), in_scope.end()); } else if (in_keyType == KeyType::STREAMING) { cdmLicenseType = wvcdm::kLicenseTypeStreaming; cdmSessionId.assign(in_scope.begin(), in_scope.end()); } else if (in_keyType == KeyType::RELEASE) { cdmLicenseType = wvcdm::kLicenseTypeRelease; cdmKeySetId.assign(in_scope.begin(), in_scope.end()); } else { _aidl_return->request = request; _aidl_return->requestType = KeyRequestType::UNKNOWN; _aidl_return->defaultUrl = ""; return toNdkScopedAStatus(Status::BAD_VALUE); } std::string cdmInitDataType = in_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 (in_initData.size() > 0 && WvContentDecryptionModule::IsCenc(cdmInitDataType) && !initDataResemblesPSSH(in_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(psshPrefix), sizeof(psshPrefix) / sizeof(uint8_t)); processedInitData.append(reinterpret_cast(in_initData.data()), in_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(in_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(in_initData.data()), in_initData.size()); } CdmAppParameterMap cdmParameters; for (size_t i = 0; i < in_optionalParameters.size(); ++i) { const std::string& key(in_optionalParameters[i].key); const std::string& value(in_optionalParameters[i].value); std::string cdmKey(key.c_str(), key.size()); std::string cdmValue(value.c_str(), value.size()); cdmParameters[cdmKey] = cdmValue; } // Inserting additional client ID parameters here, this will appear // in the license request. // Note: This will overwrite user parameters of the same key. cdmParameters[kKeyAppPackageName] = mAppPackageName; cdmParameters[kKeyOrigin] = mCdmIdentifierBuilder.origin(); 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 (in_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(in_scope, res); } _aidl_return->request = request; _aidl_return->requestType = requestType; _aidl_return->defaultUrl = defaultUrl; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus WVDrmPlugin::provideKeyResponse( const vector& in_scope, const vector& in_response, ::aidl::android::hardware::drm::KeySetId* _aidl_return) { if (in_scope.size() == 0 || in_response.size() == 0) { *_aidl_return = {}; return toNdkScopedAStatus(Status::BAD_VALUE); } CdmKeySetId cdmKeySetId; CdmSessionId cdmSessionId; CdmKeyResponse cdmResponse(in_response.begin(), in_response.end()); bool isRequest = (memcmp(in_scope.data(), wvcdm::SESSION_ID_PREFIX, sizeof(wvcdm::SESSION_ID_PREFIX) - 1) == 0); bool isRelease = (memcmp(in_scope.data(), wvcdm::KEY_SET_ID_PREFIX, sizeof(wvcdm::KEY_SET_ID_PREFIX) - 1) == 0); vector keySetId; if (isRequest) { cdmSessionId.assign(in_scope.begin(), in_scope.end()); } else if (isRelease) { cdmKeySetId.assign(in_scope.begin(), in_scope.end()); } else { _aidl_return->keySetId = keySetId; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, &cdmKeySetId); if (isRequest && isCdmResponseTypeSuccess(res)) { keySetId = StrToVector(cdmKeySetId); } WvStatus 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(in_scope, res); // For "NEED_KEY," we still want to send the notification, 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 = WvStatus(Status::OK); } } _aidl_return->keySetId = keySetId; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus WVDrmPlugin::removeKeys( const vector& in_sessionId) { if (!in_sessionId.size()) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmResponseType res = mCDM->RemoveKeys(cdmSessionId); return toNdkScopedAStatus(mapAndNotifyOfCdmResponseType(in_sessionId, res)); } ::ndk::ScopedAStatus WVDrmPlugin::restoreKeys( const vector& in_sessionId, const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { if (!in_sessionId.size() || !in_keySetId.keySetId.size()) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmKeySetId cdmKeySetId(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); CdmResponseType res = mCDM->RestoreKey(cdmSessionId, cdmKeySetId); return toNdkScopedAStatus(mapAndNotifyOfCdmResponseType(in_sessionId, res)); } ::ndk::ScopedAStatus WVDrmPlugin::queryKeyStatus( const vector& in_sessionId, vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) { if (in_sessionId.size() == 0) { *_aidl_return = vector(); return toNdkScopedAStatus(Status::BAD_VALUE); } CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmQueryMap cdmLicenseInfo; CdmResponseType res = mCDM->QueryKeyStatus(cdmSessionId, &cdmLicenseInfo); vector 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); } } *_aidl_return = infoMapVec; return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::getProvisionRequest( const std::string& in_certificateType, const std::string& in_certificateAuthority, ::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) { WvStatus status(Status::OK); std::string defaultUrl; vector request; if (mPropertySet.use_atsc_mode()) { _aidl_return->defaultUrl = defaultUrl; _aidl_return->request = request; return toNdkScopedAStatus( mapCdmResponseType(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC)); } CdmIdentifier identifier; status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { _aidl_return->defaultUrl = defaultUrl; _aidl_return->request = request; return toNdkScopedAStatus(status); } CdmProvisioningRequest cdmProvisionRequest; std::string cdmDefaultUrl; CdmCertificateType cdmCertType = wvcdm::kCertificateWidevine; if (in_certificateType == "X.509") { cdmCertType = wvcdm::kCertificateX509; } std::string cdmCertAuthority = in_certificateAuthority; CdmResponseType res = mCDM->GetProvisioningRequest( cdmCertType, cdmCertAuthority, identifier, mProvisioningServiceCertificate, getRequestedSecurityLevel(), &cdmProvisionRequest, &cdmDefaultUrl); if (isCdmResponseTypeSuccess(res)) { request = StrToVector(cdmProvisionRequest); defaultUrl.clear(); defaultUrl.assign(cdmDefaultUrl.data(), cdmDefaultUrl.size()); } _aidl_return->defaultUrl = defaultUrl; _aidl_return->request = request; return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::provideProvisionResponse( const vector& in_response, ::aidl::android::hardware::drm::ProvideProvisionResponseResult* _aidl_return) { if (!in_response.size()) { _aidl_return->certificate = vector(); _aidl_return->wrappedKey = vector(); return toNdkScopedAStatus(Status::BAD_VALUE); } vector certificate; vector wrappedKey; CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { _aidl_return->certificate = certificate; _aidl_return->wrappedKey = wrappedKey; return toNdkScopedAStatus(status); } CdmProvisioningResponse cdmResponse(in_response.begin(), in_response.end()); if (cdmResponse == kSpecialUnprovisionResponse) { if (identifier.IsEquivalentToDefault()) { ALOGW("Returns UNKNOWN error for legacy status kErrorNoOriginSpecified"); _aidl_return->certificate = certificate; _aidl_return->wrappedKey = wrappedKey; return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } _aidl_return->certificate = certificate; _aidl_return->wrappedKey = wrappedKey; return toNdkScopedAStatus(unprovision(identifier)); } else { std::string cdmCertificate; std::string cdmWrappedKey; CdmResponseType res = mCDM->HandleProvisioningResponse( identifier, cdmResponse, getRequestedSecurityLevel(), &cdmCertificate, &cdmWrappedKey); if (isCdmResponseTypeSuccess(res)) { certificate = StrToVector(cdmCertificate); wrappedKey = StrToVector(cdmWrappedKey); } _aidl_return->certificate = certificate; _aidl_return->wrappedKey = wrappedKey; return toNdkScopedAStatus(mapCdmResponseType(res)); } } Status WVDrmPlugin::unprovisionDevice() { return unprovision(kDefaultCdmIdentifier).get(); } ::ndk::ScopedAStatus WVDrmPlugin::getSecureStop( const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId, ::aidl::android::hardware::drm::SecureStop* _aidl_return) { *_aidl_return = SecureStop(); if (in_secureStopId.secureStopId.empty()) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } const CdmSecureStopId cdmSsId(in_secureStopId.secureStopId.begin(), in_secureStopId.secureStopId.end()); CdmUsageReport cdmUsageReport; CdmResponseType res = mCDM->GetUsageInfo(mPropertySet.app_id(), cdmSsId, identifier, &cdmUsageReport); SecureStop secureStop; if (isCdmResponseTypeSuccess(res)) { secureStop.opaqueData = StrToVector(cdmUsageReport); *_aidl_return = std::move(secureStop); } return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::getSecureStops( vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) { _aidl_return->clear(); CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } CdmUsageReportList cdmUsageReports; const CdmResponseType res = mCDM->GetUsageInfo(mPropertySet.app_id(), identifier, &cdmUsageReports); vector secureStops; if (isCdmResponseTypeSuccess(res)) { for (const CdmUsageReport& cdmUsageReport : cdmUsageReports) { SecureStop secureStop; secureStop.opaqueData = StrToVector(cdmUsageReport); secureStops.push_back(std::move(secureStop)); } } *_aidl_return = std::move(secureStops); return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::releaseAllSecureStops() { const auto res = removeAllSecureStops(); Status status = Status::OK; if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { status = static_cast(res.getServiceSpecificError()); } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus WVDrmPlugin::releaseSecureStop( const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { if (!in_secureStopId.secureStopId.size()) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } const CdmKeyResponse cdmMessage(in_secureStopId.secureStopId.begin(), in_secureStopId.secureStopId.end()); CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier); return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::getMetrics( vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) { _aidl_return->clear(); CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } drm_metrics::WvCdmMetrics proto_metrics; const CdmResponseType result = mCDM->GetCurrentMetrics(identifier, &proto_metrics); if (result != wvcdm::NO_ERROR) { return toNdkScopedAStatus(mapCdmResponseType(result)); } vector wvMetrics; ::wvcdm::WvMetricsAdapter adapter; ::wvcdm::WvMetricsAdapter::ToWvMetrics(proto_metrics, &wvMetrics); *_aidl_return = std::move(wvMetrics); return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::getSecureStopIds( vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) { vector secureStopIds; CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { *_aidl_return = secureStopIds; return toNdkScopedAStatus(status); } vector ssids; CdmResponseType res = mCDM->GetSecureStopIds(mPropertySet.app_id(), identifier, &ssids); if (isCdmResponseTypeSuccess(res)) { for (auto itr = ssids.begin(); itr != ssids.end(); ++itr) { const CdmSecureStopId& cdmSsid = *itr; SecureStopId ssid; ssid.secureStopId = StrToVector(cdmSsid); secureStopIds.push_back(ssid); } } *_aidl_return = secureStopIds; return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::releaseSecureStops( const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) { if (in_ssRelease.opaqueData.size() == 0) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } const vector& data = in_ssRelease.opaqueData; const CdmKeyResponse cdmMessage(data.begin(), data.end()); // Only releases a single secure stop. const CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier); return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::removeSecureStop( const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { if (!in_secureStopId.secureStopId.size()) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } const CdmSecureStopId ssid(in_secureStopId.secureStopId.begin(), in_secureStopId.secureStopId.end()); const CdmResponseType res = mCDM->RemoveUsageInfo(mPropertySet.app_id(), identifier, ssid); return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::removeAllSecureStops() { CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } const CdmResponseType res = mCDM->RemoveAllUsageInfo(mPropertySet.app_id(), identifier); return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::getHdcpLevels( ::aidl::android::hardware::drm::HdcpLevels* _aidl_return) { HdcpLevel connectedLevel = HdcpLevel::HDCP_NONE; HdcpLevel maxLevel = HdcpLevel::HDCP_NO_OUTPUT; std::string level; auto status = queryProperty(wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, level); if (status == Status::OK) { connectedLevel = mapHdcpLevel(level); } else { ALOGE("Failed to query current hdcp level."); _aidl_return->connectedLevel = connectedLevel; _aidl_return->maxLevel = maxLevel; return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); } status = queryProperty(wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, level); if (status == Status::OK) { maxLevel = mapHdcpLevel(level); } else { ALOGE("Failed to query maximum hdcp level."); _aidl_return->connectedLevel = connectedLevel; _aidl_return->maxLevel = maxLevel; return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); } _aidl_return->connectedLevel = connectedLevel; _aidl_return->maxLevel = maxLevel; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::getNumberOfSessions( ::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) { uint32_t currentSessions = 0; uint32_t maxSessions = 1; std::string value; auto status = queryProperty(wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, value); if (status == Status::OK) { currentSessions = std::strtoul(value.c_str(), nullptr, 10); } else { ALOGE("Failed to query currently opened sessions."); _aidl_return->currentSessions = currentSessions; _aidl_return->maxSessions = maxSessions; return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); } status = queryProperty(wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, value); if (status == Status::OK) { maxSessions = std::strtoul(value.c_str(), nullptr, 10); } else { ALOGE( "Failed to query maximum number of sessions that the device can " "support."); _aidl_return->currentSessions = currentSessions; _aidl_return->maxSessions = maxSessions; return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); } _aidl_return->currentSessions = currentSessions; _aidl_return->maxSessions = maxSessions; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::getSecurityLevel( const vector& in_sessionId, ::aidl::android::hardware::drm::SecurityLevel* _aidl_return) { if (in_sessionId.size() == 0) { *_aidl_return = SecurityLevel::UNKNOWN; return toNdkScopedAStatus(Status::BAD_VALUE); } CdmQueryMap info; SecurityLevel securityLevel = SecurityLevel::UNKNOWN; CdmResponseType status = mCDM->QuerySessionStatus( std::string(in_sessionId.begin(), in_sessionId.end()), &info); if (wvcdm::NO_ERROR == status) { std::string level = info[wvcdm::QUERY_KEY_SECURITY_LEVEL]; securityLevel = mapSecurityLevel(level); } else { ALOGE("Failed to query security level, status=%d", static_cast(status)); } *_aidl_return = securityLevel; return toNdkScopedAStatus(mapCdmResponseType(status)); } ::ndk::ScopedAStatus WVDrmPlugin::getOfflineLicenseKeySetIds( vector<::aidl::android::hardware::drm::KeySetId>* keySetIds) { keySetIds->clear(); CdmIdentifier identifier; const auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } std::vector levelsToList; if (mPropertySet.security_level() != wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) { // Do not list L1 offline licenses if the DRM plugin is in // L3-only mode. levelsToList.push_back(wvcdm::kSecurityLevelL1); } // Always list L3, as "default" may imply either. levelsToList.push_back(wvcdm::kSecurityLevelL3); std::vector allKeySetIds; CdmResponseType res(wvcdm::UNKNOWN_ERROR); bool success = false; for (const auto& level : levelsToList) { std::vector levelKeySetIds; res = mCDM->ListStoredLicenses(level, identifier, &levelKeySetIds); if (!isCdmResponseTypeSuccess(res)) continue; success = true; if (levelKeySetIds.empty()) continue; if (allKeySetIds.empty()) { allKeySetIds = std::move(levelKeySetIds); } else { allKeySetIds.reserve(allKeySetIds.size() + levelKeySetIds.size()); for (CdmKeySetId& keySetId : levelKeySetIds) { allKeySetIds.push_back(std::move(keySetId)); } } } if (!success) { // Return whatever the last error was. return toNdkScopedAStatus(mapCdmResponseType(res)); } // Filter out key sets based on ATSC mode. const auto isAllowedKeySetId = mPropertySet.use_atsc_mode() ? IsAtscKeySetId : IsNotAtscKeySetId; keySetIds->reserve(allKeySetIds.size()); for (const CdmKeySetId& keySetId : allKeySetIds) { if (isAllowedKeySetId(keySetId)) { keySetIds->push_back(KeySetId{StrToVector(keySetId)}); } } return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus WVDrmPlugin::getOfflineLicenseState( const ::aidl::android::hardware::drm::KeySetId& in_keySetId, ::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) { OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN; if (!in_keySetId.keySetId.size()) { *_aidl_return = licenseState; return toNdkScopedAStatus(Status::BAD_VALUE); } CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { *_aidl_return = licenseState; return toNdkScopedAStatus(status); } CdmResponseType res = wvcdm::CdmResponseType(wvcdm::UNKNOWN_ERROR); CdmKeySetId keySetIdStr(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); wvcdm::CdmOfflineLicenseState state = wvcdm::kLicenseStateUnknown; res = mCDM->GetOfflineLicenseState(keySetIdStr, wvcdm::kSecurityLevelL1, identifier, &state); if (!isCdmResponseTypeSuccess(res)) { // try L3 res = mCDM->GetOfflineLicenseState(keySetIdStr, wvcdm::kSecurityLevelL3, identifier, &state); if (!isCdmResponseTypeSuccess(res)) { *_aidl_return = licenseState; return toNdkScopedAStatus(Status::BAD_VALUE); } } switch (state) { case wvcdm::kLicenseStateActive: licenseState = OfflineLicenseState::USABLE; break; case wvcdm::kLicenseStateReleasing: licenseState = OfflineLicenseState::INACTIVE; break; default: licenseState = OfflineLicenseState::UNKNOWN; ALOGE("Return unknown offline license state for %s", keySetIdStr.c_str()); break; } *_aidl_return = licenseState; return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::removeOfflineLicense( const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { if (in_keySetId.keySetId.empty()) { return toNdkScopedAStatus(Status::BAD_VALUE); } CdmIdentifier identifier; const auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { return toNdkScopedAStatus(status); } const std::vector levels = {wvcdm::kSecurityLevelL1, wvcdm::kSecurityLevelL3}; const CdmKeySetId cdmKeySetId(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); for (const CdmSecurityLevel level : levels) { std::vector keySetIds; const CdmResponseType res = mCDM->ListStoredLicenses(level, identifier, &keySetIds); if (!isCdmResponseTypeSuccess(res)) { // This could failure for several reasons, but none that are // worth returning to the app at this time. ALOGW("Failed to list stored licenses: res = %d", static_cast(res)); continue; } // Check if exists. if (keySetIds.empty() || std::find(keySetIds.begin(), keySetIds.end(), cdmKeySetId) == keySetIds.end()) { // Does not exist for this security level. continue; } return toNdkScopedAStatus(mapCdmResponseType( mCDM->RemoveOfflineLicense(cdmKeySetId, level, identifier))); } // Could only reach this state if the key set could not be found. return toNdkScopedAStatus(Status::BAD_VALUE); } ::ndk::ScopedAStatus WVDrmPlugin::getPropertyString( const std::string& in_propertyName, std::string* _aidl_return) { WvStatus status(Status::OK); std::string name(in_propertyName.c_str()); std::string value; if (name == "vendor") { #ifdef __ANDROID_APEX__ auto info = ::widevine::apex::GetApexInfo(); if (info.ok()) { value = info->name; } else { auto err = info.error(); ALOGW("apex info error %d: %s", err.code().value(), err.message().c_str()); value = "Google"; } #else value = "Google"; #endif } else if (name == "version") { status = queryProperty(wvcdm::QUERY_KEY_WVCDM_VERSION, value); } 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 if (name == "CurrentSRMVersion") { status = queryProperty(wvcdm::QUERY_KEY_CURRENT_SRM_VERSION, value); } else if (name == "SRMUpdateSupport") { status = queryProperty(wvcdm::QUERY_KEY_SRM_UPDATE_SUPPORT, value); } else if (name == "resourceRatingTier") { status = queryProperty(wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, value); } else if (name == "oemCryptoBuildInformation") { status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value); } else if (name == "decryptHashSupport") { status = queryProperty(wvcdm::QUERY_KEY_DECRYPT_HASH_SUPPORT, value); } else if (name == "decryptHashError") { status = mapCdmResponseType( mCDM->GetDecryptHashError(mDecryptHashSessionId, &value)); } else if (name == "maxUsageEntriesSupported") { status = queryProperty(wvcdm::QUERY_KEY_MAX_USAGE_TABLE_ENTRIES, value); } else if (name == "oemCryptoApiMinorVersion") { status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION, value); } else if (name == "analogOutputCapabilities") { status = queryProperty(wvcdm::QUERY_KEY_ANALOG_OUTPUT_CAPABILITIES, value); } else if (name == "canDisableAnalogOutput") { status = queryProperty(wvcdm::QUERY_KEY_CAN_DISABLE_ANALOG_OUTPUT, value); } else if (name == "atscMode") { if (mPropertySet.use_atsc_mode()) { value = kEnable; } else { value = kDisable; } } else if (name == "watermarkingSupport") { status = queryProperty(wvcdm::QUERY_KEY_WATERMARKING_SUPPORT, value); } else if (name == "productionReady") { status = queryProperty(wvcdm::QUERY_KEY_PRODUCTION_READY, value); } else if (name == "provisioningModel") { status = queryProperty(wvcdm::QUERY_KEY_PROVISIONING_MODEL, value); } else if (name == "processorWidth") { #ifdef __LP64__ value = "64"; #else value = "32"; #endif } else { ALOGE("App requested unknown string property %s", name.c_str()); *_aidl_return = value; return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } *_aidl_return = value; return toNdkScopedAStatus(status); } static WvStatus getDeviceSignedCsrPayload( android::sp const cdm, std::string& certificate_signing_request_challenge, std::string& device_info, std::string* csr) { if (certificate_signing_request_challenge.empty() || device_info.empty()) { ALOGW("CSR challenge or device info is empty."); return WvStatus(Status::BAD_VALUE); } std::string signed_csr_payload; CdmResponseType res = cdm->QueryDeviceSignedCsrPayload( certificate_signing_request_challenge, device_info, &signed_csr_payload); if (res != wvcdm::NO_ERROR) { ALOGE("Error querying CDM device_signed_csr_payload: %d", static_cast(res)); return mapCdmResponseType(res); } *csr = signed_csr_payload; return WvStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::getPropertyByteArray( const std::string& in_propertyName, vector* _aidl_return) { WvStatus status(Status::OK); std::string name(in_propertyName.c_str()); vector 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 if (name == "provisioningServiceCertificate") { value = StrToVector(mProvisioningServiceCertificate); } else if (name == "metrics") { drm_metrics::WvCdmMetrics metrics; // If the cdm identifier is not yet sealed, then there are no metrics // for that cdm engine. Avoid calling getCdmIdentifier and sealing // the identifier builder. if (mCdmIdentifierBuilder.is_sealed()) { CdmIdentifier identifier; status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { ALOGE("Unexpected error retrieving cdm identifier: %d", status.get()); } else { status = mapCdmResponseType(mCDM->GetCurrentMetrics(identifier, &metrics)); } } if (status == Status::OK) { std::string serialized_metrics; if (!metrics.SerializeToString(&serialized_metrics)) { status = WvStatus(Status::ERROR_DRM_UNKNOWN); } else { value = StrToVector(serialized_metrics); } } } else if (name == "bootCertificateChain" && isCsrAccessAllowed()) { std::string boot_certificate_chain; CdmResponseType res = mCDM->QueryStatus( wvcdm::kLevelDefault, wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN, &boot_certificate_chain); if (res != wvcdm::NO_ERROR) { ALOGE("Error querying CDM boot certificate chain: %d", static_cast(res)); status = mapCdmResponseType(res); } else { value = StrToVector(boot_certificate_chain); } } else if (name == "verifiedDeviceInfo" && isCsrAccessAllowed()) { std::string verified_device_info; CdmResponseType res = mCDM->QueryStatus(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_DEVICE_INFORMATION, &verified_device_info); if (res != wvcdm::NO_ERROR) { ALOGE("Error querying CDM verified device info: %d", static_cast(res)); status = mapCdmResponseType(res); } else { value = StrToVector(verified_device_info); } } else if (name == "deviceSignedCsrPayload" && isCsrAccessAllowed()) { std::string signed_csr_payload; status = getDeviceSignedCsrPayload(mCDM, mCertificateSigningRequestChallenge, mDeviceInfo, &signed_csr_payload); if (status != Status::OK) { ALOGE("Error querying CDM signed CSR payload: %d", status.get()); } else { value = StrToVector(signed_csr_payload); } mCertificateSigningRequestChallenge.clear(); mDeviceInfo.clear(); } else { ALOGE("App requested unknown byte array property %s", name.c_str()); status = WvStatus(Status::ERROR_DRM_CANNOT_HANDLE); } *_aidl_return = value; return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus WVDrmPlugin::setPropertyString( const std::string& in_propertyName, const std::string& in_value) { std::string name(in_propertyName.c_str()); std::string _value(in_value.c_str()); if (name == "securityLevel") { if (mSessionInfoMap.empty()) { 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; auto status = queryProperty(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL, current_security_level); if (status != Status::OK) { return toNdkScopedAStatus(status); } if (current_security_level != wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) { ALOGE("App requested L1 security on a non-L1 device."); return toNdkScopedAStatus(Status::BAD_VALUE); } else { mPropertySet.set_security_level(kResetSecurityLevel); } } else if (_value == kResetSecurityLevel || _value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_DEFAULT) { mPropertySet.set_security_level(kResetSecurityLevel); } else { ALOGE("App requested invalid security level %s", _value.c_str()); return toNdkScopedAStatus(Status::BAD_VALUE); } } else { ALOGE("App tried to change security level while sessions are open."); ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen"); return toNdkScopedAStatus(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 toNdkScopedAStatus(Status::BAD_VALUE); } } else if (name == "sessionSharing") { if (mSessionInfoMap.empty()) { 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 toNdkScopedAStatus(Status::BAD_VALUE); } } else { ALOGE("App tried to change key sharing while sessions are open."); ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen"); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } } else if (name == "appId") { if (mSessionInfoMap.empty()) { 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 toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } } else if (name == "origin") { if (!mSessionInfoMap.empty()) { ALOGE("App tried to set the origin while sessions are opened."); ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen"); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } else { if (!mCdmIdentifierBuilder.set_origin(_value.c_str())) { return toNdkScopedAStatus(Status::BAD_VALUE); } } } else if (name == "debugIgnoreKeyboxCount") { std::istringstream ss(_value); uint32_t count = 0; ss >> count; if (ss.fail()) { ALOGE("Could not parse an integer from '%s'", _value.c_str()); count = 0; return toNdkScopedAStatus(Status::BAD_VALUE); } CdmResponseType res = mCDM->SetDebugIgnoreKeyboxCount(count); return toNdkScopedAStatus(mapCdmResponseType(res)); } else if (name == "decryptHash") { wvcdm::CdmSessionId sessionId; CdmResponseType res = mCDM->SetDecryptHash(_value.c_str(), &sessionId); if (wvcdm::NO_ERROR == res) mDecryptHashSessionId = sessionId; return toNdkScopedAStatus(mapCdmResponseType(res)); } else if (name == "decryptHashSessionId") { mDecryptHashSessionId = _value.c_str(); } else if (name == "atscMode") { if (_value == kEnable) { if (!mCdmIdentifierBuilder.set_use_atsc_mode(true)) { ALOGE("Cdm identifier builder is sealed. Setting ATSC mode prohibited"); return toNdkScopedAStatus(Status::BAD_VALUE); } mPropertySet.set_use_atsc_mode(true); } else if (_value == kDisable) { if (!mCdmIdentifierBuilder.set_use_atsc_mode(false)) { ALOGE("Cdm identifier builder is sealed. Setting ATSC mode prohibited"); return toNdkScopedAStatus(Status::BAD_VALUE); } mPropertySet.set_use_atsc_mode(false); } else { ALOGE("App requested unknown ATSC mode %s", _value.c_str()); return toNdkScopedAStatus(Status::BAD_VALUE); } } else if (name == "debugOtaKeyboxFallbackDuration") { bool success = false; if (_value == "default") { success = mCDM->SetDefaultOtaKeyboxFallbackDurationRules(); } else if (_value == "fast") { success = mCDM->SetFastOtaKeyboxFallbackDurationRules(); } else { ALOGE("Unknown OTA fallback duration value %s", _value.c_str()); return toNdkScopedAStatus(Status::BAD_VALUE); } if (!success) { return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } } else if (name == "storeAtscLicense") { if (!mCdmIdentifierBuilder.is_sealed()) { ALOGE( "Cdm identifier builder is not sealed. Storing ATSC license " "prohibited"); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } if (!mPropertySet.use_atsc_mode()) { ALOGE("ATSC mode not enabled. Storing ATSC license prohibited"); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { ALOGE("Unable to get CDM Identifier = %d", status.get()); return toNdkScopedAStatus(status); } std::string key_set_id, license_data; status = parseAtscLicenseData(_value, &key_set_id, &license_data); if (status != Status::OK) return toNdkScopedAStatus(status); const CdmResponseType res = mCDM->StoreAtscLicense( identifier, getRequestedSecurityLevel(), key_set_id, license_data); return toNdkScopedAStatus(mapCdmResponseType(res)); } else { ALOGE("App set unknown string property %s", name.c_str()); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::setPropertyByteArray( const std::string& in_propertyName, const vector& in_value) { std::string name(in_propertyName.c_str()); vector _value = in_value; if (name == "serviceCertificate") { std::string cert(_value.begin(), _value.end()); if (_value.empty() || mCDM->IsValidServiceCertificate(cert)) { mPropertySet.set_service_certificate(cert); } else { return toNdkScopedAStatus(Status::BAD_VALUE); } } else if (name == "provisioningServiceCertificate") { std::string cert(_value.begin(), _value.end()); if (_value.empty() || mCDM->IsValidServiceCertificate(cert)) { mProvisioningServiceCertificate = cert; } else { return toNdkScopedAStatus(Status::BAD_VALUE); } } else if (name == "certificateSigningRequestChallenge" && isCsrAccessAllowed()) { mCertificateSigningRequestChallenge = std::string(_value.begin(), _value.end()); } else if (name == "deviceInfo" && isCsrAccessAllowed()) { mDeviceInfo = std::string(_value.begin(), _value.end()); } else { ALOGE("App set unknown byte array property %s", name.c_str()); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::setCipherAlgorithm( const vector& in_sessionId, const std::string& in_algorithm) { if (in_sessionId.empty() || in_algorithm.empty()) { return toNdkScopedAStatus(Status::BAD_VALUE); } const std::string algo(in_algorithm.c_str()); const CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); shared_ptr sessionInfo = mSessionInfoMap.get(cdmSessionId); if (!sessionInfo) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } if (algo == "AES/CBC/NoPadding") { sessionInfo->setEncryptionAlgorithm(wvcdm::kEncryptionAlgorithmAesCbc128); return toNdkScopedAStatus(Status::OK); } return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus WVDrmPlugin::setMacAlgorithm( const vector& in_sessionId, const std::string& in_algorithm) { if (in_sessionId.empty() || in_algorithm.empty()) { return toNdkScopedAStatus(Status::BAD_VALUE); } const std::string algo(in_algorithm.c_str()); const CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); shared_ptr sessionInfo = mSessionInfoMap.get(cdmSessionId); if (!sessionInfo) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } if (algo == "HmacSHA256") { sessionInfo->setSigningAlgorithm(wvcdm::kSigningAlgorithmHmacSha256); return toNdkScopedAStatus(Status::OK); } return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } ::ndk::ScopedAStatus WVDrmPlugin::encrypt(const vector& in_sessionId, const vector& in_keyId, const vector& in_input, const vector& in_iv, vector* _aidl_return) { _aidl_return->clear(); const CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); shared_ptr sessionInfo = mSessionInfoMap.get(cdmSessionId); if (!sessionInfo) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } if (!sessionInfo->hasEncryptionAlgorithm()) { ALOGW("Encryption algorithm not set"); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } std::string output; const CdmResponseType result = mCDM->GenericEncrypt( cdmSessionId, KeyId(in_keyId.begin(), in_keyId.end()), std::string(in_input.begin(), in_input.end()), std::string(in_iv.begin(), in_iv.end()), sessionInfo->getEncryptionAlgorithm(), &output); if (!result.IsOk()) { ALOGE("Generic encryption failed: %s", result.ToString().c_str()); return toNdkScopedAStatus( mapAndNotifyOfCdmResponseType(in_sessionId, result)); } _aidl_return->assign(output.begin(), output.end()); return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::decrypt(const vector& in_sessionId, const vector& in_keyId, const vector& in_input, const vector& in_iv, vector* _aidl_return) { _aidl_return->clear(); const CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); shared_ptr sessionInfo = mSessionInfoMap.get(cdmSessionId); if (!sessionInfo) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } if (!sessionInfo->hasEncryptionAlgorithm()) { ALOGW("Encryption algorithm not set"); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } std::string output; const CdmResponseType result = mCDM->GenericDecrypt( cdmSessionId, KeyId(in_keyId.begin(), in_keyId.end()), std::string(in_input.begin(), in_input.end()), std::string(in_iv.begin(), in_iv.end()), sessionInfo->getEncryptionAlgorithm(), &output); if (!result.IsOk()) { ALOGE("Generic decryption failed: %s", result.ToString().c_str()); return toNdkScopedAStatus( mapAndNotifyOfCdmResponseType(in_sessionId, result)); } _aidl_return->assign(output.begin(), output.end()); return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::sign(const vector& in_sessionId, const vector& in_keyId, const vector& in_message, vector* _aidl_return) { _aidl_return->clear(); const CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); shared_ptr sessionInfo = mSessionInfoMap.get(cdmSessionId); if (!sessionInfo) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } if (!sessionInfo->hasSigningAlgorithm()) { ALOGW("Signing algorithm not set"); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } std::string signature; const CdmResponseType result = mCDM->GenericSign(cdmSessionId, KeyId(in_keyId.begin(), in_keyId.end()), std::string(in_message.begin(), in_message.end()), sessionInfo->getSigningAlgorithm(), &signature); if (!result.IsOk()) { ALOGE("Generic signature failed: %s", result.ToString().c_str()); return toNdkScopedAStatus( mapAndNotifyOfCdmResponseType(in_sessionId, result)); } _aidl_return->assign(signature.begin(), signature.end()); return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::verify(const vector& in_sessionId, const vector& in_keyId, const vector& in_message, const vector& in_signature, bool* _aidl_return) { *_aidl_return = false; const CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); shared_ptr sessionInfo = mSessionInfoMap.get(cdmSessionId); if (!sessionInfo) { return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); } if (!sessionInfo->hasSigningAlgorithm()) { ALOGW("Signing algorithm not set"); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } const CdmResponseType result = mCDM->GenericVerify( cdmSessionId, KeyId(in_keyId.begin(), in_keyId.end()), std::string(in_message.begin(), in_message.end()), sessionInfo->getSigningAlgorithm(), std::string(in_signature.begin(), in_signature.end())); if (result.IsOk()) { *_aidl_return = true; return toNdkScopedAStatus(Status::OK); } if (result == wvcdm::UNKNOWN_ERROR && result.oemc_result() == OEMCrypto_ERROR_SIGNATURE_FAILURE) { // TODO(b/279245250): Use a better error code. return toNdkScopedAStatus(Status::OK); } ALOGE("Generic verify failed: %s", result.ToString().c_str()); return toNdkScopedAStatus( mapAndNotifyOfCdmResponseType(in_sessionId, result)); } ::ndk::ScopedAStatus WVDrmPlugin::signRSA(const vector& in_sessionId, const std::string& in_algorithm, const vector& in_message, const vector& in_wrappedkey, vector* _aidl_return) { *_aidl_return = vector(); if (in_sessionId.size() == 0 || in_algorithm.size() == 0 || in_message.size() == 0 || in_wrappedkey.size() == 0) { return toNdkScopedAStatus(Status::BAD_VALUE); } const char* sid = AIBinder_getCallingSid(); if (!sid || (!strstr(sid, ":mediashell_app:") && !strstr(sid, ":mediadrmserver:") && !strstr(sid, ":setupwraith_app:") && !strstr(sid, ":gmscore_app:"))) { ALOGE( "Only mediashell/mediadrmserver/setupwraith_app/gmscore_app can call signRSA, " "but actually: %s", sid); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); } const std::string algo(in_algorithm.c_str()); vector signature; *_aidl_return = 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()); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); } Status status = Status::OK; OEMCryptoResult res = mCrypto->signRSA( in_wrappedkey.data(), in_wrappedkey.size(), in_message.data(), in_message.size(), signature, padding_scheme); *_aidl_return = signature; if (res != OEMCrypto_SUCCESS) { ALOGE("OEMCrypto_GenerateRSASignature failed with %u", res); status = mapOEMCryptoResult(res); } return toNdkScopedAStatus(status); } ::ndk::ScopedAStatus WVDrmPlugin::setListener( const shared_ptr<::aidl::android::hardware::drm::IDrmPluginListener>& in_listener) { mListener = in_listener; ::ndk::ScopedAStatus _aidl_status; _aidl_status.set(AStatus_fromStatus(STATUS_OK)); return _aidl_status; } void WVDrmPlugin::sendEvent( ::aidl::android::hardware::drm::EventType in_eventType, const vector& in_sessionId, const vector& in_data) { ::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok(); if (mListener != nullptr) { err = mListener->onEvent(in_eventType, in_sessionId, in_data); } else { ALOGE("Null event listener, event not sent"); } if (!err.isOk()) { ALOGW("sendEvent failed %s", err.getDescription().c_str()); } return; } void WVDrmPlugin::sendExpirationUpdate(const vector& in_sessionId, int64_t in_expiryTimeInMS) { ::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok(); if (mListener != nullptr) { err = mListener->onExpirationUpdate(in_sessionId, in_expiryTimeInMS); } else { ALOGE("Null event listener, event not sent"); } if (!err.isOk()) { ALOGW("sendExpirationUpdate failed %s", err.getDescription().c_str()); } return; } void WVDrmPlugin::sendKeysChange( const vector& in_sessionId, const vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList, bool in_hasNewUsableKey) { ::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok(); if (mListener != nullptr) { err = mListener->onKeysChange(in_sessionId, in_keyStatusList, in_hasNewUsableKey); } else { ALOGE("Null event listener, event not sent"); } if (!err.isOk()) { ALOGW("sendKeysChange failed %s", err.getDescription().c_str()); } return; } void WVDrmPlugin::sendSessionLostState(const vector& in_sessionId) { ::ndk::ScopedAStatus err = ::ndk::ScopedAStatus::ok(); if (mListener != nullptr) { err = mListener->onSessionLostState(in_sessionId); } else { ALOGE("Null event listener, event not sent"); } if (!err.isOk()) { ALOGW("sendSessionLostState failed %s", err.getDescription().c_str()); } return; } ::ndk::ScopedAStatus WVDrmPlugin::getLogMessages( vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) { const vector& logs(wvutil::g_logbuf.getLogs()); vector<::aidl::android::hardware::drm::LogMessage> msgs; for (auto log : logs) { msgs.push_back( {log.time_ms_, toAidlLogPriority(log.priority_), log.message_}); } *_aidl_return = msgs; return toNdkScopedAStatus(Status::OK); } ::ndk::ScopedAStatus WVDrmPlugin::requiresSecureDecoder( const std::string& in_mime, SecurityLevel in_level, bool* _aidl_return) { if (!strncasecmp(in_mime.c_str(), "video/", 6)) { if (in_level == SecurityLevel::DEFAULT) { std::string level(mPropertySet.security_level()); if (level == kResetSecurityLevel) { mCDM->QueryStatus(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL, &level); } *_aidl_return = level == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1; } else { // Type is video, so check level to see if we require a secure decoder. *_aidl_return = in_level == SecurityLevel::HW_SECURE_ALL || in_level == SecurityLevel::HW_SECURE_DECODE; } } else { // Type is not video, so never require a secure decoder. *_aidl_return = false; } return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus WVDrmPlugin::setPlaybackId( const vector& in_sessionId, const std::string& in_playbackId) { CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmResponseType res = mCDM->SetPlaybackId(cdmSessionId, in_playbackId); return toNdkScopedAStatus(mapCdmResponseType(res)); } void WVDrmPlugin::OnSessionRenewalNeeded(const CdmSessionId& cdmSessionId) { const vector sessionId = StrToVector(cdmSessionId); const vector data; // data is ignored sendEvent(EventType::KEY_NEEDED, sessionId, data); } void WVDrmPlugin::OnSessionKeysChange(const CdmSessionId& cdmSessionId, const CdmKeyStatusMap& cdmKeysStatus, bool hasNewUsableKey) { bool expired = false; vector 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 = StrToVector(keyId); keyStatus.type = ConvertFromCdmKeyStatus(cdmKeyStatus); keyStatusList.push_back(keyStatus); } const vector sessionId = StrToVector(cdmSessionId); const vector data; // data is ignored sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); // For backward compatibility. if (expired) { sendEvent(EventType::KEY_EXPIRED, sessionId, data); } } void WVDrmPlugin::OnExpirationUpdate(const CdmSessionId& cdmSessionId, int64_t newExpiryTimeSeconds) { const vector sessionId = StrToVector(cdmSessionId); int64_t newExpiryTimeMilliseconds = newExpiryTimeSeconds == wvcdm::NEVER_EXPIRES ? newExpiryTimeSeconds : newExpiryTimeSeconds * 1000; sendExpirationUpdate(sessionId, newExpiryTimeMilliseconds); } void WVDrmPlugin::OnSessionLostState(const CdmSessionId& cdmSessionId) { const vector sessionId = StrToVector(cdmSessionId); sendSessionLostState(sessionId); } WvStatus WVDrmPlugin::queryProperty(const std::string& property, std::string& stringValue) const { return queryProperty(getRequestedSecurityLevel(), property, stringValue); } WvStatus WVDrmPlugin::queryProperty(RequestedSecurityLevel 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: %d", static_cast(res)); } return mapCdmResponseType(res); } ::ndk::SpAIBinder WVDrmPlugin::createBinder() { auto binder = BnDrmPlugin::createBinder(); AIBinder_setRequestingSid(binder.get(), true); return binder; } WvStatus WVDrmPlugin::queryProperty(const std::string& property, vector& vector_value) const { std::string string_value; auto status = queryProperty(property, string_value); if (status != Status::OK) return status; vector_value = StrToVector(string_value); return WvStatus(Status::OK); } bool WVDrmPlugin::isProvisioned(wvcdm::CdmSecurityLevel securityLevel, const std::string& origin, const std::string& spoid, bool atsc_mode_enabled) const { return mCDM->IsProvisioned(securityLevel, origin, spoid, atsc_mode_enabled); } WvStatus WVDrmPlugin::parseAtscLicenseData( const std::string& in_value, std::string* key_set_id, std::string* serialized_license_data) { if (key_set_id == nullptr) { ALOGE("key_set_id null"); return WvStatus(Status::ERROR_DRM_CANNOT_HANDLE); } if (serialized_license_data == nullptr) { ALOGE("serialized_license_data null"); return WvStatus(Status::ERROR_DRM_CANNOT_HANDLE); } if (in_value.compare(0, strlen(wvcdm::ATSC_KEY_SET_ID_PREFIX), &wvcdm::ATSC_KEY_SET_ID_PREFIX[0]) != 0) { ALOGE( "ATSC license input does not conform to expectations. Key set does " "not have a valid ATSC Key set prefix %s", in_value.c_str()); return WvStatus(Status::BAD_VALUE); } const char kColon = ':'; const size_t pos = in_value.find(kColon); if (pos == std::string::npos) { ALOGE( "ATSC license input does not conform to expectations. Missing colon " "= %s", in_value.c_str()); return WvStatus(Status::BAD_VALUE); } if (pos == in_value.length()) { ALOGE( "ATSC license input does not conform to expectations. No data after " "colon"); return WvStatus(Status::BAD_VALUE); } *key_set_id = in_value.substr(0, pos); const std::vector license_data_binary = wvutil::Base64Decode(in_value.substr(pos + 1)); if (license_data_binary.empty()) { ALOGE( "ATSC license input does not conform to expectations. License data " "failed to decode from Base64"); return WvStatus(Status::BAD_VALUE); } serialized_license_data->assign(license_data_binary.begin(), license_data_binary.end()); return WvStatus(Status::OK); } WvStatus WVDrmPlugin::mapAndNotifyOfCdmResponseType( const vector& sessionId, CdmResponseType res) { notifyOfCdmResponseType(sessionId, res); return mapCdmResponseType(res); } void WVDrmPlugin::notifyOfCdmResponseType(const vector& sessionId, CdmResponseType res) { const vector data; // data is ignored if (res == wvcdm::NEED_PROVISIONING) { sendEvent(EventType::PROVISION_REQUIRED, sessionId, data); } else if (res == wvcdm::NEED_KEY) { sendEvent(EventType::KEY_NEEDED, sessionId, data); } } Status WVDrmPlugin::mapAndNotifyOfOEMCryptoResult( const vector& sessionId, OEMCryptoResult res) { const vector data; // data is ignored if (res == OEMCrypto_ERROR_NO_DEVICE_KEY) { sendEvent(EventType::PROVISION_REQUIRED, 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: case OEMCrypto_ERROR_UNKNOWN_FAILURE: case OEMCrypto_ERROR_OPEN_SESSION_FAILED: FALLTHROUGH_INTENDED; /* FALLTHROUGH */ default: ALOGW("Returns UNKNOWN error for legacy status: %d", res); return static_cast(Status::GENERAL_OEM_ERROR); } } RequestedSecurityLevel WVDrmPlugin::getRequestedSecurityLevel() const { return mPropertySet.security_level().compare( wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0 ? wvcdm::kLevel3 : wvcdm::kLevelDefault; } bool WVDrmPlugin::initDataResemblesPSSH(const vector& 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(&initDataArray[sizeof(nboSize)]); std::string id(idField, kPsshTag.size()); return id == kPsshTag; } WvStatus WVDrmPlugin::unprovision(const CdmIdentifier& identifier) { if (mPropertySet.use_atsc_mode()) return mapCdmResponseType(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC); 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) { mCdmIdentifier.app_package_name = mAppPackageName; mCdmIdentifier.unique_id = getNextUniqueId(); mCdmIdentifier.user_id = wvutil::GetIpcCallingUid(); } WvStatus WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier( CdmIdentifier* identifier) { if (!mIsIdentifierSealed) { auto res = calculateSpoid(); if (res != Status::OK) return res; mIsIdentifierSealed = true; } *identifier = mCdmIdentifier; return WvStatus(Status::OK); } WvStatus WVDrmPlugin::CdmIdentifierBuilder::getDeviceUniqueId(std::string* id) { if (mUseSpoid) { CdmIdentifier identifier; auto res = getCdmIdentifier(&identifier); if (res != Status::OK) return res; *id = identifier.spoid; return WvStatus(Status::OK); } else { return getOemcryptoDeviceId(id); } } WvStatus 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. auto res = getDeviceUniqueId(id); if (res != Status::OK) return res; for (char& c : *id) { c = ~c; } return WvStatus(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; } bool WVDrmPlugin::CdmIdentifierBuilder::set_use_atsc_mode(bool enable) { if (is_sealed()) return false; mCdmIdentifier.app_package_name = enable ? wvcdm::ATSC_APP_PACKAGE_NAME : mAppPackageName; return true; } WvStatus WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() { if (!mUseSpoid) return WvStatus(Status::OK); // Calculate SPOID for default security level if appropriate std::string deviceId; if (mParent.getRequestedSecurityLevel() == wvcdm::kLevelDefault) { auto res = getOemcryptoDeviceId(&deviceId); if (res != Status::OK) return res; return WvStatus(calculateSpoid(deviceId, &mCdmIdentifier.spoid)); } // If requested security level is L3, possibilities are // (a) L3 has not been provisioned // (b) L3 was provisioned with L3 device ID in the CdmIdentifier // (c) L3 was provisioned (incorrectly) with L1 device ID in the // CdmIdentifier Check (b) first. Get L3 device ID, calculate SPOID and if // provisioned with this SPOID, return this SPOID. Check (c) next. Get L1 // device ID, calculate SPOID and if provisioned with this SPOID, return // this SPOID. On any errors in (c) or not provisioned return L3 SPOID. auto res = getOemcryptoDeviceId(wvcdm::kLevel3, &deviceId); if (res != Status::OK) return res; std::string spoidL3; auto status = calculateSpoid(deviceId, &spoidL3); if (status != Status::OK) return WvStatus(status); bool atsc_mode_enabled = mCdmIdentifier.app_package_name == wvcdm::ATSC_APP_PACKAGE_NAME; if (mParent.isProvisioned(wvcdm::kSecurityLevelL3, origin(), spoidL3, atsc_mode_enabled)) { mCdmIdentifier.spoid = spoidL3; return WvStatus(Status::OK); } // Not provisioned with CdmIdentifier containing SPOID with L3 device ID. // Try SPOID with L1 device ID. std::string deviceIdLevelDefault; res = getOemcryptoDeviceId(wvcdm::kLevelDefault, &deviceIdLevelDefault); if (res != Status::OK) { mCdmIdentifier.spoid = spoidL3; return WvStatus(Status::OK); } // If the L3 and default security level IDs are identical then the // device does not support L1. if (deviceId == deviceIdLevelDefault) { mCdmIdentifier.spoid = spoidL3; return WvStatus(Status::OK); } std::string spoidLevelDefault; status = calculateSpoid(deviceIdLevelDefault, &spoidLevelDefault); if (status != Status::OK) { mCdmIdentifier.spoid = spoidL3; return WvStatus(Status::OK); } if (mParent.isProvisioned(wvcdm::kSecurityLevelL1, origin(), spoidLevelDefault, atsc_mode_enabled)) { mCdmIdentifier.spoid = spoidLevelDefault; return WvStatus(Status::OK); } // Not provisioned with CdmIdentifier containing SPOID with L1 or L3 // device ID. Return L3 SPOID. mCdmIdentifier.spoid = spoidL3; return WvStatus(Status::OK); } Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid( const std::string& deviceId, std::string* spoid) { if (spoid == nullptr) return Status::ERROR_DRM_CANNOT_HANDLE; if (!mUseSpoid) { spoid->clear(); return Status::OK; } 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); *spoid = std::string(reinterpret_cast(hash), SHA256_DIGEST_LENGTH); return Status::OK; } WvStatus WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId( std::string* id) { return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id); } WvStatus WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId( wvcdm::RequestedSecurityLevel securityLevel, std::string* id) { return mParent.queryProperty(securityLevel, wvcdm::QUERY_KEY_DEVICE_ID, *id); } uint32_t WVDrmPlugin::CdmIdentifierBuilder::getNextUniqueId() { // Start with 1. 0 is reserved for the default cdm identifier. static uint32_t unique_id = 1; return ++unique_id; } } // namespace widevine } // namespace drm } // namespace hardware } // namespace wvdrm