Files
media_cas_client/plugin/src/crypto_session.cpp
2021-03-05 16:09:59 -08:00

1066 lines
40 KiB
C++

// 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 "crypto_session.h"
#include <openssl/sha.h>
#include <cstring>
#include <sstream>
#include "cas_util.h"
#include "log.h"
static const uint32_t kExpectedOEMCryptoVersion = 16;
namespace wvcas {
namespace {
// Find the offset of the |field| within |message|. Notice that if |field| is
// empty, 0 will be returned as |pos|.
size_t GetOffset(const std::string& message, const std::string& field) {
size_t pos = message.find(field);
if (pos == std::string::npos) {
LOGE("GetOffset : Cannot find offset for %s", field.c_str());
pos = 0;
}
return pos;
}
OEMCryptoCipherMode CipherModeFromKeyData(CryptoMode key_cipher) {
switch (key_cipher) {
case CryptoMode::kAesCBC:
return OEMCrypto_CipherMode_CBC;
case CryptoMode::kAesCTR:
return OEMCrypto_CipherMode_CTR;
case CryptoMode::kDvbCsa2:
return OEMCrypto_CipherMode_CSA2;
case CryptoMode::kDvbCsa3:
return OEMCrypto_CipherMode_CSA3;
case CryptoMode::kAesOFB:
return OEMCrypto_CipherMode_OFB;
case CryptoMode::kAesSCTE:
return OEMCrypto_CipherMode_SCTE;
default:
return OEMCrypto_CipherMode_CTR;
};
}
// Fill an oemcrypto entitled key object from the provided key data.
void FillEntitledContentKeyObjectFromKeyData(
std::string& message, const KeySlot& src,
OEMCrypto_EntitledCasKeyObject* dest) {
if (nullptr == dest) {
return;
}
std::string entitlement_key_id(src.entitlement_key_id.begin(),
src.entitlement_key_id.end());
std::string content_key_id(src.key_id.begin(), src.key_id.end());
std::string content_key_data_iv(src.wrapped_key_iv.begin(),
src.wrapped_key_iv.end());
std::string content_key_data(src.wrapped_key.begin(), src.wrapped_key.end());
std::string content_iv(src.content_iv.begin(), src.content_iv.end());
dest->entitlement_key_id.offset = message.size();
message += entitlement_key_id;
dest->entitlement_key_id.length = entitlement_key_id.length();
dest->content_key_id.offset = message.size();
message += content_key_id;
dest->content_key_id.length = content_key_id.length();
dest->content_key_data_iv.offset = message.size();
message += content_key_data_iv;
dest->content_key_data_iv.length = content_key_data_iv.length();
dest->content_key_data.offset = message.size();
message += content_key_data;
dest->content_key_data.length = content_key_data.length();
dest->content_iv.offset = message.size();
message += content_iv;
dest->content_iv.length = content_iv.length();
dest->cipher_mode = CipherModeFromKeyData(src.cipher_mode);
}
} // namespace
shared_mutex CryptoLock::static_field_mutex_;
shared_mutex CryptoLock::oem_crypto_mutex_;
template <class Func>
auto CryptoLock::WithStaticFieldWriteLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("Static field write lock: %s", tag);
std::unique_lock<shared_mutex> auto_lock(static_field_mutex_);
return body();
}
template <class Func>
auto CryptoLock::WithStaticFieldReadLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("Static field read lock: %s", tag);
shared_lock<shared_mutex> auto_lock(static_field_mutex_);
return body();
}
template <class Func>
auto CryptoLock::WithOecWriteLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("OEMCrypto write lock: %s", tag);
std::unique_lock<shared_mutex> auto_lock(oem_crypto_mutex_);
return body();
}
template <class Func>
auto CryptoLock::WithOecReadLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("OEMCrypto read lock: %s", tag);
shared_lock<shared_mutex> auto_lock(oem_crypto_mutex_);
return body();
}
template <class Func>
auto CryptoLock::WithOecSessionLock(const char* tag, Func body)
-> decltype(body()) {
LOGV("OEMCrypto Session Lock - %s", tag);
shared_lock<shared_mutex> oec_auto_lock(oem_crypto_mutex_);
std::unique_lock<std::mutex> session_auto_lock(oem_crypto_session_mutex_);
return body();
}
bool CryptoInterface::initialized_ = false;
int CryptoInterface::session_count_ = 0;
std::unique_ptr<CryptoLock> CryptoInterface::lock_ = make_unique<CryptoLock>();
OEMCryptoResult CryptoInterface::OEMCrypto_OpenSession(
OEMCrypto_SESSION* session) {
return lock_->WithOecWriteLock("OpenSession", [&] {
return oemcrypto_interface_->OEMCrypto_OpenSession(session);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_CloseSession(
OEMCrypto_SESSION session) {
return lock_->WithOecWriteLock("CloseSession", [&] {
return oemcrypto_interface_->OEMCrypto_CloseSession(session);
});
}
OEMCrypto_ProvisioningMethod
CryptoInterface::OEMCrypto_GetProvisioningMethod() {
return lock_->WithOecReadLock("GetProvisioningMethod", [&] {
return oemcrypto_interface_->OEMCrypto_GetProvisioningMethod();
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_GetKeyData(uint8_t* keyData,
size_t* keyDataLength) {
return lock_->WithOecReadLock("GetKeyData", [&] {
return oemcrypto_interface_->OEMCrypto_GetKeyData(keyData, keyDataLength);
});
}
uint32_t CryptoInterface::OEMCrypto_SupportedCertificates() {
return lock_->WithOecReadLock("SupportedCertificates", [&] {
return oemcrypto_interface_->OEMCrypto_SupportedCertificates();
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_GenerateNonce(
OEMCrypto_SESSION session, uint32_t* nonce) {
return lock_->WithOecWriteLock("GenerateNonce", [&] {
return oemcrypto_interface_->OEMCrypto_GenerateNonce(session, nonce);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_GenerateDerivedKeys(
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
uint32_t enc_key_context_length) {
return lock_->WithOecSessionLock("GenerateDerivedKeys", [&] {
return oemcrypto_interface_->OEMCrypto_GenerateDerivedKeys(
session, mac_key_context, mac_key_context_length, enc_key_context,
enc_key_context_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_PrepAndSignLicenseRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_size, uint8_t* signature, size_t* signature_length) {
return lock_->WithOecSessionLock("PrepAndSignLicenseRequest", [&] {
return oemcrypto_interface_->OEMCrypto_PrepAndSignLicenseRequest(
session, message, message_length, core_message_size, signature,
signature_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_PrepAndSignRenewalRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_size, uint8_t* signature, size_t* signature_length) {
return lock_->WithOecSessionLock("PrepareAndSignRenewalRequest", [&] {
return oemcrypto_interface_->OEMCrypto_PrepAndSignRenewalRequest(
session, message, message_length, core_message_size, signature,
signature_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_PrepAndSignProvisioningRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_size, uint8_t* signature, size_t* signature_length) {
return lock_->WithOecSessionLock("PrepareAndSignProvisioningRequest", [&] {
return oemcrypto_interface_->OEMCrypto_PrepAndSignProvisioningRequest(
session, message, message_length, core_message_size, signature,
signature_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_LoadProvisioning(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t core_message_length, const uint8_t* signature,
size_t signature_length, uint8_t* wrapped_private_key,
size_t* wrapped_private_key_length) {
return lock_->WithOecSessionLock("LoadProvisioning", [&] {
return oemcrypto_interface_->OEMCrypto_LoadProvisioning(
session, message, message_length, core_message_length, signature,
signature_length, wrapped_private_key, wrapped_private_key_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_GetOEMPublicCertificate(
OEMCrypto_SESSION session, uint8_t* public_cert,
size_t* public_cert_length) {
return lock_->WithOecSessionLock("GetOEMPublicCertificate", [&] {
return oemcrypto_interface_->OEMCrypto_GetOEMPublicCertificate(
session, public_cert, public_cert_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_LoadDRMPrivateKey(
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length) {
return lock_->WithOecSessionLock("LoadDRMPrivateKey", [&] {
return oemcrypto_interface_->OEMCrypto_LoadDRMPrivateKey(
session, key_type, wrapped_rsa_key, wrapped_rsa_key_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_GenerateRSASignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length,
RSA_Padding_Scheme padding_scheme) {
return lock_->WithOecSessionLock("GenerateRsaSignature", [&] {
return oemcrypto_interface_->OEMCrypto_GenerateRSASignature(
session, message, message_length, signature, signature_length,
padding_scheme);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_DeriveKeysFromSessionKey(
OEMCrypto_SESSION session, const uint8_t* enc_session_key,
size_t enc_session_key_length, const uint8_t* mac_key_context,
size_t mac_key_context_length, const uint8_t* enc_key_context,
size_t enc_key_context_length) {
return lock_->WithOecSessionLock("DeriveKeysFromSessionKey", [&] {
return oemcrypto_interface_->OEMCrypto_DeriveKeysFromSessionKey(
session, enc_session_key, enc_session_key_length, mac_key_context,
mac_key_context_length, enc_key_context, enc_key_context_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_LoadLicense(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t core_message_length, const uint8_t* signature,
size_t signature_length) {
return lock_->WithOecSessionLock("LoadLicense", [&] {
return oemcrypto_interface_->OEMCrypto_LoadLicense(
session, message, message_length, core_message_length, signature,
signature_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_LoadRenewal(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t core_message_length, const uint8_t* signature,
size_t signature_length) {
return lock_->WithOecSessionLock("LoadRenewal", [&] {
return oemcrypto_interface_->OEMCrypto_LoadRenewal(
session, message, message_length, core_message_length, signature,
signature_length);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_LoadCasECMKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const OEMCrypto_EntitledCasKeyObject* even_key,
const OEMCrypto_EntitledCasKeyObject* odd_key) {
return lock_->WithOecSessionLock("LoadCasECMKeys", [&] {
return oemcrypto_interface_->OEMCrypto_LoadCasECMKeys(
session, message, message_length, even_key, odd_key);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_SelectKey(
OEMCrypto_SESSION session, const uint8_t* content_key_id,
size_t content_key_id_length, OEMCryptoCipherMode cipher_mode) {
return lock_->WithOecSessionLock("SelectKey", [&] {
return oemcrypto_interface_->OEMCrypto_SelectKey(
session, content_key_id, content_key_id_length, cipher_mode);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_GetHDCPCapability(
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* max) {
return lock_->WithOecReadLock("GetHdcpCapabilities", [&] {
return oemcrypto_interface_->OEMCrypto_GetHDCPCapability(current, max);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_GetDeviceID(uint8_t* deviceID,
size_t* idLength) {
return lock_->WithOecReadLock("GetDeviceID", [&] {
return oemcrypto_interface_->OEMCrypto_GetDeviceID(deviceID, idLength);
});
}
const char* CryptoInterface::OEMCrypto_SecurityLevel() {
return lock_->WithOecReadLock("SecurityLevel", [&] {
return oemcrypto_interface_->OEMCrypto_SecurityLevel();
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_CreateEntitledKeySession(
OEMCrypto_SESSION oec_session, OEMCrypto_SESSION* key_session) {
return lock_->WithOecSessionLock("CreateEntitledKeySession", [&] {
return oemcrypto_interface_->OEMCrypto_CreateEntitledKeySession(
oec_session, key_session);
});
}
OEMCryptoResult CryptoInterface::OEMCrypto_RemoveEntitledKeySession(
OEMCrypto_SESSION key_session) {
return lock_->WithOecSessionLock("RemoveEntitledKeySession", [&] {
return oemcrypto_interface_->OEMCrypto_RemoveEntitledKeySession(
key_session);
});
}
uint32_t CryptoInterface::OEMCrypto_APIVersion() {
return lock_->WithOecReadLock("APIVersion", [&] {
return oemcrypto_interface_->OEMCrypto_APIVersion();
});
}
OEMCryptoResult CryptoInterface::create_internal(
OEMCryptoInterface* oemcrypto_interface,
std::unique_ptr<CryptoInterface>* init) {
static OEMCryptoInterface default_oemcrypto_interface;
// The oemcrypto interface currently used. It may point to different
// oemcrypto_interface implementations in normal running v.s. test running.
static OEMCryptoInterface* oemcrypto_interface_in_use;
if (init == nullptr) {
return OEMCrypto_ERROR_INIT_FAILED;
}
std::unique_ptr<CryptoInterface> new_oemcrypto_interface;
OEMCryptoResult status = OEMCrypto_SUCCESS;
lock_->WithStaticFieldWriteLock("Initialize", [&] {
if (!oemcrypto_interface)
oemcrypto_interface = &default_oemcrypto_interface;
if (session_count_ == 0) {
lock_->WithOecWriteLock("Initialize", [&] {
status = oemcrypto_interface->OEMCrypto_Initialize();
});
if (status != OEMCrypto_SUCCESS) {
return;
}
oemcrypto_interface_in_use = oemcrypto_interface;
}
// Using 'new' to access a non-public constructor.
new_oemcrypto_interface =
std::unique_ptr<CryptoInterface>(new CryptoInterface());
new_oemcrypto_interface->oemcrypto_interface_ = oemcrypto_interface_in_use;
++session_count_;
});
if (status != OEMCrypto_SUCCESS) {
return status;
}
*init = std::move(new_oemcrypto_interface);
return OEMCrypto_SUCCESS;
}
CryptoInterface::CryptoInterface() : oemcrypto_interface_(nullptr) {}
CryptoInterface::~CryptoInterface() {
lock_->WithStaticFieldWriteLock("Terminate", [&] {
if (session_count_ > 0) {
if (--session_count_ == 0) {
lock_->WithOecWriteLock(
"Terminate", [&] { oemcrypto_interface_->OEMCrypto_Terminate(); });
}
}
});
}
/************************************************************************/
CryptoSession::CryptoSession() {}
CryptoSession::~CryptoSession() {}
CasStatus CryptoSession::initialize() {
OEMCryptoResult result;
result = getCryptoInterface(&crypto_interface_);
if (result != OEMCrypto_SUCCESS) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"unable to ceate interface");
}
// Open a crypto session here.
result = crypto_interface_->OEMCrypto_OpenSession(&session_);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_OpenSession returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::reset() {
CasStatus status = close();
if (status.status_code() != CasStatusCode::kNoError) {
return status;
}
return initialize();
}
CasStatus CryptoSession::close() {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
OEMCryptoResult result = crypto_interface_->OEMCrypto_CloseSession(session_);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_CloseSession returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasProvisioningMethod CryptoSession::provisioning_method() {
if (!crypto_interface_) {
return ProvisioningError;
}
switch (crypto_interface_->OEMCrypto_GetProvisioningMethod()) {
case OEMCrypto_Keybox:
return Keybox;
case OEMCrypto_OEMCertificate:
return OEMCertificate;
case OEMCrypto_DrmCertificate:
return DrmCertificate;
default:
return ProvisioningError;
}
}
CasStatus CryptoSession::GetKeyData(uint8_t* keyData, size_t* keyDataLength) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
OEMCryptoResult result =
crypto_interface_->OEMCrypto_GetKeyData(keyData, keyDataLength);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_GetKeyData returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
SupportedCertificates CryptoSession::supported_certificates() {
if (!crypto_interface_) {
return SupportedCertificates(0);
}
return SupportedCertificates(
crypto_interface_->OEMCrypto_SupportedCertificates());
}
CasStatus CryptoSession::GenerateNonce(uint32_t* nonce) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
OEMCryptoResult result =
crypto_interface_->OEMCrypto_GenerateNonce(session_, nonce);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_GenerateNonce returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::GenerateDerivedKeys(const uint8_t* mac_key_context,
uint32_t mac_key_context_length,
const uint8_t* enc_key_context,
uint32_t enc_key_context_length) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
OEMCryptoResult result = crypto_interface_->OEMCrypto_GenerateDerivedKeys(
session_, mac_key_context, mac_key_context_length, enc_key_context,
enc_key_context_length);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_GenerateDerivedKeys returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::PrepareAndSignLicenseRequest(
const std::string& message, std::string* core_message,
std::string* signature) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (core_message == nullptr || signature == nullptr) {
LOGE("Missing core_message or signature.");
return CasStatus(CasStatusCode::kInvalidParameter,
"Missing core_message or signature.");
}
OEMCryptoResult sts;
size_t signature_length = 0;
size_t core_message_length = 0;
*core_message = "";
std::string combined_message = *core_message + message;
// First call is intended to determine the required size of the
// output buffers.
sts = crypto_interface_->OEMCrypto_PrepAndSignLicenseRequest(
session_,
reinterpret_cast<uint8_t*>(const_cast<char*>(combined_message.data())),
combined_message.size(), &core_message_length, nullptr,
&signature_length);
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
std::ostringstream err_string;
err_string << "OEMCrypto_PrepareAndSignLicenseRequest returned " << sts;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
// Resize.
core_message->resize(core_message_length);
signature->resize(signature_length);
combined_message = *core_message + message;
sts = crypto_interface_->OEMCrypto_PrepAndSignLicenseRequest(
session_,
reinterpret_cast<uint8_t*>(const_cast<char*>(combined_message.data())),
combined_message.size(), &core_message_length,
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&signature_length);
if (sts != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_PrepareAndSignLicenseRequest returned " << sts;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
signature->resize(signature_length);
*core_message = std::move(combined_message);
// Truncate combined message to only contain the core message.
core_message->resize(core_message_length);
return CasStatus::OkStatus();
}
CasStatus CryptoSession::PrepareAndSignRenewalRequest(
const std::string& message, std::string* core_message,
std::string* signature) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (core_message == nullptr || signature == nullptr) {
LOGE("Missing core_message or signature.");
return CasStatus(CasStatusCode::kInvalidParameter,
"Missing core_message or signature.");
}
OEMCryptoResult sts;
size_t signature_length = 0;
size_t core_message_length = 0;
*core_message = "";
std::string combined_message = *core_message + message;
// First call is intended to determine the required size of the
// output buffers.
sts = crypto_interface_->OEMCrypto_PrepAndSignRenewalRequest(
session_,
reinterpret_cast<uint8_t*>(const_cast<char*>(combined_message.data())),
combined_message.size(), &core_message_length, nullptr,
&signature_length);
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
std::ostringstream err_string;
err_string << "OEMCrypto_PrepAndSignRenewalRequest returned " << sts;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
// Resize.
core_message->resize(core_message_length);
signature->resize(signature_length);
combined_message = *core_message + message;
sts = crypto_interface_->OEMCrypto_PrepAndSignRenewalRequest(
session_,
reinterpret_cast<uint8_t*>(const_cast<char*>(combined_message.data())),
combined_message.size(), &core_message_length,
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&signature_length);
if (sts != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_PrepAndSignRenewalRequest returned " << sts;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
signature->resize(signature_length);
*core_message = std::move(combined_message);
// Truncate combined message to only contain the core message.
core_message->resize(core_message_length);
return CasStatus::OkStatus();
}
CasStatus CryptoSession::PrepareAndSignProvisioningRequest(
const std::string& message, std::string* core_message,
std::string* signature) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (core_message == nullptr || signature == nullptr) {
LOGE("Missing core_message or signature.");
return CasStatus(CasStatusCode::kInvalidParameter,
"Missing core_message or signature.");
}
OEMCryptoResult sts;
size_t signature_length = 0;
size_t core_message_length = 0;
*core_message = "";
std::string combined_message = *core_message + message;
// First call is intended to determine the required size of the
// output buffers.
sts = crypto_interface_->OEMCrypto_PrepAndSignProvisioningRequest(
session_,
reinterpret_cast<uint8_t*>(const_cast<char*>(combined_message.data())),
combined_message.size(), &core_message_length, nullptr,
&signature_length);
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
std::ostringstream err_string;
err_string << "OEMCrypto_PrepAndSignProvisioningRequest returned " << sts;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
// Resize.
core_message->resize(core_message_length);
signature->resize(signature_length);
combined_message = *core_message + message;
sts = crypto_interface_->OEMCrypto_PrepAndSignProvisioningRequest(
session_,
reinterpret_cast<uint8_t*>(const_cast<char*>(combined_message.data())),
combined_message.size(), &core_message_length,
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&signature_length);
if (sts != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_PrepAndSignProvisioningRequest returned " << sts;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
signature->resize(signature_length);
*core_message = std::move(combined_message);
// Truncate combined message to only contain the core message.
core_message->resize(core_message_length);
return CasStatus::OkStatus();
}
CasStatus CryptoSession::LoadProvisioning(const std::string& signed_message,
const std::string& core_message,
const std::string& signature,
std::string* wrapped_private_key) {
LOGV("Loading provisioning certificate: id = %u", session_);
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (wrapped_private_key == nullptr) {
LOGE("Missing wrapped |wrapped_private_key|.");
return CasStatus(CasStatusCode::kInvalidParameter,
"Missing wrapped |wrapped_private_key|.");
}
const std::string combined_message = core_message + signed_message;
// Round 1, get the size of the wrapped private key buffer.
size_t wrapped_private_key_length = 0;
OEMCryptoResult result = crypto_interface_->OEMCrypto_LoadProvisioning(
session_, reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
nullptr, &wrapped_private_key_length);
if (result != OEMCrypto_ERROR_SHORT_BUFFER) {
std::ostringstream err_string;
err_string << "OEMCrypto_LoadProvisioning returned " << result;
return CasStatus(CasStatusCode::kIndividualizationError, err_string.str());
}
wrapped_private_key->resize(wrapped_private_key_length);
result = crypto_interface_->OEMCrypto_LoadProvisioning(
session_, reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
reinterpret_cast<uint8_t*>(&wrapped_private_key->front()),
&wrapped_private_key_length);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_LoadProvisioning returned " << result;
return CasStatus(CasStatusCode::kIndividualizationError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::GetOEMPublicCertificate(uint8_t* public_cert,
size_t* public_cert_length) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (!public_cert || !public_cert_length) {
return CasStatus(CasStatusCode::kCryptoSessionError, "invalid argument");
}
OEMCryptoResult result = crypto_interface_->OEMCrypto_GetOEMPublicCertificate(
session_, public_cert, public_cert_length);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_GetOEMPublicCertificate returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::LoadDeviceRSAKey(const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (!wrapped_rsa_key) {
return CasStatus(CasStatusCode::kCryptoSessionError, "invalid argument");
}
OEMCryptoResult result = crypto_interface_->OEMCrypto_LoadDRMPrivateKey(
session_, OEMCrypto_RSA_Private_Key, wrapped_rsa_key,
wrapped_rsa_key_length);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_LoadDRMPrivateKey returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::GenerateRSASignature(
const uint8_t* message, size_t message_length, uint8_t* signature,
size_t* signature_length, RSA_Padding_Scheme padding_scheme) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (!message || !signature || signature_length == 0) {
return CasStatus(CasStatusCode::kCryptoSessionError, "invalid argument");
}
OEMCryptoResult result = crypto_interface_->OEMCrypto_GenerateRSASignature(
session_, message, message_length, signature, signature_length,
padding_scheme);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_GenerateRSASignature returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::DeriveKeysFromSessionKey(
const uint8_t* enc_session_key, size_t enc_session_key_length,
const uint8_t* mac_key_context, size_t mac_key_context_length,
const uint8_t* enc_key_context, size_t enc_key_context_length) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (!enc_session_key || !mac_key_context || !enc_key_context) {
return CasStatus(CasStatusCode::kCryptoSessionError, "invalid argument");
}
OEMCryptoResult result =
crypto_interface_->OEMCrypto_DeriveKeysFromSessionKey(
session_, enc_session_key, enc_session_key_length, mac_key_context,
mac_key_context_length, enc_key_context, enc_key_context_length);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_DeriveKeysFromSessionKey returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::LoadLicense(const std::string& signed_message,
const std::string& core_message,
const std::string& signature) {
LOGV("Loading license: id = %u", session_);
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
const std::string combined_message = core_message + signed_message;
OEMCryptoResult result = crypto_interface_->OEMCrypto_LoadLicense(
session_, reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size());
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_LoadLicense returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::LoadRenewal(const std::string& signed_message,
const std::string& core_message,
const std::string& signature) {
LOGV("Loading license renewal: id = %u", session_);
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
const std::string combined_message = core_message + signed_message;
OEMCryptoResult result = crypto_interface_->OEMCrypto_LoadRenewal(
session_, reinterpret_cast<const uint8_t*>(combined_message.data()),
combined_message.size(), core_message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size());
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_LoadRenewal returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::LoadCasECMKeys(OEMCrypto_SESSION session,
const KeySlot* even_key,
const KeySlot* odd_key) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (nullptr == even_key && nullptr == odd_key) {
return CasStatus::OkStatus();
}
OEMCrypto_EntitledCasKeyObject even_ecko;
OEMCrypto_EntitledCasKeyObject odd_ecko;
std::string message;
if (nullptr != even_key) {
FillEntitledContentKeyObjectFromKeyData(message, *even_key, &even_ecko);
}
if (nullptr != odd_key) {
FillEntitledContentKeyObjectFromKeyData(message, *odd_key, &odd_ecko);
}
OEMCryptoResult result = crypto_interface_->OEMCrypto_LoadCasECMKeys(
session, reinterpret_cast<const uint8_t*>(message.data()), message.size(),
(nullptr == even_key ? nullptr : &even_ecko),
(nullptr == odd_key ? nullptr : &odd_ecko));
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_LoadCasECMKeys returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::SelectKey(OEMCrypto_SESSION session,
const std::vector<uint8_t>& key_id,
CryptoMode crypto_mode) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (current_key_id_.find(session) != current_key_id_.end() &&
key_id == current_key_id_.at(session) &&
current_crypto_mode_.find(session) != current_crypto_mode_.end() &&
crypto_mode == current_crypto_mode_.at(session)) {
return CasStatus::OkStatus();
}
OEMCryptoCipherMode oem_crypto_cipher_mode;
switch (crypto_mode) {
case CryptoMode::kAesCBC:
oem_crypto_cipher_mode = OEMCrypto_CipherMode_CBC;
break;
case CryptoMode::kAesCTR:
oem_crypto_cipher_mode = OEMCrypto_CipherMode_CTR;
break;
case CryptoMode::kDvbCsa2:
oem_crypto_cipher_mode = OEMCrypto_CipherMode_CSA2;
break;
case CryptoMode::kDvbCsa3:
oem_crypto_cipher_mode = OEMCrypto_CipherMode_CSA3;
break;
case CryptoMode::kAesOFB:
oem_crypto_cipher_mode = OEMCrypto_CipherMode_OFB;
break;
case CryptoMode::kAesSCTE:
oem_crypto_cipher_mode = OEMCrypto_CipherMode_SCTE;
break;
default:
std::ostringstream err_string;
err_string << "unsupported crypto mode " << static_cast<int>(crypto_mode);
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
OEMCryptoResult result = crypto_interface_->OEMCrypto_SelectKey(
session, key_id.data(), key_id.size(), oem_crypto_cipher_mode);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_SelectKey returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
current_key_id_[session] = key_id;
current_crypto_mode_[session] = crypto_mode;
return CasStatus::OkStatus();
}
bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
HdcpCapability* max) {
if (!crypto_interface_) {
return false;
}
if (current == nullptr || max == nullptr) {
LOGE(
"CryptoSession::GetHdcpCapabilities: |current|, |max| cannot be "
"NULL");
return false;
}
OEMCryptoResult status =
crypto_interface_->OEMCrypto_GetHDCPCapability(current, max);
if (OEMCrypto_SUCCESS != status) {
LOGW("OEMCrypto_GetHDCPCapability fails with %d", status);
return false;
}
return true;
}
OEMCryptoResult CryptoSession::getCryptoInterface(
std::unique_ptr<CryptoInterface>* interface) {
return CryptoInterface::create(interface);
}
CasStatus CryptoSession::GetDeviceID(std::string* buffer) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (buffer == nullptr) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing buffer for device id");
}
std::string intermediate;
intermediate.resize(32);
size_t buf_size = 32;
OEMCryptoResult result = crypto_interface_->OEMCrypto_GetDeviceID(
reinterpret_cast<uint8_t*>(&intermediate[0]), &buf_size);
// If the error is for a short buffer, resize the destination and retry
// reading the id. This should never happen.
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
intermediate.resize(buf_size);
result = crypto_interface_->OEMCrypto_GetDeviceID(
reinterpret_cast<uint8_t*>(&intermediate[0]), &buf_size);
}
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_GetDeviceID returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
buffer->resize(SHA256_DIGEST_LENGTH);
SHA256(reinterpret_cast<const uint8_t*>(intermediate.data()),
intermediate.size(), reinterpret_cast<uint8_t*>(&(*buffer)[0]));
return CasStatus::OkStatus();
}
const char* CryptoSession::SecurityLevel() {
if (!crypto_interface_) {
return nullptr;
}
return crypto_interface_->OEMCrypto_SecurityLevel();
}
CasStatus CryptoSession::CreateEntitledKeySession(
OEMCrypto_SESSION* entitled_key_session_id) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
if (entitled_key_session_id == nullptr) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"entitled_key_session_id is nullptr");
}
OEMCryptoResult result =
crypto_interface_->OEMCrypto_CreateEntitledKeySession(
session_, entitled_key_session_id);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_CreateEntitledKeySession returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::RemoveEntitledKeySession(
OEMCrypto_SESSION entitled_key_session_id) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
OEMCryptoResult result =
crypto_interface_->OEMCrypto_RemoveEntitledKeySession(
entitled_key_session_id);
if (result != OEMCrypto_SUCCESS) {
std::ostringstream err_string;
err_string << "OEMCrypto_RemoveEntitledKeySession returned " << result;
return CasStatus(CasStatusCode::kCryptoSessionError, err_string.str());
}
return CasStatus::OkStatus();
}
CasStatus CryptoSession::APIVersion(uint32_t* api_version) {
if (!crypto_interface_) {
return CasStatus(CasStatusCode::kCryptoSessionError,
"missing crypto interface");
}
uint32_t oemcrypto_api_version = crypto_interface_->OEMCrypto_APIVersion();
if (oemcrypto_api_version != kExpectedOEMCryptoVersion) {
std::ostringstream err_string;
err_string << "OEMCrypto_APIVersion returned: " << oemcrypto_api_version
<< ". While the correct API version should be: "
<< kExpectedOEMCryptoVersion;
return CasStatus(CasStatusCode::kOEMCryptoVersionMismatch,
err_string.str());
}
*api_version = oemcrypto_api_version;
return CasStatus::OkStatus();
}
} // namespace wvcas