Merge changes from topic "entitlement_license"

* changes:
  Fix some unit tests
  Add basic handling for entitlement keys in a license.
  Refactor key sessions to move them out of crypto session.
  Fix entitlement keys encryption and content key loading.
This commit is contained in:
Fred Gylys-Colwell
2018-01-30 20:17:32 +00:00
committed by Android (Google) Code Review
29 changed files with 1456 additions and 993 deletions

View File

@@ -382,11 +382,11 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
std::vector<video_widevine::SubLicense> embedded_key_data =
init_data.ExtractEmbeddedKeys();
init_data.ExtractSublicenseKeys();
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
CdmResponseType sts = crypto_session_->AddSubSession(
embedded_key_data[i].sub_session_key_id(),
init_data.ExtractGroupMasterKeyId());
embedded_key_data[i].group_id());
if (NO_ERROR != sts) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate sub session");
return sts;

View File

@@ -0,0 +1,174 @@
#include "content_key_session.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "log.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
// Generate Derived Keys for ContentKeySession
bool ContentKeySession::GenerateDerivedKeys(const std::string& message) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_GenerateDerivedKeys(
oec_session_id_,
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_generate_derived_keys_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
return false;
}
return true;
}
// Generate Derived Keys (from session key) for ContentKeySession
bool ContentKeySession::GenerateDerivedKeys(const std::string& message,
const std::string& session_key) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts;
M_TIME(
sts = OEMCrypto_DeriveKeysFromSessionKey(
oec_session_id_, reinterpret_cast<const uint8_t*>(session_key.data()),
session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_derive_keys_from_session_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
return false;
}
return true;
}
// Load Keys for ContentKeySession
OEMCryptoResult ContentKeySession::LoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
const std::string& srm_requirement) {
return LoadKeys(message, signature, mac_key_iv, mac_key, keys,
provider_session_token, cipher_mode, srm_requirement,
OEMCrypto_ContentLicense);
}
// Select Key for ContentKeySession
OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) {
// Crypto session lock already locked.
if (!cached_key_id_.empty() && cached_key_id_ == key_id) {
// Already using the desired key.
return OEMCrypto_SUCCESS;
}
cached_key_id_ = key_id;
const uint8_t* key_id_string =
reinterpret_cast<const uint8_t*>(cached_key_id_.data());
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_SelectKey(
oec_session_id_, key_id_string, cached_key_id_.size(),
static_cast<OEMCryptoCipherMode>(cipher_mode)),
metrics_, oemcrypto_select_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
cached_key_id_.clear();
}
return sts;
}
// Decrypt for ContentKeySession
OEMCryptoResult ContentKeySession::Decrypt(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc& buffer_descriptor,
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DecryptCENC(
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
metrics_, oemcrypto_decrypt_cenc_, sts,
metrics::Pow2Bucket(params.encrypt_length));
return sts;
}
OEMCryptoResult ContentKeySession::LoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
const std::string& srm_requirement, OEMCrypto_LicenseType license_type) {
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* enc_mac_key = NULL;
const uint8_t* enc_mac_key_iv = NULL;
if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) {
enc_mac_key = msg + GetOffset(message, mac_key);
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
} else {
LOGV("ContentKeySession::LoadKeys: enc_mac_key not set");
}
std::vector<OEMCrypto_KeyObject> load_keys(keys.size());
for (size_t i = 0; i < keys.size(); ++i) {
const CryptoKey* ki = &keys[i];
OEMCrypto_KeyObject* ko = &load_keys[i];
ko->key_id = msg + GetOffset(message, ki->key_id());
ko->key_id_length = ki->key_id().length();
ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv());
ko->key_data = msg + GetOffset(message, ki->key_data());
ko->key_data_length = ki->key_data().length();
if (ki->HasKeyControl()) {
ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv());
ko->key_control = msg + GetOffset(message, ki->key_control());
} else {
LOGE("For key %d: XXX key has no control block. size=%d", i,
ki->key_control().size());
ko->key_control_iv = NULL;
ko->key_control = NULL;
}
// TODO(jfore): Is returning the cipher needed. If not drop this.
*cipher_mode = ki->cipher_mode();
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
pst =
const_cast<uint8_t*>(msg) + GetOffset(message, provider_session_token);
}
uint8_t* srm_req = NULL;
if (!srm_requirement.empty()) {
srm_req = const_cast<uint8_t*>(msg) + GetOffset(message, srm_requirement);
}
LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts;
M_TIME(
sts = OEMCrypto_LoadKeys(
oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
enc_mac_key_iv, enc_mac_key, keys.size(), &load_keys[0], pst,
provider_session_token.length(), srm_req, license_type),
metrics_, oemcrypto_load_keys_, sts);
return sts;
}
} // namespace wvcdm

View File

@@ -11,7 +11,9 @@
#include <iostream>
#include <memory>
#include "content_key_session.h"
#include "crypto_key.h"
#include "entitlement_key_session.h"
#include "log.h"
#include "openssl/asn1.h"
#include "openssl/sha.h"
@@ -19,6 +21,7 @@
#include "properties.h"
#include "pst_report.h"
#include "string_conversions.h"
#include "sublicense_key_session.h"
#include "usage_table_header.h"
#include "wv_cdm_constants.h"
@@ -34,6 +37,46 @@ std::string EncodeUint32(unsigned int u) {
return s;
}
const uint32_t kRsaSignatureLength = 256;
const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB
const size_t kEstimatedInitialUsageTableHeader = 40;
const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13;
// Constants and utility objects relating to OEM Certificates
const int kExtensionOidSize = 64;
const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
// Helpers for working with BoringSSL
template <typename T, void (*func)(T*)>
class boringssl_ptr {
public:
explicit boringssl_ptr(T* p = NULL) : ptr_(p) {}
~boringssl_ptr() {
if (ptr_) func(ptr_);
}
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
T* get() const { return ptr_; }
private:
T* ptr_;
CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr);
};
void DeleteX509Stack(STACK_OF(X509)* stack) {
sk_X509_pop_free(stack, X509_free);
}
}
namespace wvcdm {
Lock CryptoSession::crypto_lock_;
bool CryptoSession::initialized_ = false;
int CryptoSession::session_count_ = 0;
uint64_t CryptoSession::request_id_index_ = 0;
UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL;
UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL;
size_t GetOffset(std::string message, std::string field) {
size_t pos = message.find(field);
if (pos == std::string::npos) {
@@ -77,607 +120,12 @@ void GenerateEncryptContext(const std::string& input_context,
deriv_context->append(EncodeUint32(kEncryptionKeySizeBits));
}
OEMCryptoCipherMode ToOEMCryptoCipherMode(wvcdm::CdmCipherMode cipher_mode) {
return (cipher_mode == wvcdm::kCipherModeCtr) ? OEMCrypto_CipherMode_CTR
: OEMCrypto_CipherMode_CBC;
OEMCrypto_LicenseType OEMCryptoLicenseType(CdmLicenseKeyType cdm_license_type) {
return cdm_license_type == kLicenseKeyTypeContent
? OEMCrypto_ContentLicense
: OEMCrypto_EntitlementLicense;
}
const uint32_t kRsaSignatureLength = 256;
const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB
const size_t kEstimatedInitialUsageTableHeader = 40;
const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13;
// Constants and utility objects relating to OEM Certificates
const int kExtensionOidSize = 64;
const char* const kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
// Helpers for working with BoringSSL
template <typename T, void (*func)(T*)>
class boringssl_ptr {
public:
explicit boringssl_ptr(T* p = NULL) : ptr_(p) {}
~boringssl_ptr() {
if (ptr_) func(ptr_);
}
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
T* get() const { return ptr_; }
private:
T* ptr_;
CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr);
};
void DeleteX509Stack(STACK_OF(X509)* stack) {
sk_X509_pop_free(stack, X509_free);
}
}
namespace wvcdm {
Lock CryptoSession::crypto_lock_;
bool CryptoSession::initialized_ = false;
int CryptoSession::session_count_ = 0;
uint64_t CryptoSession::request_id_index_ = 0;
UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL;
UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL;
// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own
// module or refactored out.
class DefaultKeySession : public KeySession {
public:
DefaultKeySession(CryptoSessionId oec_session_id,
metrics::CryptoMetrics* metrics)
: KeySession(metrics), oec_session_id_(oec_session_id) {}
virtual ~DefaultKeySession() {}
KeySessionType Type() { return kDefault; }
// Generate Derived Keys for DefaultKeySession
bool GenerateDerivedKeys(const std::string& message) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_GenerateDerivedKeys(
oec_session_id_,
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_generate_derived_keys_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
return false;
}
return true;
}
// Generate Derived Keys (from session key) for DefaultKeySession
bool GenerateDerivedKeys(const std::string& message,
const std::string& session_key) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DeriveKeysFromSessionKey(
oec_session_id_,
reinterpret_cast<const uint8_t*>(session_key.data()),
session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_derive_keys_from_session_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d",
sts);
return false;
}
return true;
}
// Load Keys for DefaultKeySession
OEMCryptoResult LoadKeys(const std::string& message,
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
CdmCipherMode* cipher_mode,
const std::string& srm_requirement) {
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* enc_mac_key = NULL;
const uint8_t* enc_mac_key_iv = NULL;
if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) {
enc_mac_key = msg + GetOffset(message, mac_key);
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
} else {
LOGV("DefaultKeySession::LoadKeys: enc_mac_key not set");
}
std::vector<OEMCrypto_KeyObject_V13> load_keys(keys.size());
for (size_t i = 0; i < keys.size(); ++i) {
const CryptoKey* ki = &keys[i];
OEMCrypto_KeyObject_V13* ko = &load_keys[i];
ko->key_id = msg + GetOffset(message, ki->key_id());
ko->key_id_length = ki->key_id().length();
ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv());
ko->key_data = msg + GetOffset(message, ki->key_data());
ko->key_data_length = ki->key_data().length();
if (ki->HasKeyControl()) {
ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv());
ko->key_control = msg + GetOffset(message, ki->key_control());
} else {
LOGE("For key %d: XXX key has no control block. size=%d", i,
ki->key_control().size());
ko->key_control_iv = NULL;
ko->key_control = NULL;
}
*cipher_mode = ki->cipher_mode();
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
pst = const_cast<uint8_t*>(msg) +
GetOffset(message, provider_session_token);
}
uint8_t* srm_req = NULL;
if (!srm_requirement.empty()) {
srm_req = const_cast<uint8_t*>(msg) + GetOffset(message, srm_requirement);
}
LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_LoadKeys_Back_Compat(
oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), enc_mac_key_iv, enc_mac_key, keys.size(),
&load_keys[0], pst, provider_session_token.length(), srm_req,
OEMCrypto_ContentLicense),
metrics_, oemcrypto_load_keys_, sts);
return sts;
}
// Select Key for DefaultKeySession
OEMCryptoResult SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) {
// Crypto session lock already locked.
if (!cached_key_id_.empty() && cached_key_id_ == key_id) {
// Already using the desired key.
return OEMCrypto_SUCCESS;
}
cached_key_id_ = key_id;
const uint8_t* key_id_string =
reinterpret_cast<const uint8_t*>(cached_key_id_.data());
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_SelectKey(oec_session_id_, key_id_string,
cached_key_id_.size(),
ToOEMCryptoCipherMode(cipher_mode)),
metrics_, oemcrypto_select_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
cached_key_id_.clear();
}
return sts;
}
// Decrypt for DefaultKeySession
OEMCryptoResult Decrypt(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc& buffer_descriptor,
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DecryptCENC(
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
metrics_, oemcrypto_decrypt_cenc_, sts,
metrics::Pow2Bucket(params.encrypt_length));
return sts;
}
private:
CryptoSessionId oec_session_id_;
KeyId cached_key_id_;
};
// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own
// module or refactored out.
class SubLicenseKeySession : public KeySession {
typedef enum {
kInitializing,
kInitialLicenseLoaded,
kInitialLicenseFailed,
} SubLicenseState;
public:
SubLicenseKeySession(SubLicenseSessionMap& sub_license_oec_sessions,
metrics::CryptoMetrics* metrics,
const std::string& wrapped_private_device_key,
SecurityLevel requested_security_level,
const std::string& group_master_key_id)
: KeySession(metrics),
state_(kInitializing),
wrapped_private_device_key_(wrapped_private_device_key),
sub_license_oec_sessions_(sub_license_oec_sessions),
requested_security_level_(requested_security_level),
group_master_key_id_(group_master_key_id) {}
virtual ~SubLicenseKeySession() {
for (SubLicenseSessionMap::iterator oec_session =
sub_license_oec_sessions_.begin();
oec_session != sub_license_oec_sessions_.end(); oec_session++) {
metrics_->oemcrypto_close_session_.Increment(
OEMCrypto_CloseSession(oec_session->second));
}
sub_license_oec_sessions_.clear();
}
KeySessionType Type() { return kSubLicense; }
// This version of GenerateDerivedKeys is for devices using keyboxes. It is
// not supported using sub licenses.
bool GenerateDerivedKeys(const std::string&) { return false; }
// GenerateDerivedKeys is called for each open oemcrypto session and is only
// called once.
bool GenerateDerivedKeys(const std::string& message,
const std::string& session_key) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin();
it != sub_license_oec_sessions_.end(); it++) {
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second);
OEMCryptoResult sts;
M_TIME(
sts = OEMCrypto_DeriveKeysFromSessionKey(
it->second, reinterpret_cast<const uint8_t*>(session_key.data()),
session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_derive_keys_from_session_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d",
sts);
return false;
}
}
return true;
}
// Load the keys in |keys|. The initial keys are saved for key rotation.
OEMCryptoResult LoadKeys(const std::string& message,
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
CdmCipherMode* cipher_mode,
const std::string& srm_requirement) {
if (state_ == kInitializing) {
state_ = kInitialLicenseLoaded;
keys_ = keys;
OEMCryptoResult sts =
DoLoadKeys(message, signature, mac_key_iv, mac_key, keys,
provider_session_token, cipher_mode, srm_requirement);
if (OEMCrypto_SUCCESS != sts) {
state_ = kInitialLicenseFailed;
}
return sts;
}
return DoSubLicenseLoadKeys(message, signature, mac_key_iv, mac_key,
keys[0], provider_session_token, cipher_mode,
srm_requirement);
}
// Each oemcrypto session contains a single key. Find the right sub session
// and save it's id as the selected oemcrypto session.
OEMCryptoResult SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) {
for (size_t i = 0; i < keys_.size(); ++i) {
if (keys_[i].key_id() == key_id) {
cached_sub_session_key_id_ = keys_[i].sub_session_key_id();
if (keys_[i].cipher_mode() != cipher_mode) {
SubLicenseSessionMap::iterator it =
sub_license_oec_sessions_.find(cached_sub_session_key_id_);
if (it == sub_license_oec_sessions_.end()) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
OEMCryptoResult status = OEMCrypto_SUCCESS;
M_TIME(status = OEMCrypto_SelectKey(
it->second,
reinterpret_cast<const uint8_t*>(keys_[i].key_id().data()),
keys_[i].key_id().size(),
static_cast<OEMCryptoCipherMode>(cipher_mode)),
metrics_, oemcrypto_select_key_, status);
if (OEMCrypto_SUCCESS != status) {
return status;
}
keys_[i].set_cipher_mode(cipher_mode);
}
}
}
return OEMCrypto_SUCCESS;
}
// Decrypt performs the decryption using the selected oemcrypto session.
// TODO(jfore): Support DecryptInChunks.
OEMCryptoResult Decrypt(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc& buffer_descriptor,
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
SubLicenseSessionMap::iterator it =
sub_license_oec_sessions_.find(cached_sub_session_key_id_);
if (it == sub_license_oec_sessions_.end()) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DecryptCENC(
it->second, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
metrics_, oemcrypto_decrypt_cenc_, sts,
metrics::Pow2Bucket(params.encrypt_length));
return sts;
}
private:
// Destroy each open oemcrypto session and relace them with new ones.
OEMCryptoResult ResetCryptoSessions() {
for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin();
it != sub_license_oec_sessions_.end(); it++) {
OEMCryptoResult sts = OEMCrypto_CloseSession(it->second);
metrics_->oemcrypto_close_session_.Increment(sts);
if (OEMCrypto_SUCCESS != sts) {
return sts;
}
sts = OEMCrypto_OpenSession(&it->second, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
return sts;
}
M_TIME(sts = OEMCrypto_LoadDeviceRSAKey(
it->second,
reinterpret_cast<const uint8_t*>(
wrapped_private_device_key_.data()),
wrapped_private_device_key_.size()),
metrics_, oemcrypto_load_device_rsa_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
return sts;
}
}
return OEMCrypto_SUCCESS;
}
// DoLoadKeys loads a single key into each oemcrypto session.
OEMCryptoResult DoLoadKeys(const std::string& message,
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
CdmCipherMode* cipher_mode,
const std::string& srm_requirement) {
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* enc_mac_key = NULL;
const uint8_t* enc_mac_key_iv = NULL;
if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) {
enc_mac_key = msg + GetOffset(message, mac_key);
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
} else {
LOGV("CryptoSession::LoadKeys: enc_mac_key not set");
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
pst = const_cast<uint8_t*>(msg) +
GetOffset(message, provider_session_token);
}
uint8_t* srm_req = NULL;
if (!srm_requirement.empty()) {
srm_req = const_cast<uint8_t*>(msg) + GetOffset(message, srm_requirement);
}
for (size_t i = 0; i < keys.size(); i++) {
OEMCrypto_KeyObject_V13 key_object;
const CryptoKey& key_data = keys[i];
key_object.key_id = msg + GetOffset(message, key_data.key_id());
key_object.key_id_length = key_data.key_id().length();
key_object.key_data_iv = msg + GetOffset(message, key_data.key_data_iv());
key_object.key_data = msg + GetOffset(message, key_data.key_data());
key_object.key_data_length = key_data.key_data().length();
if (key_data.HasKeyControl()) {
key_object.key_control_iv =
msg + GetOffset(message, key_data.key_control_iv());
key_object.key_control =
msg + GetOffset(message, key_data.key_control());
} else {
LOGE("For key %s: XXX key has no control block. size=%d",
key_data.key_id().c_str(), key_data.key_control().size());
key_object.key_control_iv = NULL;
key_object.key_control = NULL;
}
*cipher_mode = key_data.cipher_mode();
SubLicenseSessionMap::iterator oec_session_id =
sub_license_oec_sessions_.find(key_data.sub_session_key_id());
if (oec_session_id == sub_license_oec_sessions_.end()) {
LOGE("CryptoSession::LoadKeys: Unrecognized sub session %s",
key_data.sub_session_key_id().c_str());
return OEMCrypto_ERROR_INVALID_SESSION;
}
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_LoadKeys_Back_Compat(
oec_session_id->second, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object,
pst, provider_session_token.length(), srm_req,
OEMCrypto_ContentLicense),
metrics_, oemcrypto_load_keys_, sts);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
M_TIME(sts = OEMCrypto_SelectKey(
oec_session_id->second,
reinterpret_cast<const uint8_t*>(key_data.key_id().data()),
key_data.key_id().size(),
static_cast<OEMCryptoCipherMode>(key_data.cipher_mode())),
metrics_, oemcrypto_select_key_, sts);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
}
keys_ = keys;
return OEMCrypto_SUCCESS;
}
// DoLoadKeys loads a single key into each oemcrypto session.
OEMCryptoResult DoSubLicenseLoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const CryptoKey& key, const std::string& provider_session_token,
CdmCipherMode*, const std::string& srm_requirement) {
SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.end();
size_t key_index = 0;
for (; key_index < keys_.size(); key_index++) {
if (keys_[key_index].track_label() == key.track_label()) {
it = sub_license_oec_sessions_.find(
keys_[key_index].sub_session_key_id());
CryptoKey tmp = key;
tmp.set_sub_session_key_id(keys_[key_index].sub_session_key_id());
tmp.set_sub_session_key(keys_[key_index].sub_session_key());
keys_[key_index] = tmp;
break;
}
}
if (it == sub_license_oec_sessions_.end()) {
return OEMCrypto_SUCCESS;
}
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second);
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(group_master_key_id_ + key.track_label(),
&mac_deriv_message);
GenerateEncryptContext(group_master_key_id_ + key.track_label(),
&enc_deriv_message);
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* enc_mac_key = NULL;
const uint8_t* enc_mac_key_iv = NULL;
if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) {
enc_mac_key = msg + GetOffset(message, mac_key);
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
} else {
LOGV("CryptoSession::LoadKeys: enc_mac_key not set");
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
pst = const_cast<uint8_t*>(msg) +
GetOffset(message, provider_session_token);
}
uint8_t* srm_req = NULL;
if (!srm_requirement.empty()) {
srm_req = const_cast<uint8_t*>(msg) + GetOffset(message, srm_requirement);
}
OEMCryptoResult sts;
const std::string& sub_session_key = keys_[key_index].sub_session_key();
LOGE("ssksize = %d", sub_session_key.size());
M_TIME(sts = OEMCrypto_DeriveKeysFromSessionKey(
it->second,
reinterpret_cast<const uint8_t*>(sub_session_key.data()),
sub_session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_derive_keys_from_session_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d",
sts);
return sts;
}
OEMCrypto_KeyObject_V13 key_object;
key_object.key_id = msg + GetOffset(message, keys_[key_index].key_id());
key_object.key_id_length = keys_[key_index].key_id().length();
key_object.key_data_iv =
msg + GetOffset(message, keys_[key_index].key_data_iv());
key_object.key_data = msg + GetOffset(message, keys_[key_index].key_data());
key_object.key_data_length = keys_[key_index].key_data().length();
if (key.HasKeyControl()) {
key_object.key_control_iv =
msg + GetOffset(message, keys_[key_index].key_control_iv());
key_object.key_control =
msg + GetOffset(message, keys_[key_index].key_control());
}
M_TIME(
sts = OEMCrypto_LoadKeys_Back_Compat(
it->second, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, pst,
provider_session_token.length(), srm_req, OEMCrypto_ContentLicense),
metrics_, oemcrypto_load_keys_, sts);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
M_TIME(
sts = OEMCrypto_SelectKey(
it->second,
reinterpret_cast<const uint8_t*>(keys_[key_index].key_id().data()),
keys_[key_index].key_id().size(),
static_cast<OEMCryptoCipherMode>(keys_[key_index].cipher_mode())),
metrics_, oemcrypto_select_key_, sts);
return sts;
}
SubLicenseState state_;
std::string cached_sub_session_key_id_;
std::string wrapped_private_device_key_;
std::string message_;
std::string session_key_;
std::vector<CryptoKey> keys_;
SubLicenseSessionMap& sub_license_oec_sessions_;
SecurityLevel requested_security_level_;
KeyId group_master_key_id_;
};
CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
: metrics_(metrics),
system_id_(-1),
@@ -1251,7 +699,7 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
}
// TODO(gmorgan, jfore): resolve handling of usage records in sublicenses
key_session_.reset(new DefaultKeySession(oec_session_id_, metrics_));
key_session_.reset(new ContentKeySession(oec_session_id_, metrics_));
return NO_ERROR;
}
@@ -1338,10 +786,15 @@ CdmResponseType CryptoSession::LoadKeys(
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
const std::string& srm_requirement) {
const std::string& srm_requirement, CdmLicenseKeyType key_type) {
LOGV("CryptoSession::LoadKeys: Lock");
AutoLock auto_lock(crypto_lock_);
if (key_type == kLicenseKeyTypeEntitlement &&
key_session_->Type() != KeySession::kEntitlement) {
key_session_.reset(new EntitlementKeySession(oec_session_id_, metrics_));
}
LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts = key_session_->LoadKeys(
message, signature, mac_key_iv, mac_key, keys, provider_session_token,
@@ -1374,6 +827,13 @@ CdmResponseType CryptoSession::LoadKeys(
return result;
}
CdmResponseType CryptoSession::LoadEntitledContentKeys(
const std::vector<CryptoKey>& key_array) {
// TODO(jfore): Handle and return errors.
/*OEMCryptoResult status =*/ key_session_->LoadEntitledContentKeys(key_array);
return KEY_ADDED;
}
bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
LOGV("CryptoSession::LoadCertificatePrivateKey: Lock");
AutoLock auto_lock(crypto_lock_);

View File

@@ -0,0 +1,54 @@
#include "entitlement_key_session.h"
#include "crypto_key.h"
namespace wvcdm {
EntitlementKeySession::EntitlementKeySession(CryptoSessionId oec_session_id,
metrics::CryptoMetrics* metrics)
: ContentKeySession(oec_session_id, metrics) {}
OEMCryptoResult EntitlementKeySession::LoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
const std::string& srm_requirement) {
keys_.resize(keys.size());
return ContentKeySession::LoadKeys(
message, signature, mac_key_iv, mac_key, keys, provider_session_token,
cipher_mode, srm_requirement, OEMCrypto_EntitlementLicense);
}
OEMCryptoResult EntitlementKeySession::LoadEntitledContentKeys(
const std::vector<CryptoKey>& keys) {
// The array |keys| contains new content keys, plus entitlement key ids for
// those content keys.
std::vector<OEMCrypto_EntitledContentKeyObject> entitlements;
entitlements.resize(keys.size());
for (size_t i = 0; i < keys.size(); ++i) {
entitlements[i].entitlement_key_id =
reinterpret_cast<const uint8_t*>(keys[i].entitlement_key_id().data());
entitlements[i].entitlement_key_id_length =
keys[i].entitlement_key_id().size();
entitlements[i].content_key_id =
reinterpret_cast<const uint8_t*>(keys[i].key_id().data());
entitlements[i].content_key_id_length = keys[i].key_id().size();
entitlements[i].content_key_data_iv =
reinterpret_cast<const uint8_t*>(keys[i].key_data_iv().data());
entitlements[i].content_key_data =
reinterpret_cast<const uint8_t*>(keys[i].key_data().data());
entitlements[i].content_key_data_length = keys[i].key_data().size();
}
OEMCryptoResult result = OEMCrypto_SUCCESS;
M_TIME(result = OEMCrypto_LoadEntitledContentKeys(
oec_session_id_, entitlements.size(), &entitlements[0]),
metrics_, oemcrypto_load_entitled_keys_, result);
return result;
}
} // namespace wvcdm

View File

@@ -67,7 +67,7 @@ InitializationData::InitializationData(const std::string& type,
// Parse the pssh data and return the embedded key data if it exists.
std::vector<video_widevine::SubLicense>
InitializationData::ExtractEmbeddedKeys() const {
InitializationData::ExtractSublicenseKeys() const {
std::vector<video_widevine::SubLicense> keys;
WidevinePsshData cenc_header;
if (!is_cenc_ || !cenc_header.ParseFromString(data_) ||
@@ -81,6 +81,20 @@ InitializationData::ExtractEmbeddedKeys() const {
return keys;
}
std::vector<video_widevine::WrappedKey> InitializationData::ExtractWrappedKeys()
const {
std::vector<video_widevine::WrappedKey> keys;
WidevinePsshData cenc_header;
if (!is_cenc_ || !cenc_header.ParseFromString(data_) ||
cenc_header.entitled_keys().size() == 0)
return keys;
keys.reserve(cenc_header.entitled_keys().size());
for (int i = 0; i < cenc_header.entitled_keys().size(); ++i) {
keys.push_back(cenc_header.entitled_keys(i));
}
return keys;
}
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH.
bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
@@ -483,7 +497,7 @@ bool InitializationData::ConstructWidevineInitData(
// have not yet been pushed to production. Set until then.
cenc_header.set_algorithm(WidevinePsshData_Algorithm_AESCTR);
for (size_t i = 0; i < key_ids.size(); ++i) {
cenc_header.add_key_id(key_ids[i]);
cenc_header.add_key_ids(key_ids[i]);
}
cenc_header.set_provider(provider);
cenc_header.set_content_id(content_id);
@@ -589,19 +603,4 @@ std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
return versions;
}
// Extract the key id of the group master key used to generate sublicense data.
// Returns an empty string if not defined.
const std::string InitializationData::ExtractGroupMasterKeyId() const {
if (!is_cenc_) {
return "";
}
WidevinePsshData cenc_header;
if (!cenc_header.ParseFromString(data_)) {
return "";
}
return cenc_header.group_master_key_id();
}
} // namespace wvcdm

View File

@@ -72,6 +72,54 @@ static std::vector<CryptoKey> ExtractSubSessionKeys(const License& license) {
return key_array;
}
static std::vector<CryptoKey> ExtractEntitlementKeys(const License& license) {
std::vector<CryptoKey> key_array;
// Extract sub session key(s)
for (int i = 0; i < license.key_size(); ++i) {
CryptoKey key;
switch (license.key(i).type()) {
case License_KeyContainer::ENTITLEMENT: {
key.set_key_data(license.key(i).key());
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;
}
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> key_array;
@@ -239,6 +287,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return PrepareKeyRequest(restored_init_data, license_type, app_parameters,
signed_request, server_url);
}
wrapped_keys_ = init_data.ExtractWrappedKeys();
if (!init_data.is_supported()) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
@@ -306,7 +355,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// Prepare the request for any embedded keys that may exist in the
// initialization data.
std::vector<video_widevine::SubLicense> embedded_key_data =
init_data.ExtractEmbeddedKeys();
init_data.ExtractSublicenseKeys();
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
bool exists = false;
if (!crypto_session_->GenerateSubSessionNonce(
@@ -575,8 +624,15 @@ CdmResponseType CdmLicense::HandleKeyResponse(
}
}
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
if (!key_array.size()) {
CdmLicenseKeyType key_type = kLicenseKeyTypeEntitlement;
std::vector<CryptoKey> key_array = ExtractEntitlementKeys(license);
if (key_array.empty()) {
key_array = ExtractContentKeys(license);
key_type = kLicenseKeyTypeContent;
} else if (wrapped_keys_.empty()) {
key_array.clear();
}
if (key_array.empty()) {
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
return NO_CONTENT_KEY;
}
@@ -601,17 +657,15 @@ CdmResponseType CdmLicense::HandleKeyResponse(
renew_with_client_id_ = license.policy().always_include_client_id();
}
CdmResponseType resp = crypto_session_->LoadKeys(
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
key_array, provider_session_token_, license.srm_requirement());
if (KEY_ADDED == resp) {
loaded_keys_.clear();
for (std::vector<CryptoKey>::iterator it = key_array.begin();
it != key_array.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
policy_engine_->SetLicense(license);
CdmResponseType resp = NO_CONTENT_KEY;
if (kLicenseKeyTypeEntitlement == key_type) {
resp = HandleEntitlementKeyResponse(signed_response.msg(),
signed_response.signature(), mac_key_iv,
mac_key, key_array, license);
} else if (kLicenseKeyTypeContent == key_type) {
resp = HandleContentKeyResponse(signed_response.msg(),
signed_response.signature(), mac_key_iv,
mac_key, key_array, license);
}
return resp;
}
@@ -699,7 +753,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
CdmResponseType CdmLicense::HandleSubLicense(
const InitializationData& init_data) {
std::vector<video_widevine::SubLicense> subkeys =
init_data.ExtractEmbeddedKeys();
init_data.ExtractSublicenseKeys();
std::set<KeyId> loaded_keys;
// Build a license with the rotated keys.
License license;
@@ -732,7 +786,7 @@ CdmResponseType CdmLicense::HandleSubLicense(
//TODO: passing empty cipher_mode and srm_req params - OK?
CdmResponseType result = crypto_session_->LoadKeys(
sm.msg(), sm.signature(), std::string(), std::string(), keys,
std::string(), std::string());
std::string(), std::string(), kLicenseKeyTypeContent);
if (result != KEY_ADDED) {
LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d",
result);
@@ -1049,6 +1103,101 @@ CdmResponseType CdmLicense::PrepareContentId(
return NO_ERROR;
}
CdmResponseType CdmLicense::HandleContentKeyResponse(
const std::string& msg, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty()) {
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
return NO_CONTENT_KEY;
}
CdmResponseType 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<CryptoKey>::const_iterator it = key_array.begin();
it != key_array.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
policy_engine_->SetLicense(license);
}
return resp;
}
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
const std::string& msg, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty() || wrapped_keys_.empty()) {
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
return NO_CONTENT_KEY;
}
CdmResponseType 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;
}
std::vector<CryptoKey> entitled_key_array;
entitled_key_array.reserve(key_array.size());
for (std::vector<video_widevine::WrappedKey>::iterator wk =
wrapped_keys_.begin();
wk != wrapped_keys_.end(); wk++) {
for (std::vector<CryptoKey>::const_iterator key = key_array.begin();
key != key_array.end(); key++) {
if (wk->wrapping_key_id() == key->key_id()) {
entitled_key_array.resize(entitled_key_array.size() + 1);
CryptoKey& this_entry = entitled_key_array.back();
this_entry.set_key_id(wk->key_id());
this_entry.set_key_data(wk->wrapped_key());
this_entry.set_key_data_iv(wk->wrapping_iv());
this_entry.set_entitlement_key_id(wk->wrapping_key_id());
}
}
}
resp = crypto_session_->LoadEntitledContentKeys(entitled_key_array);
if (KEY_ADDED == resp) {
loaded_keys_.clear();
for (std::vector<video_widevine::WrappedKey>::const_iterator it =
wrapped_keys_.begin();
it != wrapped_keys_.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
// TODO(jfore): Move the information to build this "license" to the
// entitlement key session. It is used to update the policy engine and
// key status when using entitlement licenses. It may become unnecessary
// if policy manager ius changed to allow setting keys from the wrapped
// keys from init_data.
video_widevine::License entitled_license;
entitled_license.mutable_policy()->CopyFrom(license.policy());
entitled_license.mutable_id()->CopyFrom(license.id());
entitled_license.mutable_key()->CopyFrom(license.key());
entitled_license.set_license_start_time(license.license_start_time());
for (size_t i = 0; i < wrapped_keys_.size(); ++i) {
for (int x = 0; x < entitled_license.key().size(); ++x) {
if (entitled_license.key(x).id() ==
wrapped_keys_[i].wrapping_key_id()) {
video_widevine::License::KeyContainer* kc =
entitled_license.mutable_key(x);
kc->set_type(video_widevine::License::KeyContainer::CONTENT);
kc->set_key(wrapped_keys_[i].wrapped_key());
kc->set_id(wrapped_keys_[i].key_id());
}
}
}
policy_engine_->SetLicense(entitled_license);
}
return resp;
}
template <typename T>
bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
const std::string& request_id, T* content_id) {

View File

@@ -102,6 +102,7 @@ message License {
KEY_CONTROL = 3;
OPERATOR_SESSION = 4;
SUB_SESSION = 5;
ENTITLEMENT = 6;
}
// The SecurityLevel enumeration allows the server to communicate the level
@@ -729,28 +730,54 @@ message SubLicense {
// proto. Internally the message field will contain a serialized KeyContainer
// holding a single content key.
optional bytes key_msg = 2;
// TODO(jfore): There is some uncertainty about including the current group in
// a license. This may change.
// Byte string that identifies the group to which this this content
// belongs.
optional bytes group_id = 13;
}
// Container for keys which are wrapped using an entitlement key from a master
// license.
message WrappedKey {
// ID of the wrapped key. Required.
optional bytes key_id = 1;
// ID of wrapping key. Required.
optional bytes wrapping_key_id = 2;
// IV used to wrap the key. Required.
optional bytes wrapping_iv = 3;
// Encrypted entitled key. Wrapped with the entitlement key and IV, using
// AES-256-CBC with PKCS#7 padding. Required.
optional bytes wrapped_key = 4;
}
message WidevinePsshData {
// Superceded by protection_scheme.
enum Algorithm {
UNENCRYPTED = 0;
AESCTR = 1;
};
// Replaced with protection_scheme.
optional Algorithm algorithm = 1;
repeated bytes key_id = 2;
optional Algorithm algorithm = 1 [deprecated = true];
// Key IDentifier(s). This field is mutually exclusive with content_id, below.
// Only One or the other, but at least one must be present.
repeated bytes key_ids = 2;
// Content provider name.
optional string provider = 3;
optional string provider = 3 [deprecated = true];
// A content identifier, specified by content provider.
// This field is mutually exclusive with key_ids, above. Only
// one or the other, but at least one must be present.
optional bytes content_id = 4;
// Track type. Acceptable values are SD, HD and AUDIO. Used to differentiate
// content keys used by an asset.
// No longer adding track_type to the PSSH since the Widevine license server
// will return keys for all allowed track types in a single license.
optional string track_type_deprecated = 5;
optional string track_type = 5 [deprecated = true];
// The name of a registered policy to be used for this asset.
optional string policy = 6 [deprecated=true];
@@ -760,16 +787,16 @@ message WidevinePsshData {
// Optional protected context for group content. The grouped_license is a
// serialized SignedMessage.
optional bytes grouped_license = 8;
optional bytes grouped_license = 8 [deprecated = true];
// Protection scheme identifying the encryption algorithm. The protection
// scheme is represented as a uint32 value. The uint32 contains 4 bytes each
// representing a single ascii character in one of the 4CC protection scheme
// values.
// values. To be soon deprecated in favor of signaling from content.
// 'cenc' (AES-CTR) protection_scheme = 0x63656E63,
// 'cbc1' (AES-CBC) protection_scheme = 0x63626331,
// 'cens' (AES-CTR subsample) protection_scheme = 0x63656E73,
// 'cbcs' (AES-CBC subsample) protection_scheme = 0x63626373.
// 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73,
// 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373.
optional uint32 protection_scheme = 9;
// Optional. For media using key rotation, this represents the duration
@@ -779,9 +806,15 @@ message WidevinePsshData {
// Required when using content keys that are embedded in content.
repeated SubLicense sub_licenses = 11;
// Key ID used to identify the group master key License Server is supposed
// to use to generate group license.
optional string group_master_key_id = 12;
// IDs of the groups to which the content belongs. A group is a set of
// content IDs. A particular piece of content may belong to multiple groups.
repeated bytes group_ids = 12;
// Copy/copies of the content key used to decrypt the media stream in which
// the PSSH box is embedded, each wrapped with a different entitlement key.
// May be repeated if using group entitlement keys. Optional, used for content
// key rotation.
repeated WrappedKey entitled_keys = 13;
}
// Signed device certificate definition.

View File

@@ -0,0 +1,351 @@
#include "sublicense_key_session.h"
#include "crypto_session.h"
#include "log.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
SubLicenseKeySession::SubLicenseKeySession(
SubLicenseSessionMap& sub_license_oec_sessions,
metrics::CryptoMetrics* metrics,
const std::string& wrapped_private_device_key,
SecurityLevel requested_security_level,
const std::string& group_id)
: KeySession(metrics),
state_(kInitializing),
wrapped_private_device_key_(wrapped_private_device_key),
sub_license_oec_sessions_(sub_license_oec_sessions),
requested_security_level_(requested_security_level),
group_id_(group_id) {}
SubLicenseKeySession::~SubLicenseKeySession() {
for (SubLicenseSessionMap::iterator oec_session =
sub_license_oec_sessions_.begin();
oec_session != sub_license_oec_sessions_.end(); oec_session++) {
metrics_->oemcrypto_close_session_.Increment(
OEMCrypto_CloseSession(oec_session->second));
}
sub_license_oec_sessions_.clear();
}
// GenerateDerivedKeys is called for each open oemcrypto session and is only
// called once.
bool SubLicenseKeySession::GenerateDerivedKeys(const std::string& message,
const std::string& session_key) {
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(message, &mac_deriv_message);
GenerateEncryptContext(message, &enc_deriv_message);
for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin();
it != sub_license_oec_sessions_.end(); it++) {
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second);
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DeriveKeysFromSessionKey(
it->second, reinterpret_cast<const uint8_t*>(session_key.data()),
session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_derive_keys_from_session_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d",
sts);
return false;
}
}
return true;
}
OEMCryptoResult SubLicenseKeySession::LoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
const std::string& srm_requirement) {
if (state_ == kInitializing) {
state_ = kInitialLicenseLoaded;
keys_ = keys;
OEMCryptoResult sts =
DoLoadKeys(message, signature, mac_key_iv, mac_key, keys,
provider_session_token, cipher_mode, srm_requirement);
if (OEMCrypto_SUCCESS != sts) {
state_ = kInitialLicenseFailed;
}
return sts;
}
return DoSubLicenseLoadKeys(message, signature, mac_key_iv, mac_key, keys[0],
provider_session_token, cipher_mode,
srm_requirement);
}
OEMCryptoResult SubLicenseKeySession::SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) {
for (size_t i = 0; i < keys_.size(); ++i) {
if (keys_[i].key_id() == key_id) {
cached_sub_session_key_id_ = keys_[i].sub_session_key_id();
if (keys_[i].cipher_mode() != cipher_mode) {
SubLicenseSessionMap::iterator it =
sub_license_oec_sessions_.find(cached_sub_session_key_id_);
if (it == sub_license_oec_sessions_.end()) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
OEMCryptoResult status = OEMCrypto_SUCCESS;
M_TIME(status = OEMCrypto_SelectKey(
it->second,
reinterpret_cast<const uint8_t*>(keys_[i].key_id().data()),
keys_[i].key_id().size(),
static_cast<OEMCryptoCipherMode>(cipher_mode)),
metrics_, oemcrypto_select_key_, status);
if (OEMCrypto_SUCCESS != status) {
return status;
}
keys_[i].set_cipher_mode(cipher_mode);
}
}
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult SubLicenseKeySession::Decrypt(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc& buffer_descriptor,
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) {
SubLicenseSessionMap::iterator it =
sub_license_oec_sessions_.find(cached_sub_session_key_id_);
if (it == sub_license_oec_sessions_.end()) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
OEMCryptoResult sts;
M_TIME(sts = OEMCrypto_DecryptCENC(
it->second, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, &pattern_descriptor, params.subsample_flags),
metrics_, oemcrypto_decrypt_cenc_, sts,
metrics::Pow2Bucket(params.encrypt_length));
return sts;
}
OEMCryptoResult SubLicenseKeySession::ResetCryptoSessions() {
for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin();
it != sub_license_oec_sessions_.end(); it++) {
OEMCryptoResult sts = OEMCrypto_CloseSession(it->second);
metrics_->oemcrypto_close_session_.Increment(sts);
if (OEMCrypto_SUCCESS != sts) {
return sts;
}
sts = OEMCrypto_OpenSession(&it->second, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
return sts;
}
M_TIME(sts = OEMCrypto_LoadDeviceRSAKey(
it->second,
reinterpret_cast<const uint8_t*>(
wrapped_private_device_key_.data()),
wrapped_private_device_key_.size()),
metrics_, oemcrypto_load_device_rsa_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
return sts;
}
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult SubLicenseKeySession::DoLoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
const std::string& srm_requirement) {
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* enc_mac_key = NULL;
const uint8_t* enc_mac_key_iv = NULL;
if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) {
enc_mac_key = msg + GetOffset(message, mac_key);
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
} else {
LOGV("CryptoSession::LoadKeys: enc_mac_key not set");
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
pst =
const_cast<uint8_t*>(msg) + GetOffset(message, provider_session_token);
}
uint8_t* srm_req = NULL;
if (!srm_requirement.empty()) {
srm_req = const_cast<uint8_t*>(msg) + GetOffset(message, srm_requirement);
}
for (size_t i = 0; i < keys.size(); i++) {
OEMCrypto_KeyObject key_object;
const CryptoKey& key_data = keys[i];
key_object.key_id = msg + GetOffset(message, key_data.key_id());
key_object.key_id_length = key_data.key_id().length();
key_object.key_data_iv = msg + GetOffset(message, key_data.key_data_iv());
key_object.key_data = msg + GetOffset(message, key_data.key_data());
key_object.key_data_length = key_data.key_data().length();
if (key_data.HasKeyControl()) {
key_object.key_control_iv =
msg + GetOffset(message, key_data.key_control_iv());
key_object.key_control = msg + GetOffset(message, key_data.key_control());
} else {
LOGE("For key %s: XXX key has no control block. size=%d",
key_data.key_id().c_str(), key_data.key_control().size());
key_object.key_control_iv = NULL;
key_object.key_control = NULL;
}
// TODO(jfore): Does returning the cipher mode serve any purpose?
// If not drop.
*cipher_mode = key_data.cipher_mode();
SubLicenseSessionMap::iterator oec_session_id =
sub_license_oec_sessions_.find(key_data.sub_session_key_id());
if (oec_session_id == sub_license_oec_sessions_.end()) {
LOGE("CryptoSession::LoadKeys: Unrecognized sub session %s",
key_data.sub_session_key_id().c_str());
return OEMCrypto_ERROR_INVALID_SESSION;
}
OEMCryptoResult sts;
M_TIME(
sts = OEMCrypto_LoadKeys(
oec_session_id->second, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()),
signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, pst,
provider_session_token.length(), srm_req, OEMCrypto_ContentLicense),
metrics_, oemcrypto_load_keys_, sts);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
M_TIME(sts = OEMCrypto_SelectKey(
oec_session_id->second,
reinterpret_cast<const uint8_t*>(key_data.key_id().data()),
key_data.key_id().size(),
static_cast<OEMCryptoCipherMode>(key_data.cipher_mode())),
metrics_, oemcrypto_select_key_, sts);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
}
keys_ = keys;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult SubLicenseKeySession::DoSubLicenseLoadKeys(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const CryptoKey& key, const std::string& provider_session_token,
CdmCipherMode*, const std::string& srm_requirement) {
SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.end();
size_t key_index = 0;
for (; key_index < keys_.size(); key_index++) {
if (keys_[key_index].track_label() == key.track_label()) {
it =
sub_license_oec_sessions_.find(keys_[key_index].sub_session_key_id());
CryptoKey tmp = key;
tmp.set_sub_session_key_id(keys_[key_index].sub_session_key_id());
tmp.set_sub_session_key(keys_[key_index].sub_session_key());
keys_[key_index] = tmp;
break;
}
}
if (it == sub_license_oec_sessions_.end()) {
return OEMCrypto_SUCCESS;
}
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second);
std::string mac_deriv_message;
std::string enc_deriv_message;
GenerateMacContext(group_id_ + message.c_str(),
&mac_deriv_message);
GenerateEncryptContext(group_id_ + message.c_str(),
&enc_deriv_message);
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* enc_mac_key = NULL;
const uint8_t* enc_mac_key_iv = NULL;
if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) {
enc_mac_key = msg + GetOffset(message, mac_key);
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
} else {
LOGV("CryptoSession::LoadKeys: enc_mac_key not set");
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
pst =
const_cast<uint8_t*>(msg) + GetOffset(message, provider_session_token);
}
uint8_t* srm_req = NULL;
if (!srm_requirement.empty()) {
srm_req = const_cast<uint8_t*>(msg) + GetOffset(message, srm_requirement);
}
OEMCryptoResult sts;
const std::string& sub_session_key = keys_[key_index].sub_session_key();
LOGV("ssksize = %d", sub_session_key.size());
M_TIME(
sts = OEMCrypto_DeriveKeysFromSessionKey(
it->second, reinterpret_cast<const uint8_t*>(sub_session_key.data()),
sub_session_key.size(),
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
mac_deriv_message.size(),
reinterpret_cast<const uint8_t*>(enc_deriv_message.data()),
enc_deriv_message.size()),
metrics_, oemcrypto_derive_keys_from_session_key_, sts);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
return sts;
}
OEMCrypto_KeyObject key_object;
key_object.key_id = msg + GetOffset(message, keys_[key_index].key_id());
key_object.key_id_length = keys_[key_index].key_id().length();
key_object.key_data_iv =
msg + GetOffset(message, keys_[key_index].key_data_iv());
key_object.key_data = msg + GetOffset(message, keys_[key_index].key_data());
key_object.key_data_length = keys_[key_index].key_data().length();
if (key.HasKeyControl()) {
key_object.key_control_iv =
msg + GetOffset(message, keys_[key_index].key_control_iv());
key_object.key_control =
msg + GetOffset(message, keys_[key_index].key_control());
}
M_TIME(
sts = OEMCrypto_LoadKeys(
it->second, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
enc_mac_key_iv, enc_mac_key, 1, &key_object, pst,
provider_session_token.length(), srm_req, OEMCrypto_ContentLicense),
metrics_, oemcrypto_load_keys_, sts);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
M_TIME(sts = OEMCrypto_SelectKey(
it->second,
reinterpret_cast<const uint8_t*>(keys_[key_index].key_id().data()),
keys_[key_index].key_id().size(),
static_cast<OEMCryptoCipherMode>(keys_[key_index].cipher_mode())),
metrics_, oemcrypto_select_key_, sts);
return sts;
}
} // namespace wvcdm