Widevine MediaCas client code that works with Android R
This commit is contained in:
132
plugin/src/widevine_cas_session.cpp
Normal file
132
plugin/src/widevine_cas_session.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
#include "widevine_cas_session.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
KeySlot& CasKeySlotData::operator[](KeySlotId slot_id) {
|
||||
return keys_[static_cast<int>(slot_id)];
|
||||
}
|
||||
|
||||
const KeySlot& CasKeySlotData::operator[](KeySlotId slot_id) const {
|
||||
return keys_[static_cast<int>(slot_id)];
|
||||
}
|
||||
|
||||
WidevineCasSession::~WidevineCasSession() {
|
||||
if (crypto_session_ != nullptr) {
|
||||
crypto_session_->RemoveEntitledKeySession(key_session_id_);
|
||||
}
|
||||
}
|
||||
|
||||
CasStatus WidevineCasSession::initialize(
|
||||
std::shared_ptr<CryptoSession> crypto_session, uint32_t* session_id) {
|
||||
if (crypto_session == nullptr || session_id == nullptr) {
|
||||
LOGE("WidevineCasSession::initialize: missing input parameters");
|
||||
return CasStatus(CasStatusCode::kInvalidParameter,
|
||||
"missing input parameters");
|
||||
}
|
||||
crypto_session_ = std::move(crypto_session);
|
||||
crypto_session_->CreateEntitledKeySession(&key_session_id_);
|
||||
*session_id = key_session_id_;
|
||||
return CasStatusCode::kNoError;
|
||||
}
|
||||
|
||||
const KeySlot& WidevineCasSession::key(KeySlotId slot_id) const {
|
||||
// TODO(): Make this function private and assume the mutex is locked.
|
||||
// std::unique_lock<std::mutex> lock(lock_);
|
||||
const KeySlot& key_slot = keys_[slot_id];
|
||||
return key_slot;
|
||||
}
|
||||
|
||||
CasStatus WidevineCasSession::processEcm(const CasEcm& ecm,
|
||||
uint8_t parental_control_age) {
|
||||
if (ecm != current_ecm_) {
|
||||
LOGD("WidevineCasSession::processEcm: received new ecm");
|
||||
std::unique_ptr<const EcmParser> ecm_parser = getEcmParser(ecm);
|
||||
if (!ecm_parser) {
|
||||
return CasStatus(CasStatusCode::kInvalidParameter, "invalid ecm");
|
||||
}
|
||||
|
||||
ecm_age_restriction_ = ecm_parser->age_restriction();
|
||||
// Parental control check.
|
||||
if (parental_control_age > 0 &&
|
||||
parental_control_age < ecm_age_restriction_) {
|
||||
const std::string message(1, parental_control_age);
|
||||
return CasStatus(CasStatusCode::kAccessDeniedByParentalControl, message);
|
||||
}
|
||||
|
||||
bool load_even = false;
|
||||
bool load_odd = false;
|
||||
|
||||
std::unique_lock<std::mutex> lock(lock_);
|
||||
KeySlotId keyslot_id = KeySlotId::kEvenKeySlot;
|
||||
// Temporary key slots to only have successfully loaded keys in |keys_|.
|
||||
CasKeySlotData keys;
|
||||
do {
|
||||
if (keys_[keyslot_id].key_id != ecm_parser->content_key_id(keyslot_id)) {
|
||||
KeySlot& key = keys[keyslot_id];
|
||||
key.key_id = ecm_parser->content_key_id(keyslot_id);
|
||||
key.wrapped_key = ecm_parser->wrapped_key_data(keyslot_id);
|
||||
key.wrapped_key_iv = ecm_parser->wrapped_key_iv(keyslot_id);
|
||||
key.entitlement_key_id = ecm_parser->entitlement_key_id(keyslot_id);
|
||||
key.cipher_mode = ecm_parser->crypto_mode();
|
||||
key.content_iv = ecm_parser->content_iv(keyslot_id);
|
||||
if (keyslot_id == KeySlotId::kEvenKeySlot) {
|
||||
load_even = true;
|
||||
} else {
|
||||
load_odd = true;
|
||||
}
|
||||
if (key.content_iv.size() == 8) {
|
||||
key.content_iv.resize(16, 0);
|
||||
}
|
||||
}
|
||||
if (!ecm_parser->rotation_enabled() ||
|
||||
keyslot_id == KeySlotId::kOddKeySlot) {
|
||||
break;
|
||||
}
|
||||
keyslot_id = KeySlotId::kOddKeySlot;
|
||||
} while (true);
|
||||
if (load_even || load_odd) {
|
||||
CasStatus status = crypto_session_->LoadCasECMKeys(
|
||||
key_session_id_,
|
||||
(load_even ? &keys[KeySlotId::kEvenKeySlot] : nullptr),
|
||||
(load_odd ? &keys[KeySlotId::kOddKeySlot] : nullptr));
|
||||
if (status.status_code() != CasStatusCode::kNoError) {
|
||||
LOGE("WidevineCasSession::processEcm: error %d, msg %s",
|
||||
status.status_code(), status.error_string().c_str());
|
||||
return status;
|
||||
}
|
||||
|
||||
// Don't update on failure, to not to lose still working key_id.
|
||||
if (load_even) {
|
||||
keys_[KeySlotId::kEvenKeySlot] = keys[KeySlotId::kEvenKeySlot];
|
||||
}
|
||||
if (load_odd) {
|
||||
keys_[KeySlotId::kOddKeySlot] = keys[KeySlotId::kOddKeySlot];
|
||||
}
|
||||
}
|
||||
current_ecm_ = ecm;
|
||||
}
|
||||
return CasStatusCode::kNoError;
|
||||
}
|
||||
|
||||
std::unique_ptr<const EcmParser> WidevineCasSession::getEcmParser(
|
||||
const CasEcm& ecm) const {
|
||||
std::unique_ptr<const EcmParser> new_ecm_parser;
|
||||
if (!EcmParser::create(ecm, &new_ecm_parser)) {
|
||||
return std::unique_ptr<const EcmParser>();
|
||||
}
|
||||
return new_ecm_parser;
|
||||
}
|
||||
|
||||
const char* WidevineCasSession::securityLevel() {
|
||||
return crypto_session_->SecurityLevel();
|
||||
}
|
||||
|
||||
} // namespace wvcas
|
||||
Reference in New Issue
Block a user