// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #include "license.h" #include #include #include "cdm_session.h" #include "client_identification.h" #include "clock.h" #include "crypto_key.h" #include "crypto_session.h" #include "device_files.h" #include "log.h" #include "platform.h" #include "policy_engine.h" #include "privacy_crypto.h" #include "properties.h" #include "service_certificate.h" #include "string_conversions.h" #include "wv_cdm_constants.h" namespace { const uint32_t kFourCcCbc1 = 0x63626331; const uint32_t kFourCcCbcs = 0x63626373; const uint32_t kFourCcLittleEndianCbc1 = 0x31636263; const uint32_t kFourCcLittleEndianCbcs = 0x73636263; const uint32_t kFourCcCenc = 0x63656e63; const std::string kEmptyString; // MAC key in the license are two separate MAC keys (client and server). constexpr size_t kLicenseMacKeySize = wvcdm::MAC_KEY_SIZE * 2; } // namespace namespace wvcdm { // Protobuf generated classes. using video_widevine::EncryptedClientIdentification; using video_widevine::License; using video_widevine::License_KeyContainer; using video_widevine::LicenseError; using video_widevine::LicenseIdentification; using video_widevine::LicenseRequest; using video_widevine::LicenseRequest_ContentIdentification; using video_widevine::LicenseRequest_ContentIdentification_ExistingLicense; using video_widevine::LicenseRequest_ContentIdentification_WebmKeyId; using video_widevine::LicenseRequest_ContentIdentification_WidevinePsshData; using video_widevine::SignedMessage; namespace { std::vector ExtractEntitlementKeys(const License& license) { std::vector key_array; for (int i = 0; i < license.key_size(); ++i) { CryptoKey key; size_t length = 0; switch (license.key(i).type()) { case License_KeyContainer::ENTITLEMENT: { // Strip off PKCS#5 padding - since we know the key is 32 or 48 bytes, // the padding will always be 16 bytes. if (license.key(i).key().size() > 32) { length = license.key(i).key().size() - 16; } else { length = 0; } key.set_key_data(license.key(i).key().substr(0, length)); key.set_key_data_iv(license.key(i).iv()); key.set_key_id(license.key(i).id()); key.set_track_label(license.key(i).track_label()); if (license.key(i).has_key_control()) { key.set_key_control(license.key(i).key_control().key_control_block()); key.set_key_control_iv(license.key(i).key_control().iv()); } uint32_t four_cc = kFourCcCenc; if (license.has_protection_scheme()) { four_cc = license.protection_scheme(); } key.set_track_label(license.key(i).track_label()); switch (four_cc) { // b/30713238: Android N assumed that the "protection scheme" Four // CC code, after being extracted from the protobuf, was host byte // order dependent. Later versions do not assume this, and thus, // for backwards compatibility, must support both byte orders. case kFourCcCbc1: case kFourCcCbcs: case kFourCcLittleEndianCbc1: case kFourCcLittleEndianCbcs: key.set_cipher_mode(kCipherModeCbc); break; default: key.set_cipher_mode(kCipherModeCtr); break; } key_array.push_back(key); } break; default: // Ignore all but ENTITLEMENT key types. break; } } return key_array; } std::vector ExtractContentKeys(const License& license) { std::vector key_array; // Extract content key(s) for (int i = 0; i < license.key_size(); ++i) { CryptoKey key; size_t length; switch (license.key(i).type()) { case License_KeyContainer::CONTENT: case License_KeyContainer::OPERATOR_SESSION: { key.set_key_id(license.key(i).id()); // Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes, // the padding will always be 16 bytes. // TODO(b/111069024): remove this! if (license.key(i).key().size() > 16) { length = license.key(i).key().size() - 16; } else { length = 0; } key.set_key_data(license.key(i).key().substr(0, length)); key.set_key_data_iv(license.key(i).iv()); if (license.key(i).has_key_control()) { key.set_key_control(license.key(i).key_control().key_control_block()); key.set_key_control_iv(license.key(i).key_control().iv()); } uint32_t four_cc = kFourCcCenc; if (license.has_protection_scheme()) { four_cc = license.protection_scheme(); } key.set_track_label(license.key(i).track_label()); switch (four_cc) { // b/30713238: Android N assumed that the "protection scheme" Four // CC code, after being extracted from the protobuf, was host byte // order dependent. Later versions do not assume this, and thus, // for backwards compatibility, must support both byte orders. case kFourCcCbc1: case kFourCcCbcs: case kFourCcLittleEndianCbc1: case kFourCcLittleEndianCbcs: key.set_cipher_mode(kCipherModeCbc); break; default: key.set_cipher_mode(kCipherModeCtr); break; } key_array.push_back(key); break; } case License_KeyContainer::KEY_CONTROL: if (license.key(i).has_key_control()) { key.set_key_control(license.key(i).key_control().key_control_block()); if (license.key(i).key_control().has_iv()) { key.set_key_control_iv(license.key(i).key_control().iv()); } key_array.push_back(key); } break; default: // Ignore SIGNING and SUB_SESSION key types as they are not content // related. break; } } return key_array; } } // namespace CdmLicense::CdmLicense(const CdmSessionId& session_id) : crypto_session_(nullptr), policy_engine_(nullptr), session_id_(session_id), initialized_(false), renew_with_client_id_(false), is_offline_(false), supports_core_messages_(true), use_privacy_mode_(false), clock_(new Clock()), license_key_type_(kLicenseKeyTypeContent) {} CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock) : crypto_session_(nullptr), policy_engine_(nullptr), session_id_(session_id), initialized_(false), renew_with_client_id_(false), is_offline_(false), supports_core_messages_(true), use_privacy_mode_(false), license_key_type_(kLicenseKeyTypeContent) { clock_.reset(clock); } CdmLicense::~CdmLicense() {} bool CdmLicense::Init(bool use_privacy_mode, const std::string& signed_service_certificate, CryptoSession* session, PolicyEngine* policy_engine) { if (!clock_) { LOGE("Clock parameter not provided"); return false; } if (session_id_.empty()) { LOGE("Session ID not provided"); return false; } if (session == nullptr || !session->IsOpen()) { LOGE("Crypto session not provided or not open"); return false; } if (policy_engine == nullptr) { LOGE("Policy engine not provided"); return false; } if (use_privacy_mode && !signed_service_certificate.empty() && service_certificate_.Init(signed_service_certificate) != NO_ERROR) { return false; } crypto_session_ = session; policy_engine_ = policy_engine; use_privacy_mode_ = use_privacy_mode; license_nonce_ = 0; initialized_ = true; return true; } CdmResponseType CdmLicense::SetServiceCertificate( const std::string& signed_service_certificate) { return service_certificate_.Init(signed_service_certificate); } CdmResponseType CdmLicense::PrepareKeyRequest( const InitializationData& init_data, const std::string& client_token, CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request, std::string* server_url) { if (!initialized_) { LOGE("CdmLicense not initialized"); return LICENSE_PARSER_NOT_INITIALIZED_4; } client_token_ = client_token; if (init_data.IsEmpty() && stored_init_data_) { InitializationData restored_init_data = *stored_init_data_; stored_init_data_.reset(); return PrepareKeyRequest(restored_init_data, client_token, license_type, app_parameters, signed_request, server_url); } wrapped_keys_ = init_data.ExtractWrappedKeys(); if (!init_data.is_supported()) { LOGE("Unsupported init data type: type = %s", init_data.type().c_str()); return INVALID_PARAMETERS_LIC_3; } if (init_data.IsEmpty()) { LOGE("Init data is empty"); return INVALID_PARAMETERS_LIC_4; } if (signed_request == nullptr) { LOGE("Output parameter |signed_request| not provided"); return INVALID_PARAMETERS_LIC_6; } if (server_url == nullptr) { LOGE("Output parameter |server_url| not provided"); return INVALID_PARAMETERS_LIC_7; } // If privacy mode and no service certificate, depending on platform // configuration, request service certificate or declare error if (use_privacy_mode_ && !service_certificate_.has_certificate()) { if (!Properties::allow_service_certificate_requests()) { LOGE("Privacy mode failure: No service certificate"); return PRIVACY_MODE_ERROR_1; } stored_init_data_.reset(new InitializationData(init_data)); if (!ServiceCertificate::GetRequest(signed_request)) { LOGE("Failed to prepare service certificated request"); return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR; } return KEY_MESSAGE; } const std::string& request_id = crypto_session_->request_id(); LicenseRequest license_request; CdmResponseType status; status = PrepareClientId(app_parameters, kEmptyString, &license_request); if (NO_ERROR != status) return status; status = PrepareContentId(init_data, license_type, request_id, &license_request); if (NO_ERROR != status) return status; license_request.set_type(LicenseRequest::NEW); license_request.set_request_time(clock_->GetCurrentTime()); // Get/set the nonce. This value will be reflected in the Key Control Block // of the license response. status = crypto_session_->GenerateNonce(&license_nonce_); switch (status) { case NO_ERROR: break; case SESSION_LOST_STATE_ERROR: case SYSTEM_INVALIDATED_ERROR: return status; default: return LICENSE_REQUEST_NONCE_GENERATION_ERROR; } license_request.set_key_control_nonce(license_nonce_); license_request.set_protocol_version(video_widevine::VERSION_2_1); // License request is complete. Serialize it. std::string serialized_license_req; license_request.SerializeToString(&serialized_license_req); key_request_ = serialized_license_req; // Derive signing and encryption keys and construct core message and // signature. std::string core_message; std::string license_request_signature; status = crypto_session_->PrepareAndSignLicenseRequest( serialized_license_req, &core_message, &license_request_signature); if (status != NO_ERROR) { signed_request->clear(); return status; } if (license_request_signature.empty()) { LOGE("License request signature is empty"); signed_request->clear(); return EMPTY_LICENSE_REQUEST; } // Put serialized license request and signature together SignedMessage signed_message; signed_message.set_type(SignedMessage::LICENSE_REQUEST); signed_message.set_signature(license_request_signature); signed_message.set_msg(serialized_license_req); if (core_message.size() > 0) { signed_message.set_oemcrypto_core_message(core_message); } signed_message.SerializeToString(signed_request); *server_url = server_url_; return KEY_MESSAGE; } // TODO(b/166007195): Remove this. CdmResponseType CdmLicense::PrepareKeyUpdateReload(CdmSession* cdm_session) { uint32_t api_version = 0; if (!crypto_session_->GetApiVersion(&api_version)) { LOGW("Unknown API Version"); api_version = 15; } if (api_version != 16) return NO_ERROR; // To work around b/166010609, we ask OEMCrypto to prepare an unused renewal // request. This lets the ODK library update its clock saying when the renewal // was signed. constexpr bool is_renewal = true; const CdmAppParameterMap unused_app_parameters; CdmKeyMessage unused_request; std::string unused_url; return PrepareKeyUpdateRequest(is_renewal, unused_app_parameters, cdm_session, &unused_request, &unused_url); } CdmResponseType CdmLicense::PrepareKeyUpdateRequest( bool is_renewal, const CdmAppParameterMap& app_parameters, CdmSession* cdm_session, CdmKeyMessage* signed_request, std::string* server_url) { if (!initialized_) { LOGE("CdmLicense not initialized"); return LICENSE_PARSER_NOT_INITIALIZED_1; } if (signed_request == nullptr) { LOGE("Output parameter |signed_request| not provided"); return INVALID_PARAMETERS_LIC_1; } if (server_url == nullptr) { LOGE("Output parameter |server_url| not provided"); return INVALID_PARAMETERS_LIC_2; } if (is_renewal && !policy_engine_->CanRenew()) { LOGE("License renewal prohibited"); return LICENSE_RENEWAL_PROHIBITED; } if (renew_with_client_id_) { if (use_privacy_mode_ && !service_certificate_.has_certificate()) { LOGE("Privacy mode failure: No service certificate"); return PRIVACY_MODE_ERROR_2; } } LicenseRequest license_request; if (is_renewal) license_request.set_type(LicenseRequest::RENEWAL); else license_request.set_type(LicenseRequest::RELEASE); license_request.set_request_time(clock_->GetCurrentTime()); if (renew_with_client_id_) { CdmResponseType status = PrepareClientId( app_parameters, provider_client_token_, &license_request); if (NO_ERROR != status) return status; } LicenseRequest_ContentIdentification_ExistingLicense* current_license = license_request.mutable_content_id()->mutable_existing_license(); LicenseIdentification license_id = policy_engine_->license_id(); current_license->mutable_license_id()->CopyFrom(license_id); int64_t seconds_since_started, seconds_since_last_played; CryptoSession::UsageDurationStatus usage_duration_status = CryptoSession::kUsageDurationsInvalid; if (!provider_session_token_.empty()) { if (!is_renewal) { CdmResponseType status = crypto_session_->DeactivateUsageInformation(provider_session_token_); if (NO_ERROR != status) return status; } // TODO(rfrias): Refactor to avoid needing to call CdmSession if (cdm_session && cdm_session->supports_usage_info()) { const CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); if (NO_ERROR != status) return status; } std::string usage_report; CdmResponseType status = crypto_session_->GenerateUsageReport( provider_session_token_, &usage_report, &usage_duration_status, &seconds_since_started, &seconds_since_last_played); if (!is_renewal) { if (NO_ERROR == status) current_license->set_session_usage_table_entry(usage_report); else return GENERATE_USAGE_REPORT_ERROR; } } if (CryptoSession::kUsageDurationsValid != usage_duration_status) { if (policy_engine_->GetSecondsSinceStarted(&seconds_since_started) && policy_engine_->GetSecondsSinceLastPlayed(&seconds_since_last_played)) usage_duration_status = CryptoSession::kUsageDurationsValid; } if (CryptoSession::kUsageDurationsValid == usage_duration_status) { current_license->set_seconds_since_started(seconds_since_started); current_license->set_seconds_since_last_played(seconds_since_last_played); } uint32_t api_version = 0; if (!crypto_session_->GetApiVersion(&api_version)) { LOGW("Unknown API Version"); api_version = 15; } if (api_version < 16) { // For a pre-v16 license, get/set the nonce. This value will be reflected // in the Key Control Block of the license response. const CdmResponseType status = crypto_session_->GenerateNonce(&license_nonce_); switch (status) { case NO_ERROR: break; case SESSION_LOST_STATE_ERROR: case SYSTEM_INVALIDATED_ERROR: return status; default: return LICENSE_RENEWAL_NONCE_GENERATION_ERROR; } } license_request.set_key_control_nonce(license_nonce_); license_request.set_protocol_version(video_widevine::VERSION_2_1); // License request is complete. Serialize it. std::string serialized_license_req; license_request.SerializeToString(&serialized_license_req); // Construct signature and core message. std::string core_message; std::string license_request_signature; const CdmResponseType status = crypto_session_->PrepareAndSignRenewalRequest( serialized_license_req, &core_message, &license_request_signature); if (status != NO_ERROR) return status; if (license_request_signature.empty()) { LOGE("License request signature is empty"); return EMPTY_LICENSE_RENEWAL; } // Put serialize license request and signature together SignedMessage signed_message; signed_message.set_type(SignedMessage::LICENSE_REQUEST); signed_message.set_signature(license_request_signature); signed_message.set_msg(serialized_license_req); if (supports_core_messages()) { // Only include the |core_message| in renewal requests if it is // already known that the license is v16. signed_message.set_oemcrypto_core_message(core_message); } signed_message.SerializeToString(signed_request); *server_url = server_url_; return KEY_MESSAGE; } CdmResponseType CdmLicense::HandleKeyResponse( bool is_restore, const CdmKeyResponse& license_response) { if (!initialized_) { LOGE("CdmLicense not initialized"); return LICENSE_PARSER_NOT_INITIALIZED_2; } // Clear the latest service version when we receive a new response. latest_service_version_.Clear(); if (license_response.empty()) { LOGE("License response is empty"); return EMPTY_LICENSE_RESPONSE_1; } SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { LOGE("Unable to parse signed license response"); return INVALID_LICENSE_RESPONSE; } latest_service_version_ = signed_response.service_version_info(); if (use_privacy_mode_ && Properties::allow_service_certificate_requests() && signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) { std::string signed_certificate; CdmResponseType status = ServiceCertificate::ParseResponse( license_response, &signed_certificate); if (status != NO_ERROR) return status; status = service_certificate_.Init(signed_certificate); return (status == NO_ERROR) ? NEED_KEY : status; } if (signed_response.type() == SignedMessage::ERROR_RESPONSE) return HandleKeyErrorResponse(signed_response); if (signed_response.type() != SignedMessage::LICENSE) { LOGE("Unrecognized signed message type: type = %d", static_cast(signed_response.type())); return INVALID_LICENSE_TYPE; } if (!signed_response.has_signature()) { LOGE("License response is not signed"); return LICENSE_RESPONSE_NOT_SIGNED; } // Check that the server returned a |core_message|. If missing, then // the server is assumed to operate as V15. This will imply that the // |signature| field in the response does not include a core message // either. if (!signed_response.has_oemcrypto_core_message() || signed_response.oemcrypto_core_message().empty()) { supports_core_messages_ = false; } const std::string& signed_message = signed_response.msg(); const std::string core_message = signed_response.has_oemcrypto_core_message() ? signed_response.oemcrypto_core_message() : std::string(); const std::string& signature = signed_response.signature(); License license; if (!license.ParseFromString(signed_message)) { LOGE("Unable to parse license response"); return LICENSE_RESPONSE_PARSE_ERROR_1; } if (!signed_response.has_session_key()) { LOGE("Signed response has no session keys present"); return SESSION_KEYS_NOT_FOUND; } CdmResponseType status = crypto_session_->GenerateDerivedKeys( key_request_, signed_response.session_key()); if (status != NO_ERROR) return status; // Extract mac key std::string mac_key_iv; std::string mac_keys; for (int i = 0; i < license.key_size(); ++i) { if (license.key(i).type() == License_KeyContainer::SIGNING) { mac_key_iv.assign(license.key(i).iv()); // Strip off PKCS#5 padding mac_keys.assign(license.key(i).key().data(), kLicenseMacKeySize); } } if (license.policy().can_renew() || (mac_key_iv.size() != 0 || mac_keys.size() != 0)) { if (mac_key_iv.size() != KEY_IV_SIZE || mac_keys.size() != kLicenseMacKeySize) { LOGE( "MAC key/IV size error: expected = %zu/%zu, " "actual = %zu/%zu (key/iv)", kLicenseMacKeySize, KEY_IV_SIZE, mac_keys.size(), mac_key_iv.size()); return KEY_SIZE_ERROR_1; } } // A license may contain several types of keys (content, entitlement, // signing, key control and operator sessions); however, it should not // contain both entitlement keys and content keys. To determine the // overall type of the license, we check for the existence of either // type of keys. If both are present, we default to entitlement keys. CdmLicenseKeyType key_type = kLicenseKeyTypeEntitlement; std::vector key_array = ExtractEntitlementKeys(license); if (key_array.empty()) { key_array = ExtractContentKeys(license); key_type = kLicenseKeyTypeContent; } if (key_array.empty()) { LOGE("No content keys"); return NO_CONTENT_KEY; } license_key_type_ = key_type; if (license.has_provider_client_token()) provider_client_token_ = license.provider_client_token(); if (license.has_srm_update()) { status = crypto_session_->LoadSrm(license.srm_update()); switch (status) { case NO_ERROR: break; case SYSTEM_INVALIDATED_ERROR: return status; default: break; // Ignore } } if (license.id().type() == video_widevine::OFFLINE && license.policy().can_persist()) is_offline_ = true; if (license.id().has_provider_session_token()) provider_session_token_ = license.id().provider_session_token(); LOGV("provider_session_token = %s", provider_session_token_.empty() ? "N/A" : provider_session_token_.c_str()); if (license.policy().has_renewal_server_url()) { server_url_ = license.policy().renewal_server_url(); } if (license.policy().has_always_include_client_id()) { renew_with_client_id_ = license.policy().always_include_client_id(); } CdmResponseType resp = NO_CONTENT_KEY; if (kLicenseKeyTypeEntitlement == key_type) { resp = HandleEntitlementKeyResponse(is_restore, signed_message, core_message, signature, mac_key_iv, mac_keys, key_array, license); } else if (kLicenseKeyTypeContent == key_type) { resp = HandleContentKeyResponse(is_restore, signed_message, core_message, signature, mac_key_iv, mac_keys, key_array, license); } return resp; } CdmResponseType CdmLicense::HandleKeyUpdateResponse( bool is_renewal, bool is_restore, const CdmKeyResponse& license_response) { if (!initialized_) { LOGE("CdmLicense not initialized"); return LICENSE_PARSER_NOT_INITIALIZED_3; } if (license_response.empty()) { LOGE("License response is empty"); return EMPTY_LICENSE_RESPONSE_2; } SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { LOGE("Unable to parse signed message"); return LICENSE_RESPONSE_PARSE_ERROR_2; } switch (signed_response.type()) { case SignedMessage::LICENSE: break; case SignedMessage::ERROR_RESPONSE: return HandleKeyErrorResponse(signed_response); default: LOGE("Unrecognized signed message type: type = %d", static_cast(signed_response.type())); return INVALID_LICENSE_TYPE; } // At this point of the license life-cycle (handling a renewal), we should // already know if the license is v15 or not. If license is v16, then a // renewal should have a |core_message| present; otherwise there might have // been some tampering with the request or response. On the other hand, a // release is processed without loading the license, so OEMCrypto does not // know if it is v15 or v16, and will not add a core message. if (is_renewal && supports_core_messages() && (!signed_response.has_oemcrypto_core_message() || signed_response.oemcrypto_core_message().empty())) { LOGE("Renewal response is missing |core_message| field"); return CORE_MESSAGE_NOT_FOUND; } if (!signed_response.has_signature()) { LOGE("Update key response is missing signature"); return SIGNATURE_NOT_FOUND; } const std::string& signed_message = signed_response.msg(); const std::string core_message = signed_response.has_oemcrypto_core_message() ? signed_response.oemcrypto_core_message() : std::string(); const std::string& signature = signed_response.signature(); License license; if (!license.ParseFromString(signed_message)) { LOGE("Unable to parse license from signed message"); return LICENSE_RESPONSE_PARSE_ERROR_3; } if (!license.has_id()) { LOGE("License ID not present"); return LICENSE_ID_NOT_FOUND; } if (license.policy().has_always_include_client_id()) { renew_with_client_id_ = license.policy().always_include_client_id(); } if (!is_renewal) { if (!license.id().has_provider_session_token()) return KEY_ADDED; provider_session_token_ = license.id().provider_session_token(); return KEY_ADDED; } if (license.policy().has_renewal_server_url() && license.policy().renewal_server_url().size() > 0) { server_url_ = license.policy().renewal_server_url(); } CdmResponseType status; if (supports_core_messages()) { status = crypto_session_->LoadRenewal(signed_message, core_message, signature); } else { std::vector key_array = ExtractContentKeys(license); status = crypto_session_->RefreshKeys(signed_message, signature, key_array); } if (status == KEY_ADDED) { policy_engine_->UpdateLicense(license, is_restore); } return status; } CdmResponseType CdmLicense::HandleEmbeddedKeyData( const InitializationData& init_data) { return HandleNewEntitledKeys(init_data.ExtractWrappedKeys()); } CdmResponseType CdmLicense::RestoreOfflineLicense( const std::string& client_token, const CdmKeyMessage& license_request, const CdmKeyResponse& license_response, const CdmKeyResponse& license_renewal_response, int64_t playback_start_time, int64_t last_playback_time, int64_t grace_period_end_time, CdmSession* cdm_session) { if (license_request.empty()) { LOGE("License request is empty"); return EMPTY_LICENSE_REQUEST_2; } if (license_response.empty()) { LOGE("License response is empty"); return EMPTY_LICENSE_RESPONSE_3; } client_token_ = client_token; SignedMessage signed_request; if (!signed_request.ParseFromString(license_request)) { LOGE("Failed to parse license request"); return PARSE_REQUEST_ERROR_1; } if (signed_request.type() != SignedMessage::LICENSE_REQUEST) { LOGE("Unexpected license request type: expected = %d, actual = %d", static_cast(SignedMessage::LICENSE_REQUEST), static_cast(signed_request.type())); return INVALID_LICENSE_REQUEST_TYPE_1; } if (!signed_request.has_oemcrypto_core_message() || signed_request.oemcrypto_core_message().empty()) { // Pre V16 license did not include |core_message| components. // The license response is checked by HandleKeyResponse(). supports_core_messages_ = false; } key_request_ = signed_request.msg(); LicenseRequest original_license_request; if (!original_license_request.ParseFromString(key_request_)) { LOGW("Could not parse original request."); } else { license_nonce_ = original_license_request.key_control_nonce(); } CdmResponseType sts = HandleKeyResponse(true, license_response); if (sts != KEY_ADDED) return sts; if (!license_renewal_response.empty()) { sts = PrepareKeyUpdateReload(cdm_session); if (sts != KEY_MESSAGE && sts != NO_ERROR) return sts; sts = HandleKeyUpdateResponse(true, true, license_renewal_response); if (sts != KEY_ADDED) return sts; } if (!provider_session_token_.empty()) { if (cdm_session && cdm_session->supports_usage_info()) { const CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); if (NO_ERROR != status) return sts; } std::string usage_report; CryptoSession::UsageDurationStatus usage_duration_status = CryptoSession::kUsageDurationsInvalid; int64_t seconds_since_started, seconds_since_last_played; sts = crypto_session_->GenerateUsageReport( provider_session_token_, &usage_report, &usage_duration_status, &seconds_since_started, &seconds_since_last_played); if (NO_ERROR == sts) { switch (usage_duration_status) { case CryptoSession::kUsageDurationPlaybackNotBegun: playback_start_time = 0; last_playback_time = 0; break; case CryptoSession::kUsageDurationsValid: { int64_t current_time = clock_->GetCurrentTime(); if (current_time - seconds_since_started > 0) playback_start_time = current_time - seconds_since_started; if (current_time - last_playback_time > 0) last_playback_time = current_time - seconds_since_last_played; break; } default: break; } } } policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time, grace_period_end_time); return NO_ERROR; } CdmResponseType CdmLicense::RestoreLicenseForRelease( const std::string& client_token, const CdmKeyMessage& license_request, const CdmKeyResponse& license_response) { if (license_request.empty()) { LOGE("License request is empty"); return EMPTY_LICENSE_REQUEST_3; } if (license_response.empty()) { LOGE("License response is empty"); return EMPTY_LICENSE_RESPONSE_4; } client_token_ = client_token; SignedMessage signed_request; if (!signed_request.ParseFromString(license_request)) { LOGE("Failed to parse signed license request"); return PARSE_REQUEST_ERROR_2; } if (signed_request.type() != SignedMessage::LICENSE_REQUEST) { LOGE("Unexpected signed license request type: expected = %d, actual = %d", static_cast(SignedMessage::LICENSE_REQUEST), static_cast(signed_request.type())); return INVALID_LICENSE_REQUEST_TYPE_2; } if (!signed_request.has_oemcrypto_core_message()) { // Pre V16 license did not include |core_message| components. supports_core_messages_ = false; } key_request_ = signed_request.msg(); SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { LOGE("Failed to parse signed license response"); return LICENSE_RESPONSE_PARSE_ERROR_4; } if (SignedMessage::LICENSE != signed_response.type()) { LOGE("Unexpected signed license response type: expected = %d, actual = %d", static_cast(SignedMessage::LICENSE), static_cast(signed_response.type())); return INVALID_LICENSE_TYPE_2; } if (!signed_response.has_signature()) { LOGE("License response is not signed"); return SIGNATURE_NOT_FOUND_2; } if (!signed_response.has_oemcrypto_core_message() || signed_response.oemcrypto_core_message().empty()) { // Possible that the request contains a |core_message|, but the // response does not. This would occur if the licensing server // is v15. supports_core_messages_ = false; } License license; if (!license.ParseFromString(signed_response.msg())) { LOGE("Failed to parse license response"); return LICENSE_RESPONSE_PARSE_ERROR_5; } if (license.has_provider_client_token()) provider_client_token_ = license.provider_client_token(); if (license.id().has_provider_session_token()) provider_session_token_ = license.id().provider_session_token(); if (license.policy().has_always_include_client_id()) renew_with_client_id_ = license.policy().always_include_client_id(); if (!signed_response.has_session_key()) { LOGE("No session keys present"); return SESSION_KEYS_NOT_FOUND_2; } if (!license.id().has_provider_session_token()) { CdmResponseType result = HandleKeyResponse(false, license_response); return result == KEY_ADDED ? NO_ERROR : result; } if (license.policy().has_renewal_server_url()) server_url_ = license.policy().renewal_server_url(); // If the policy engine already has keys, they will now expire. // If the policy engine does not already have keys, this will not add any. policy_engine_->SetLicenseForRelease(license, supports_core_messages()); return NO_ERROR; } bool CdmLicense::IsKeyLoaded(const KeyId& key_id) { return loaded_keys_.find(key_id) != loaded_keys_.end(); } bool CdmLicense::ExtractProviderSessionToken( const CdmKeyResponse& license_response, std::string* provider_session_token) { if (license_response.empty()) { LOGE("License response is empty"); return false; } SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { LOGE("Failed to parse signed license response"); return false; } if (signed_response.type() != SignedMessage::LICENSE) { LOGE("Unexpected signed license response type: expected = %d, actual = %d", static_cast(SignedMessage::LICENSE), static_cast(signed_response.type())); return false; } License license; if (!license.ParseFromString(signed_response.msg())) { LOGE("Failed to parse license response"); return false; } if (license.id().has_provider_session_token() && !license.id().provider_session_token().empty()) { *provider_session_token = license.id().provider_session_token(); return true; } return false; } CdmResponseType CdmLicense::HandleKeyErrorResponse( const SignedMessage& signed_message) { LicenseError license_error; if (!license_error.ParseFromString(signed_message.msg())) { LOGE("Failed to parse license error response"); return KEY_ERROR; } switch (license_error.error_code()) { case LicenseError::INVALID_DRM_DEVICE_CERTIFICATE: return NEED_PROVISIONING; case LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE: return DEVICE_REVOKED; case LicenseError::SERVICE_UNAVAILABLE: default: LOGW("Unknown error type: error_code = %d", static_cast(license_error.error_code())); return KEY_ERROR; } } CdmResponseType CdmLicense::PrepareClientId( const CdmAppParameterMap& app_parameters, const std::string& provider_client_token, LicenseRequest* license_request) { wvcdm::ClientIdentification id; if (client_token_.empty()) { LOGE("Client token not set when preparing client ID"); return CLIENT_TOKEN_NOT_SET; } CdmResponseType status = id.Init(client_token_, crypto_session_); if (status != NO_ERROR) return status; video_widevine::ClientIdentification* client_id = license_request->mutable_client_id(); status = id.Prepare(app_parameters, provider_client_token, client_id); if (status != NO_ERROR) return status; if (Properties::UsePrivacyMode(session_id_)) { if (!service_certificate_.has_certificate()) { LOGE("Service certificate not staged"); return PRIVACY_MODE_ERROR_3; } EncryptedClientIdentification* encrypted_client_id = license_request->mutable_encrypted_client_id(); status = service_certificate_.EncryptClientId(crypto_session_, client_id, encrypted_client_id); if (NO_ERROR == status) { license_request->clear_client_id(); } else { license_request->clear_encrypted_client_id(); } return status; } return NO_ERROR; } CdmResponseType CdmLicense::PrepareContentId( const InitializationData& init_data, CdmLicenseType license_type, const std::string& request_id, LicenseRequest* license_request) { // Content Identification may be a cenc_id, a webm_id or a license_id LicenseRequest_ContentIdentification* content_id = license_request->mutable_content_id(); if (init_data.is_cenc() || init_data.is_hls()) { LicenseRequest_ContentIdentification_WidevinePsshData* widevine_pssh_data = content_id->mutable_widevine_pssh_data(); if (!init_data.IsEmpty()) { widevine_pssh_data->add_pssh_data(init_data.data()); } else { LOGE("ISO-CENC init data not available"); return CENC_INIT_DATA_UNAVAILABLE; } if (!SetTypeAndId(license_type, request_id, widevine_pssh_data)) { return PREPARE_CENC_CONTENT_ID_FAILED; } } else if (init_data.is_webm()) { LicenseRequest_ContentIdentification_WebmKeyId* webm_key_id = content_id->mutable_webm_key_id(); if (!init_data.IsEmpty()) { webm_key_id->set_header(init_data.data()); } else { LOGE("WebM init data not available"); return WEBM_INIT_DATA_UNAVAILABLE; } if (!SetTypeAndId(license_type, request_id, webm_key_id)) { return PREPARE_WEBM_CONTENT_ID_FAILED; } } else { LOGE("Unsupported init data type: type = %s", init_data.type().c_str()); return UNSUPPORTED_INIT_DATA_FORMAT; } return NO_ERROR; } CdmResponseType CdmLicense::HandleContentKeyResponse( bool is_restore, const std::string& msg, const std::string& core_message, const std::string& signature, const std::string& mac_key_iv, const std::string& mac_key, const std::vector& key_array, const video_widevine::License& license) { if (key_array.empty()) { LOGE("No content keys provided"); return NO_CONTENT_KEY; } CdmResponseType resp; if (supports_core_messages()) { resp = crypto_session_->LoadLicense(msg, core_message, signature, kLicenseKeyTypeContent); } else { resp = crypto_session_->LoadKeys( msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_, license.srm_requirement(), kLicenseKeyTypeContent); } if (KEY_ADDED == resp) { loaded_keys_.clear(); for (std::vector::const_iterator it = key_array.begin(); it != key_array.end(); ++it) { loaded_keys_.insert(it->key_id()); } policy_engine_->SetLicense(license, supports_core_messages(), is_restore); } return resp; } CdmResponseType CdmLicense::HandleEntitlementKeyResponse( bool is_restore, const std::string& msg, const std::string& core_message, const std::string& signature, const std::string& mac_key_iv, const std::string& mac_key, const std::vector& key_array, const video_widevine::License& license) { if (key_array.empty()) { LOGE("No entitlement keys provided"); return NO_CONTENT_KEY; } CdmResponseType resp; if (supports_core_messages()) { resp = crypto_session_->LoadLicense(msg, core_message, signature, kLicenseKeyTypeEntitlement); } else { resp = crypto_session_->LoadKeys( msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_, license.srm_requirement(), kLicenseKeyTypeEntitlement); } if (KEY_ADDED != resp) { return resp; } // Save the entitlement keys for future use to handle key changes. entitlement_keys_.CopyFrom(license.key()); policy_engine_->SetLicense(license, supports_core_messages(), is_restore); return HandleNewEntitledKeys(wrapped_keys_); } CdmResponseType CdmLicense::HandleNewEntitledKeys( const std::vector& wrapped_keys) { std::vector entitled_key_array; entitled_key_array.reserve(wrapped_keys.size()); for (RepeatedPtrField::const_iterator kc = entitlement_keys_.begin(); kc != entitlement_keys_.end(); ++kc) { if (kc->type() != video_widevine::License::KeyContainer::ENTITLEMENT) { continue; } for (std::vector::const_iterator wk = wrapped_keys.begin(); wk != wrapped_keys.end(); wk++) { if (wk->entitlement_key_id() == kc->id()) { // Add a new entry to the key array to load oemcrypto. entitled_key_array.resize(entitled_key_array.size() + 1); // Strip PKCS#5 padding from entitled content keys. std::string content_key = wk->key(); if (content_key.size() < CONTENT_KEY_SIZE) { LOGE( "Entitled content key too small: " "expected = %zu, actual = %zu (bytes)", CONTENT_KEY_SIZE, content_key.size()); return KEY_SIZE_ERROR_2; } else if (content_key.size() > CONTENT_KEY_SIZE) { content_key.resize(CONTENT_KEY_SIZE); } CryptoKey& this_entry = entitled_key_array.back(); this_entry.set_key_id(wk->key_id()); this_entry.set_key_data_iv(wk->iv()); this_entry.set_entitlement_key_id(wk->entitlement_key_id()); this_entry.set_key_data(content_key); } } } CdmResponseType resp = crypto_session_->LoadEntitledContentKeys(entitled_key_array); if (KEY_ADDED == resp) { for (std::vector::const_iterator it = wrapped_keys.begin(); it != wrapped_keys.end(); ++it) { loaded_keys_.insert(it->key_id()); } policy_engine_->SetEntitledLicenseKeys(wrapped_keys); } return resp; } template bool CdmLicense::SetTypeAndId(CdmLicenseType license_type, const std::string& request_id, T* content_id) { switch (license_type) { case kLicenseTypeOffline: content_id->set_license_type(video_widevine::OFFLINE); break; case kLicenseTypeStreaming: case kLicenseTypeTemporary: content_id->set_license_type(video_widevine::STREAMING); break; default: LOGD("Unknown license type: %d", static_cast(license_type)); return false; } content_id->set_request_id(request_id); return true; } } // namespace wvcdm