// Copyright 2012 Google Inc. All Rights Reserved. #include "license.h" #include #include #include "clock.h" #include "crypto_key.h" #include "crypto_session.h" #include "device_files.h" #include "log.h" #include "policy_engine.h" #include "properties.h" #include "privacy_crypto.h" #include "string_conversions.h" #include "wv_cdm_constants.h" namespace { std::string kCompanyNameKey = "company_name"; std::string kModelNameKey = "model_name"; std::string kArchitectureNameKey = "architecture_name"; std::string kDeviceNameKey = "device_name"; std::string kProductNameKey = "product_name"; std::string kBuildInfoKey = "build_info"; std::string kDeviceIdKey = "device_id"; std::string kOsVersion = "os_version"; const unsigned char kServiceCertificateCAPublicKey[] = { 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e, 0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01}; } namespace wvcdm { // Protobuf generated classes. using video_widevine_server::sdk::ClientIdentification; using video_widevine_server::sdk::ClientIdentification_ClientCapabilities; using video_widevine_server::sdk::ClientIdentification_NameValue; using video_widevine_server::sdk::DeviceCertificate; using video_widevine_server::sdk::EncryptedClientIdentification; using video_widevine_server::sdk::License; using video_widevine_server::sdk::License_KeyContainer; using video_widevine_server::sdk::LicenseError; using video_widevine_server::sdk::LicenseIdentification; using video_widevine_server::sdk::LicenseRequest; using video_widevine_server::sdk::LicenseRequest_ContentIdentification; using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC; using video_widevine_server::sdk::LicenseRequest_ContentIdentification_WebM; using video_widevine_server::sdk:: LicenseRequest_ContentIdentification_ExistingLicense; using video_widevine_server::sdk::SignedDeviceCertificate; using video_widevine_server::sdk::SignedMessage; static 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. 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()); } 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 key types as they are not content related break; } } return key_array; } CdmLicense::CdmLicense() : session_(NULL), initialized_(false), clock_(new Clock()) { } CdmLicense::CdmLicense(Clock* clock) : session_(NULL), initialized_(false) { if (NULL == clock) { LOGE("CdmLicense::CdmLicense: clock parameter not provided"); return; } clock_.reset(clock); } CdmLicense::~CdmLicense() {} bool CdmLicense::Init(const std::string& token, CryptoSession* session, PolicyEngine* policy_engine) { if (token.size() == 0) { LOGE("CdmLicense::Init: empty token provided"); return false; } if (session == NULL || !session->IsOpen()) { LOGE("CdmLicense::Init: crypto session not provided or not open"); return false; } if (policy_engine == NULL) { LOGE("CdmLicense::Init: no policy engine provided"); return false; } token_ = token; session_ = session; policy_engine_ = policy_engine; initialized_ = true; return true; } bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data, const CdmLicenseType license_type, const CdmAppParameterMap& app_parameters, const CdmSessionId& session_id, CdmKeyMessage* signed_request, std::string* server_url) { if (!initialized_) { LOGE("CdmLicense::PrepareKeyRequest: not initialized"); return false; } if (!init_data.is_supported()) { LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)", init_data.type().c_str()); return false; } if (init_data.IsEmpty() && stored_init_data_.empty()) { LOGE("CdmLicense::PrepareKeyRequest: empty init data provided"); return false; } if (session_id.empty()) { LOGE("CdmLicense::PrepareKeyRequest: empty session id provided"); return false; } if (!signed_request) { LOGE("CdmLicense::PrepareKeyRequest: no signed request provided"); return false; } if (!server_url) { LOGE("CdmLicense::PrepareKeyRequest: no server url provided"); return false; } bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id); std::string serialized_service_certificate; if (!Properties::GetServiceCertificate(session_id, &serialized_service_certificate) || serialized_service_certificate.empty()) serialized_service_certificate = service_certificate_; if (privacy_mode_enabled && serialized_service_certificate.empty()) { stored_init_data_ = init_data.data(); return PrepareServiceCertificateRequest(signed_request, server_url); } std::string request_id; session_->GenerateRequestId(&request_id); LicenseRequest license_request; ClientIdentification* client_id = license_request.mutable_client_id(); if (Properties::use_certificates_as_identification()) client_id->set_type(ClientIdentification::DEVICE_CERTIFICATE); else client_id->set_type(ClientIdentification::KEYBOX); client_id->set_token(token_); ClientIdentification_NameValue* client_info; CdmAppParameterMap::const_iterator iter; for (iter = app_parameters.begin(); iter != app_parameters.end(); iter++) { client_info = client_id->add_client_info(); client_info->set_name(iter->first); client_info->set_value(iter->second); } std::string value; if (Properties::GetCompanyName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kCompanyNameKey); client_info->set_value(value); } if (Properties::GetModelName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kModelNameKey); client_info->set_value(value); } if (Properties::GetArchitectureName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kArchitectureNameKey); client_info->set_value(value); } if (Properties::GetDeviceName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kDeviceNameKey); client_info->set_value(value); } if (Properties::GetProductName(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kProductNameKey); client_info->set_value(value); } if (Properties::GetBuildInfo(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kBuildInfoKey); client_info->set_value(value); } if (session_->GetDeviceUniqueId(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kDeviceIdKey); client_info->set_value(value); } if (Properties::GetOSVersion(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kOsVersionKey); client_info->set_value(value); } ClientIdentification_ClientCapabilities* client_capabilities = client_id->mutable_client_capabilities(); bool supports_usage_information; if (session_->UsageInformationSupport(&supports_usage_information)) { client_capabilities->set_session_token(supports_usage_information); } CryptoSession::OemCryptoHdcpVersion current_version, max_version; if (session_->GetHdcpCapabilities(¤t_version, &max_version)) { switch (max_version) { case CryptoSession::kOemCryptoHdcpNotSupported: client_capabilities->set_max_hdcp_version( video_widevine_server::sdk:: ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE); break; case CryptoSession::kOemCryptoHdcpVersion1: client_capabilities->set_max_hdcp_version( video_widevine_server::sdk:: ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1); break; case CryptoSession::kOemCryptoHdcpVersion2: client_capabilities->set_max_hdcp_version( video_widevine_server::sdk:: ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2); break; case CryptoSession::kOemCryptoHdcpVersion2_1: client_capabilities->set_max_hdcp_version( video_widevine_server::sdk:: ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1); break; case CryptoSession::kOemCryptoHdcpVersion2_2: client_capabilities->set_max_hdcp_version( video_widevine_server::sdk:: ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2); break; case CryptoSession::kOemCryptoNoHdcpDeviceAttached: default: LOGW( "CdmLicense::PrepareKeyRequest: unexpected HDCP max capability " "version %d", max_version); } } uint32_t version = 0; if (session_->GetApiVersion(&version)) { client_capabilities->set_oem_crypto_api_version(version); } if (privacy_mode_enabled) { EncryptedClientIdentification* encrypted_client_id = license_request.mutable_encrypted_client_id(); DeviceCertificate service_certificate; if (!service_certificate.ParseFromString(serialized_service_certificate)) { LOGE( "CdmLicense::PrepareKeyRequest: unable to parse retrieved " "service certificate"); return false; } if (service_certificate.type() != video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) { LOGE( "CdmLicense::PrepareKeyRequest: retrieved certificate not of type" " service, %d", service_certificate.type()); return false; } encrypted_client_id->set_service_id(service_certificate.service_id()); encrypted_client_id->set_service_certificate_serial_number( service_certificate.serial_number()); std::string iv(KEY_IV_SIZE, 0); std::string key(KEY_SIZE, 0); if (!session_->GetRandom(key.size(), reinterpret_cast(&key[0]))) { return false; } if (!session_->GetRandom(iv.size(), reinterpret_cast(&iv[0]))) { return false; } std::string id, enc_id, enc_key; client_id->SerializeToString(&id); AesCbcKey aes; if (!aes.Init(key)) return false; if (!aes.Encrypt(id, &enc_id, &iv)) return false; RsaPublicKey rsa; if (!rsa.Init(service_certificate.public_key())) return false; if (!rsa.Encrypt(key, &enc_key)) return false; encrypted_client_id->set_encrypted_client_id_iv(iv); encrypted_client_id->set_encrypted_privacy_key(enc_key); encrypted_client_id->set_encrypted_client_id(enc_id); license_request.clear_client_id(); } // 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()) { LicenseRequest_ContentIdentification_CENC* cenc_content_id = content_id->mutable_cenc_id(); if (!init_data.IsEmpty()) { cenc_content_id->add_pssh(init_data.data()); } else if (privacy_mode_enabled && !stored_init_data_.empty()) { cenc_content_id->add_pssh(stored_init_data_); } else { LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available"); return false; } if (!PrepareContentId(license_type, request_id, cenc_content_id)) { return false; } } else if (init_data.is_webm()) { LicenseRequest_ContentIdentification_WebM* webm_content_id = content_id->mutable_webm_id(); if (!init_data.IsEmpty()) { webm_content_id->set_header(init_data.data()); } else if (privacy_mode_enabled && !stored_init_data_.empty()) { webm_content_id->set_header(stored_init_data_); } else { LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available"); return false; } if (!PrepareContentId(license_type, request_id, webm_content_id)) { return false; } } else { LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)", init_data.type().c_str()); return false; } 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. uint32_t nonce; if (!session_->GenerateNonce(&nonce)) { return false; } license_request.set_key_control_nonce(nonce); LOGD("PrepareKeyRequest: nonce=%u", nonce); license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1); // License request is complete. Serialize it. std::string serialized_license_req; license_request.SerializeToString(&serialized_license_req); if (Properties::use_certificates_as_identification()) key_request_ = serialized_license_req; // Derive signing and encryption keys and construct signature. std::string license_request_signature; if (!session_->PrepareRequest(serialized_license_req, false, &license_request_signature)) { signed_request->clear(); return false; } if (license_request_signature.empty()) { LOGE("CdmLicense::PrepareKeyRequest: License request signature empty"); signed_request->clear(); return false; } // 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); signed_message.SerializeToString(signed_request); *server_url = server_url_; return true; } bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, std::string* server_url) { if (!initialized_) { LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized"); return false; } if (!signed_request) { LOGE("CdmLicense::PrepareKeyUpdateRequest: No signed request provided"); return false; } if (!server_url) { LOGE("CdmLicense::PrepareKeyUpdateRequest: No server url provided"); return false; } 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()); LicenseRequest_ContentIdentification_ExistingLicense* current_license = license_request.mutable_content_id()->mutable_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 = session_->DeactivateUsageInformation(provider_session_token_); if (NO_ERROR != status) return false; } std::string usage_report; CdmResponseType status = 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 false; } } 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); } // Get/set the nonce. This value will be reflected in the Key Control Block // of the license response. uint32_t nonce; if (!session_->GenerateNonce(&nonce)) { return false; } license_request.set_key_control_nonce(nonce); LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce); license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1); // License request is complete. Serialize it. std::string serialized_license_req; license_request.SerializeToString(&serialized_license_req); // Construct signature. std::string license_request_signature; if (!session_->PrepareRenewalRequest(serialized_license_req, &license_request_signature)) return false; if (license_request_signature.empty()) { LOGE( "CdmLicense::PrepareKeyUpdateRequest: empty license request" " signature"); return false; } // 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); signed_message.SerializeToString(signed_request); *server_url = server_url_; return true; } CdmResponseType CdmLicense::HandleKeyResponse( const CdmKeyResponse& license_response) { if (!initialized_) { LOGE("CdmLicense::HandleKeyResponse: not initialized"); return KEY_ERROR; } if (license_response.empty()) { LOGE("CdmLicense::HandleKeyResponse: empty license response"); return KEY_ERROR; } SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { LOGE( "CdmLicense::HandleKeyResponse: unable to parse signed license" " response"); return KEY_ERROR; } switch (signed_response.type()) { case SignedMessage::LICENSE: break; case SignedMessage::SERVICE_CERTIFICATE: return CdmLicense::HandleServiceCertificateResponse(signed_response); case SignedMessage::ERROR_RESPONSE: return HandleKeyErrorResponse(signed_response); default: LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d" , signed_response.type()); return KEY_ERROR; } if (!signed_response.has_signature()) { LOGE("CdmLicense::HandleKeyResponse: license response is not signed"); return KEY_ERROR; } License license; if (!license.ParseFromString(signed_response.msg())) { LOGE("CdmLicense::HandleKeyResponse: unable to parse license response"); return KEY_ERROR; } if (Properties::use_certificates_as_identification()) { if (!signed_response.has_session_key()) { LOGE("CdmLicense::HandleKeyResponse: no session keys present"); return KEY_ERROR; } if (!session_->GenerateDerivedKeys(key_request_, signed_response.session_key())) return KEY_ERROR; } // Extract mac key std::string mac_key_iv; std::string mac_key; if (license.policy().can_renew()) { 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_key.assign(license.key(i).key().data(), MAC_KEY_SIZE); } } if (mac_key_iv.size() != KEY_IV_SIZE || mac_key.size() != MAC_KEY_SIZE) { LOGE( "CdmLicense::HandleKeyResponse: mac key/iv size error" "(key/iv size expected: %d/%d, actual: %d/%d", MAC_KEY_SIZE, KEY_IV_SIZE, mac_key.size(), mac_key_iv.size()); return KEY_ERROR; } } std::vector key_array = ExtractContentKeys(license); if (!key_array.size()) { LOGE("CdmLicense::HandleKeyResponse : No content keys."); return KEY_ERROR; } if (license.id().has_provider_session_token()) provider_session_token_ = license.id().provider_session_token(); if (license.policy().has_renewal_server_url()) { server_url_ = license.policy().renewal_server_url(); } policy_engine_->SetLicense(license); CdmResponseType resp = session_->LoadKeys(signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key, key_array, provider_session_token_); if (KEY_ADDED == resp) { loaded_keys_.clear(); for (std::vector::iterator it = key_array.begin(); it != key_array.end(); ++it) { loaded_keys_.insert(it->key_id()); } } return resp; } CdmResponseType CdmLicense::HandleKeyUpdateResponse( bool is_renewal, const CdmKeyResponse& license_response) { if (!initialized_) { LOGE("CdmLicense::HandleKeyUpdateResponse: not initialized"); return KEY_ERROR; } if (license_response.empty()) { LOGE("CdmLicense::HandleKeyUpdateResponse : Empty license response."); return KEY_ERROR; } SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse signed message"); return KEY_ERROR; } if (signed_response.type() == SignedMessage::ERROR_RESPONSE) { return HandleKeyErrorResponse(signed_response); } if (!signed_response.has_signature()) { LOGE("CdmLicense::HandleKeyUpdateResponse: signature missing"); return KEY_ERROR; } License license; if (!license.ParseFromString(signed_response.msg())) { LOGE( "CdmLicense::HandleKeyUpdateResponse: Unable to parse license" " from signed message"); return KEY_ERROR; } if (!license.has_id()) { LOGE("CdmLicense::HandleKeyUpdateResponse: license id not present"); return KEY_ERROR; } policy_engine_->UpdateLicense(license); if (!is_renewal) { if (!license.id().has_provider_session_token()) return KEY_ADDED; provider_session_token_ = license.id().provider_session_token(); CdmResponseType status = session_->ReleaseUsageInformation(signed_response.msg(), signed_response.signature(), provider_session_token_); return (NO_ERROR == status) ? KEY_ADDED : status; } if (license.policy().has_renewal_server_url() && license.policy().renewal_server_url().size() > 0) { server_url_ = license.policy().renewal_server_url(); } std::vector key_array = ExtractContentKeys(license); if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(), key_array.size(), &key_array[0])) { return KEY_ADDED; } else { return KEY_ERROR; } } bool CdmLicense::RestoreOfflineLicense( const CdmKeyMessage& license_request, const CdmKeyResponse& license_response, const CdmKeyResponse& license_renewal_response, int64_t playback_start_time, int64_t last_playback_time) { if (license_request.empty() || license_response.empty()) { LOGE( "CdmLicense::RestoreOfflineLicense: key_request or response empty: " "%u %u", license_request.size(), license_response.size()); return false; } SignedMessage signed_request; if (!signed_request.ParseFromString(license_request)) { LOGE("CdmLicense::RestoreOfflineLicense: license_request parse failed"); return false; } if (signed_request.type() != SignedMessage::LICENSE_REQUEST) { LOGE( "CdmLicense::RestoreOfflineLicense: license request type: expected = " "%d, actual = %d", SignedMessage::LICENSE_REQUEST, signed_request.type()); return false; } if (Properties::use_certificates_as_identification()) { key_request_ = signed_request.msg(); } else { if (!session_->GenerateDerivedKeys(signed_request.msg())) return false; } CdmResponseType sts = HandleKeyResponse(license_response); if (sts != KEY_ADDED) return false; if (!license_renewal_response.empty()) { sts = HandleKeyUpdateResponse(true, license_renewal_response); if (sts != KEY_ADDED) return false; } if (!provider_session_token_.empty()) { std::string usage_report; CryptoSession::UsageDurationStatus usage_duration_status = CryptoSession::kUsageDurationsInvalid; int64_t seconds_since_started, seconds_since_last_played; sts = 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); return true; } bool CdmLicense::RestoreLicenseForRelease( const CdmKeyMessage& license_request, const CdmKeyResponse& license_response) { if (license_request.empty() || license_response.empty()) { LOGE( "CdmLicense::RestoreLicenseForRelease: key_request or response empty:" " %u %u", license_request.size(), license_response.size()); return false; } SignedMessage signed_request; if (!signed_request.ParseFromString(license_request)) { LOGE("CdmLicense::RestoreLicenseForRelease: license_request parse failed"); return false; } if (signed_request.type() != SignedMessage::LICENSE_REQUEST) { LOGE( "CdmLicense::RestoreLicenseForRelease: license request type: expected " "= %d, actual = %d", SignedMessage::LICENSE_REQUEST, signed_request.type()); return false; } if (Properties::use_certificates_as_identification()) { key_request_ = signed_request.msg(); } else { if (!session_->GenerateDerivedKeys(signed_request.msg())) return false; } SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { LOGE("CdmLicense::RestoreLicenseForRelease: unable to parse signed license" " response"); return false; } if (SignedMessage::LICENSE != signed_response.type()) { LOGE("CdmLicense::RestoreLicenseForRelease: unrecognized signed message " "type: %d" , signed_response.type()); return false; } if (!signed_response.has_signature()) { LOGE("CdmLicense::RestoreLicenseForRelease: license response is not" " signed"); return false; } License license; if (!license.ParseFromString(signed_response.msg())) { LOGE("CdmLicense::RestoreLicenseForRelease: unable to parse license" " response"); return false; } if (license.id().has_provider_session_token()) provider_session_token_ = license.id().provider_session_token(); if (Properties::use_certificates_as_identification()) { if (!signed_response.has_session_key()) { LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present"); return false; } if (license.id().has_provider_session_token()) { if (!session_->GenerateDerivedKeys(key_request_, signed_response.session_key())) return false; } else { return KEY_ADDED == HandleKeyResponse(license_response); } } if (license.policy().has_renewal_server_url()) server_url_ = license.policy().renewal_server_url(); policy_engine_->SetLicense(license); return true; } bool CdmLicense::IsKeyLoaded(const KeyId& key_id) { return loaded_keys_.find(key_id) != loaded_keys_.end(); } bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, std::string* server_url) { if (!initialized_) { LOGE("CdmLicense::PrepareServiceCertificateRequest: not initialized"); return false; } if (!signed_request) { LOGE( "CdmLicense::PrepareServiceCertificateRequest: no signed request" " provided"); return false; } if (!server_url) { LOGE( "CdmLicense::PrepareServiceCertificateRequest: no server url" " provided"); return false; } SignedMessage signed_message; signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST); signed_message.SerializeToString(signed_request); *server_url = server_url_; return true; } CdmResponseType CdmLicense::HandleServiceCertificateResponse( const video_widevine_server::sdk::SignedMessage& signed_response) { SignedDeviceCertificate signed_service_certificate; if (!signed_service_certificate.ParseFromString(signed_response.msg())) { LOGE( "CdmLicense::HandleServiceCertificateResponse: unable to parse" "signed device certificate"); return KEY_ERROR; } RsaPublicKey root_ca_key; std::string ca_public_key( &kServiceCertificateCAPublicKey[0], &kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]); if (!root_ca_key.Init(ca_public_key)) { LOGE( "CdmLicense::HandleServiceCertificateResponse: public key " "initialization failed"); return KEY_ERROR; } if (!root_ca_key.VerifySignature( signed_service_certificate.device_certificate(), signed_service_certificate.signature())) { LOGE( "CdmLicense::HandleServiceCertificateResponse: service " "certificate verification failed"); return KEY_ERROR; } DeviceCertificate service_certificate; if (!service_certificate.ParseFromString( signed_service_certificate.device_certificate())) { LOGE( "CdmLicense::HandleServiceCertificateResponse: unable to parse " "retrieved service certificate"); return KEY_ERROR; } if (service_certificate.type() != video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) { LOGE( "CdmLicense::HandleServiceCertificateResponse: certificate not of type" " service, %d", service_certificate.type()); return KEY_ERROR; } service_certificate_ = signed_service_certificate.device_certificate(); return NEED_KEY; } CdmResponseType CdmLicense::HandleKeyErrorResponse( const SignedMessage& signed_message) { LicenseError license_error; if (!license_error.ParseFromString(signed_message.msg())) { LOGE("CdmLicense::HandleKeyErrorResponse: Unable to parse license error"); return KEY_ERROR; } switch (license_error.error_code()) { case LicenseError::INVALID_DEVICE_CERTIFICATE: return NEED_PROVISIONING; case LicenseError::REVOKED_DEVICE_CERTIFICATE: return DEVICE_REVOKED; case LicenseError::SERVICE_UNAVAILABLE: default: LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d", license_error.error_code()); return KEY_ERROR; } } template bool CdmLicense::PrepareContentId(const CdmLicenseType license_type, const std::string& request_id, T* content_id) { switch (license_type) { case kLicenseTypeOffline: content_id->set_license_type(video_widevine_server::sdk::OFFLINE); break; case kLicenseTypeStreaming: content_id->set_license_type( video_widevine_server::sdk::STREAMING); break; default: LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d", license_type); return false; } content_id->set_request_id(request_id); return true; } } // namespace wvcdm