// 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 #include #include #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 auto CryptoLock::WithStaticFieldWriteLock(const char* tag, Func body) -> decltype(body()) { LOGV("Static field write lock: %s", tag); std::unique_lock auto_lock(static_field_mutex_); return body(); } template auto CryptoLock::WithStaticFieldReadLock(const char* tag, Func body) -> decltype(body()) { LOGV("Static field read lock: %s", tag); shared_lock auto_lock(static_field_mutex_); return body(); } template auto CryptoLock::WithOecWriteLock(const char* tag, Func body) -> decltype(body()) { LOGV("OEMCrypto write lock: %s", tag); std::unique_lock auto_lock(oem_crypto_mutex_); return body(); } template auto CryptoLock::WithOecReadLock(const char* tag, Func body) -> decltype(body()) { LOGV("OEMCrypto read lock: %s", tag); shared_lock auto_lock(oem_crypto_mutex_); return body(); } template auto CryptoLock::WithOecSessionLock(const char* tag, Func body) -> decltype(body()) { LOGV("OEMCrypto Session Lock - %s", tag); shared_lock oec_auto_lock(oem_crypto_mutex_); std::unique_lock session_auto_lock(oem_crypto_session_mutex_); return body(); } bool CryptoInterface::initialized_ = false; int CryptoInterface::session_count_ = 0; std::unique_ptr CryptoInterface::lock_ = make_unique(); 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* 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 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(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(const_cast(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(const_cast(combined_message.data())), combined_message.size(), &core_message_length, reinterpret_cast(const_cast(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(const_cast(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(const_cast(combined_message.data())), combined_message.size(), &core_message_length, reinterpret_cast(const_cast(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(const_cast(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(const_cast(combined_message.data())), combined_message.size(), &core_message_length, reinterpret_cast(const_cast(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(combined_message.data()), combined_message.size(), core_message.size(), reinterpret_cast(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(combined_message.data()), combined_message.size(), core_message.size(), reinterpret_cast(signature.data()), signature.size(), reinterpret_cast(&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(combined_message.data()), combined_message.size(), core_message.size(), reinterpret_cast(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(combined_message.data()), combined_message.size(), core_message.size(), reinterpret_cast(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(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& 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(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* 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(&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(&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(intermediate.data()), intermediate.size(), reinterpret_cast(&(*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