diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index ae58eaaf..7e698d1a 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -14,6 +14,7 @@ class CryptoEngine; class WvCdmEventListener; typedef std::map CdmSessionMap; +typedef std::map CdmReleaseKeySetMap; class CdmEngine : public TimerHandler { public: @@ -24,12 +25,13 @@ class CdmEngine : public TimerHandler { CdmResponseType OpenSession(const CdmKeySystem& key_system, CdmSessionId* session_id); CdmResponseType CloseSession(const CdmSessionId& session_id); + CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id); + CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); // License related methods // Construct a valid license request CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, - bool is_key_system_present, - const CdmKeySystem& key_system, + const CdmKeySetId& key_set_id, const CdmInitData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, @@ -38,12 +40,14 @@ class CdmEngine : public TimerHandler { // Accept license response and extract key info. CdmResponseType AddKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data); + const CdmKeyResponse& key_data, + CdmKeySetId& key_set_id); + + CdmResponseType RestoreKey(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id); // Cancel session and unload keys. - CdmResponseType CancelKeyRequest(const CdmSessionId& session_id, - bool is_key_system_present, - const CdmKeySystem& key_system); + CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); // Construct valid renewal request for the current session keys. CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, @@ -121,9 +125,12 @@ class CdmEngine : public TimerHandler { void DisablePolicyTimer(); virtual void OnTimerEvent(); + virtual void OnKeyReleaseEvent(CdmKeySetId key_set_id); + // instance variables CdmSession* provisioning_session_; CdmSessionMap sessions_; + CdmReleaseKeySetMap release_key_sets_; // policy timer Timer policy_timer_; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index e47a594c..e50cbf5a 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -19,14 +19,16 @@ namespace wvcdm { class CdmSession { public: CdmSession() : session_id_(GenerateSessionId()), license_received_(false), - reinitialize_session_(false) {} + reinitialize_session_(false), license_type_(kLicenseTypeStreaming) {} ~CdmSession() {} CdmResponseType Init(); CdmResponseType ReInit(); - bool DestroySession(); + CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id, + const CdmLicenseType license_type); + void set_key_system(const CdmKeySystem& ksystem) { key_system_ = ksystem; } const CdmKeySystem& key_system() { return key_system_; } @@ -37,12 +39,13 @@ class CdmSession { CdmResponseType GenerateKeyRequest(const CdmInitData& init_data, const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, + const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, std::string* server_url); // AddKey() - Accept license response and extract key info. - CdmResponseType AddKey(const CdmKeyResponse& key_response); + CdmResponseType AddKey(const CdmKeyResponse& key_response, + CdmKeySetId* key_set_id); // CancelKeyRequest() - Cancel session. CdmResponseType CancelKeyRequest(); @@ -74,19 +77,31 @@ class CdmSession { // RenewKey() - Accept renewal response and update key info. CdmResponseType RenewKey(const CdmKeyResponse& key_response); + // License release + // GenerateReleaseRequest() - Construct valid release request for the current + // session keys. + CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request, + std::string* server_url); + + // RenewKey() - Accept renewal response and update key info. + CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); + bool IsKeyValid(const KeyId& key_id); bool AttachEventListener(WvCdmEventListener* listener); bool DetachEventListener(WvCdmEventListener* listener); void OnTimerEvent(); + void OnKeyReleaseEvent(CdmKeySetId key_set_id); private: // Generate unique ID for each new session. CdmSessionId GenerateSessionId(); + CdmKeySetId GenerateKeySetId(CdmInitData& pssh_data); bool LoadDeviceCertificate(std::string* cert, std::string* wrapped_key); + bool StoreLicense(bool active); // instance variables const CdmSessionId session_id_; @@ -97,6 +112,19 @@ class CdmSession { bool license_received_; bool reinitialize_session_; + CdmLicenseType license_type_; + + // license type offline related information + CdmInitData offline_pssh_data_; + CdmKeyMessage offline_key_request_; + CdmKeyResponse offline_key_response_; + CdmKeyMessage offline_key_renewal_request_; + CdmKeyResponse offline_key_renewal_response_; + std::string offline_release_server_url_; + + // license type release and offline related information + CdmKeySetId key_set_id_; + KeyId key_id_; // Used for certificate based licensing diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index d9294ebd..fa67e144 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -9,15 +9,41 @@ namespace wvcdm { class DeviceFiles { public: + typedef enum { + kLicenseStateActive, + kLicenseStateReleasing, + kLicenseStateUnknown, + } LicenseState; + static bool StoreCertificate(const std::string& certificate, const std::string& wrapped_private_key); static bool RetrieveCertificate(std::string* certificate, std::string* wrapped_private_key); + static bool StoreLicense(const std::string& key_set_id, + const LicenseState state, + const CdmInitData& pssh_data, + const CdmKeyMessage& key_request, + const CdmKeyResponse& key_response, + const CdmKeyMessage& key_renewal_request, + const CdmKeyResponse& key_renewal_response, + const std::string& release_server_url); + static bool RetrieveLicense(const std::string& key_set_id, + LicenseState* state, + CdmInitData* pssh_data, + CdmKeyMessage* key_request, + CdmKeyResponse* key_response, + CdmKeyMessage* key_renewal_request, + CdmKeyResponse* key_renewal_response, + std::string* release_server_url); + static bool DeleteLicense(const std::string& key_set_id); + static bool LicenseExists(const std::string& key_set_id); + static std::string GetBasePath(const char* dir); static const char* kBasePath; static const char* kPathDelimiter; static const char* kDeviceCertificateFileName; + static const char* kLicenseFileNameExt; private: static bool Hash(const std::string& data, std::string* hash); diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 4899c6b3..40713731 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -26,15 +26,21 @@ class CdmLicense { bool PrepareKeyRequest(const CdmInitData& pssh_data, const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, + const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request, std::string* server_url); - bool PrepareKeyRenewalRequest(CdmKeyMessage* signed_request, - std::string* server_url); + bool PrepareKeyUpdateRequest(bool is_renewal, + CdmKeyMessage* signed_request, + std::string* server_url); CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response); - CdmResponseType HandleKeyRenewalResponse( + CdmResponseType HandleKeyUpdateResponse( + bool is_renewal, const CdmKeyResponse& license_response); + bool RestoreOfflineLicense(CdmKeyMessage& license_request, + CdmKeyResponse& license_response, + CdmKeyResponse& license_renewal_response); + private: CdmResponseType HandleKeyErrorResponse(const SignedMessage& signed_message); diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 67d1b88b..6253a845 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -5,6 +5,8 @@ #include +#include "wv_cdm_types.h" + namespace wvcdm { static const size_t KEY_CONTROL_SIZE = 16; // TODO(kqyang): Key ID size is not fixed in spec, but conventionally we @@ -16,6 +18,11 @@ static const size_t KEY_PAD_SIZE = 16; static const size_t KEY_SIZE = 16; static const size_t MAC_KEY_SIZE = 32; +static const std::string SESSION_ID_PREFIX = "sid"; +static const std::string KEY_SET_ID_PREFIX = "ksid"; + +static const CdmKeySystem KEY_SYSTEM = "com.widevine"; + // define query keys, values here static const std::string QUERY_KEY_LICENSE_TYPE = "LicenseType"; // "Streaming", "Offline" diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h index b9bfb476..a8136709 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h @@ -11,15 +11,22 @@ namespace wvcdm { // The caller of the CDM API must provide an implementation for onEvent // and signal its intent by using the Attach/DetachEventListener methods // in the WvContentDecryptionModule class. +// The listener may also specify, when the instance is created, whether to be +// notified about events for a particular session or all sessions. class WvCdmEventListener { public: WvCdmEventListener() {} + WvCdmEventListener(CdmSessionId& session_id) : session_id_(session_id) {} virtual ~WvCdmEventListener() {} virtual void onEvent(const CdmSessionId& session_id, CdmEventType cdm_event) = 0; + virtual CdmSessionId session_id() { return session_id_; } + private: + CdmSessionId session_id_; + CORE_DISALLOW_COPY_AND_ASSIGN(WvCdmEventListener); }; diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 44815b6b..e2ec0da5 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -16,6 +16,7 @@ typedef std::string CdmKeyMessage; typedef std::string CdmKeyResponse; typedef std::string KeyId; typedef std::string CdmSessionId; +typedef std::string CdmKeySetId; typedef std::string RequestId; typedef uint32_t CryptoResult; typedef uint32_t CryptoSessionId; @@ -50,7 +51,8 @@ enum CdmEventType { enum CdmLicenseType { kLicenseTypeOffline, - kLicenseTypeStreaming + kLicenseTypeStreaming, + kLicenseTypeRelease }; // forward class references diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 6a87ea41..e2f3af24 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -7,6 +7,7 @@ #include "buffer_reader.h" #include "cdm_session.h" +#include "clock.h" #include "crypto_engine.h" #include "device_files.h" #include "license_protocol.pb.h" @@ -35,10 +36,13 @@ using video_widevine_server::sdk::ProvisioningRequest; using video_widevine_server::sdk::ProvisioningResponse; using video_widevine_server::sdk::SignedProvisioningMessage; -typedef std::map::const_iterator CdmSessionIter; +typedef std::map::const_iterator CdmSessionIter; +typedef std::map::iterator CdmReleaseKeySetIter; CdmEngine::CdmEngine() : provisioning_session_(NULL) { Properties::Init(); + Clock clock; + srand(static_cast(clock.GetCurrentTime() & 0xFFFFFFFF)); } CdmEngine::~CdmEngine() { @@ -65,23 +69,22 @@ CdmResponseType CdmEngine::OpenSession( return KEY_ERROR; } - // TODO(edwinwong, rfrias): Save key_system in session for validation checks CdmSession* new_session = new CdmSession(); if (!new_session) { + LOGE("CdmEngine::OpenSession: session creation failed"); return KEY_ERROR; } - if (new_session->session_id().empty()) { + CdmSessionId new_session_id = new_session->session_id(); + + if (new_session_id.empty()) { LOGE("CdmEngine::OpenSession: failure to generate session ID"); delete(new_session); return UNKNOWN_ERROR; } - CdmSessionId new_session_id = new_session->session_id(); - CdmResponseType sts = new_session->Init(); if (sts != NO_ERROR) { - LOGE("CdmEngine::OpenSession: bad session init"); delete(new_session); return sts; } @@ -91,6 +94,24 @@ CdmResponseType CdmEngine::OpenSession( return NO_ERROR; } +CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) { + LOGI("CdmEngine::OpenKeySetSession"); + + if (key_set_id.empty()) { + LOGI("CdmEngine::OpenKeySetSession: invalid key set id"); + return KEY_ERROR; + } + + CdmSessionId session_id; + CdmResponseType sts = OpenSession(KEY_SYSTEM, &session_id); + + if (sts != NO_ERROR) + return sts; + + release_key_sets_[key_set_id] = session_id; + return NO_ERROR; +} + CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) { LOGI("CdmEngine::CloseSession"); @@ -108,10 +129,24 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) { return NO_ERROR; } +CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) { + LOGI("CdmEngine::CloseKeySetSession"); + + CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id); + if (iter == release_key_sets_.end()) { + LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s", + key_set_id.c_str()); + return KEY_ERROR; + } + + CdmResponseType sts = CloseSession(iter->second); + release_key_sets_.erase(iter); + return sts; +} + CdmResponseType CdmEngine::GenerateKeyRequest( const CdmSessionId& session_id, - bool is_key_system_present, - const CdmKeySystem& key_system, + const CdmKeySetId& key_set_id, const CdmInitData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, @@ -119,14 +154,36 @@ CdmResponseType CdmEngine::GenerateKeyRequest( std::string* server_url) { LOGI("CdmEngine::GenerateKeyRequest"); - CdmSessionIter iter = sessions_.find(session_id); - if (iter == sessions_.end()) { - LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", session_id.c_str()); - return KEY_ERROR; + CdmSessionId id = session_id; + CdmResponseType sts; + + if (license_type == kLicenseTypeRelease) { + if (key_set_id.empty()) { + LOGE("CdmEngine::GenerateKeyRequest: invalid key set ID"); + return UNKNOWN_ERROR; + } + + if (!session_id.empty()) { + LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s", + session_id.c_str()); + return UNKNOWN_ERROR; + } + + CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id); + if (iter == release_key_sets_.end()) { + LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s", + key_set_id.c_str()); + return UNKNOWN_ERROR; + } + + id = iter->second; } - if (is_key_system_present) { - // TODO(edwinwong, rfrias): validate key_system has not changed + CdmSessionIter iter = sessions_.find(id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", + id.c_str()); + return KEY_ERROR; } if (!key_request) { @@ -136,33 +193,60 @@ CdmResponseType CdmEngine::GenerateKeyRequest( key_request->clear(); - // TODO(edwinwong, rfrias): need to pass in license type and app parameters - CdmResponseType sts = iter->second->GenerateKeyRequest(init_data, - license_type, - app_parameters, - key_request, - server_url); + if (license_type == kLicenseTypeRelease) { + sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease); + if (sts != KEY_ADDED) { + LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed," + "sts = %d", (int)sts); + return sts; + } + } + + sts = iter->second->GenerateKeyRequest(init_data, license_type, + app_parameters, key_request, + server_url); if (KEY_MESSAGE != sts) { - LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d", - (int)sts); + LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, " + "sts = %d", (int)sts); return sts; } - // TODO(edwinwong, rfrias): persist init_data, license_type, app_parameters - // in session + if (license_type == kLicenseTypeRelease) { + OnKeyReleaseEvent(key_set_id); + } return KEY_MESSAGE; } CdmResponseType CdmEngine::AddKey( const CdmSessionId& session_id, - const CdmKeyResponse& key_data) { + const CdmKeyResponse& key_data, + CdmKeySetId& key_set_id) { LOGI("CdmEngine::AddKey"); - CdmSessionIter iter = sessions_.find(session_id); + CdmSessionId id = session_id; + bool license_type_release = session_id.empty(); + + if (license_type_release) { + if (key_set_id.empty()) { + LOGI("CdmEngine::AddKey: invalid key set id"); + return KEY_ERROR; + } + + CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id); + if (iter == release_key_sets_.end()) { + LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id.c_str()); + return KEY_ERROR; + } + + id = iter->second; + } + + CdmSessionIter iter = sessions_.find(id); + if (iter == sessions_.end()) { - LOGE("CdmEngine::AddKey: session_id not found = %s", session_id.c_str()); + LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str()); return KEY_ERROR; } @@ -171,18 +255,41 @@ CdmResponseType CdmEngine::AddKey( return KEY_ERROR; } - CdmResponseType sts = iter->second->AddKey(key_data); + CdmResponseType sts = iter->second->AddKey(key_data, &key_set_id); + if (KEY_ADDED != sts) { LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts); + return sts; } - EnablePolicyTimer(); - return sts; + + if (!license_type_release) { + EnablePolicyTimer(); + } + + return KEY_ADDED; } -CdmResponseType CdmEngine::CancelKeyRequest( +CdmResponseType CdmEngine::RestoreKey( const CdmSessionId& session_id, - bool is_key_system_present, - const CdmKeySystem& key_system) { + const CdmKeySetId& key_set_id) { + LOGI("CdmEngine::RestoreKey"); + + if (key_set_id.empty()) { + LOGI("CdmEngine::RestoreKey: invalid key set id"); + return KEY_ERROR; + } + + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::RestoreKey: session_id not found = %s ", + session_id.c_str()); + return UNKNOWN_ERROR; + } + + return iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline); +} + +CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) { LOGI("CdmEngine::CancelKeyRequest"); //TODO(gmorgan): Issue: what is semantics of canceling a key request. Should @@ -197,10 +304,6 @@ CdmResponseType CdmEngine::CancelKeyRequest( return KEY_ERROR; } - if (is_key_system_present) { - // TODO(edwinwong, rfrias): validate key_system has not changed - } - // TODO(edwinwong, rfrias): unload keys here DisablePolicyTimer(); return NO_ERROR; @@ -747,37 +850,64 @@ bool CdmEngine::ExtractWidevinePssh( while (1) { // size of PSSH atom, used for skipping uint32_t size; - if (!reader.Read4(&size)) return false; + if (!reader.Read4(&size)) { + LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size"); + return false; + } // "pssh" std::vector pssh; - if (!reader.ReadVec(&pssh, 4)) return false; - if (memcmp(&pssh[0], "pssh", 4)) return false; + if (!reader.ReadVec(&pssh, 4)) { + LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal"); + return false; + } + if (memcmp(&pssh[0], "pssh", 4)) { + LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present"); + return false; + } // flags uint32_t flags; - if (!reader.Read4(&flags)) return false; - if (flags != 0) return false; + if (!reader.Read4(&flags)) { + LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags"); + return false; + } + if (flags != 0) { + LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero"); + return false; + } // system id std::vector system_id; - if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) return false; + if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) { + LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID"); + return false; + } if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) { // skip the remaining contents of the atom, // after size field, atom name, flags and system id if (!reader.SkipBytes( - size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) return false; + size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) { + LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom"); + return false; + } continue; } // size of PSSH box uint32_t pssh_length; - if (!reader.Read4(&pssh_length)) return false; + if (!reader.Read4(&pssh_length)) { + LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size"); + return false; + } output->clear(); - if (!reader.ReadString(output, pssh_length)) return false; + if (!reader.ReadString(output, pssh_length)) { + LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH"); + return false; + } return true; } @@ -806,4 +936,12 @@ void CdmEngine::OnTimerEvent() { } } +void CdmEngine::OnKeyReleaseEvent(CdmKeySetId key_set_id) { + + for (CdmSessionIter iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { + iter->second->OnKeyReleaseEvent(key_set_id); + } +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 75503f1b..23368aa5 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -5,12 +5,14 @@ #include #include +#include #include "clock.h" #include "cdm_engine.h" #include "crypto_engine.h" #include "device_files.h" #include "log.h" +#include "openssl/sha.h" #include "properties.h" #include "string_conversions.h" #include "wv_cdm_constants.h" @@ -28,17 +30,22 @@ CdmResponseType CdmSession::Init() { crypto_session_ = crypto_engine->CreateSession(session_id_); if (!crypto_session_) { + LOGE("CdmSession::Init crypto session creation failure"); return UNKNOWN_ERROR; } std::string token; if (Properties::use_certificates_as_identification()) { - if (!LoadDeviceCertificate(&token, &wrapped_key_)) + if (!LoadDeviceCertificate(&token, &wrapped_key_)) { + LOGE("CdmSession::Init provisioning needed"); return NEED_PROVISIONING; + } } else { - if (!crypto_engine->GetToken(&token)) + if (!crypto_engine->GetToken(&token)) { + LOGE("CdmSession::Init token retrieval failure"); return UNKNOWN_ERROR; + } } if (license_parser_.Init(token, crypto_session_, &policy_engine_)) @@ -60,6 +67,49 @@ bool CdmSession::DestroySession() { return true; } +CdmResponseType CdmSession::RestoreOfflineSession( + const CdmKeySetId& key_set_id, + const CdmLicenseType license_type) { + key_set_id_ = key_set_id; + + // Retrieve license information from persistent store + DeviceFiles::LicenseState license_state; + + if (!DeviceFiles::RetrieveLicense(key_set_id, &license_state, + &offline_pssh_data_, + &offline_key_request_, + &offline_key_response_, + &offline_key_renewal_request_, + &offline_key_renewal_response_, + &offline_release_server_url_)) { + LOGE("CdmSession::Init failed to retrieve license. key set id = %s", + key_set_id.c_str()); + return UNKNOWN_ERROR; + } + + if (license_state != DeviceFiles::kLicenseStateActive) { + LOGE("CdmSession::Init invalid offline license state = %s", license_state); + return UNKNOWN_ERROR; + } + + if (Properties::use_certificates_as_identification()) { + if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) { + return NEED_PROVISIONING; + } + } + + if (license_type == kLicenseTypeOffline) { + if (!license_parser_.RestoreOfflineLicense(offline_key_request_, + offline_key_response_, + offline_key_renewal_response_)) + return UNKNOWN_ERROR; + } + + license_received_ = true; + license_type_ = license_type; + return KEY_ADDED; +} + bool CdmSession::VerifySession(const CdmKeySystem& key_system, const CdmInitData& init_data) { // TODO(gmorgan): Compare key_system and init_data with value received @@ -70,7 +120,7 @@ bool CdmSession::VerifySession(const CdmKeySystem& key_system, CdmResponseType CdmSession::GenerateKeyRequest( const CdmInitData& init_data, const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, + const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, std::string* server_url) { @@ -82,7 +132,6 @@ CdmResponseType CdmSession::GenerateKeyRequest( reinitialize_session_ = false; } - if (!crypto_session_) { LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session"); return UNKNOWN_ERROR; @@ -93,10 +142,14 @@ CdmResponseType CdmSession::GenerateKeyRequest( return UNKNOWN_ERROR; } - if (license_received_) { + license_type_ = license_type; + + if (license_type_ == kLicenseTypeRelease) { + return GenerateReleaseRequest(key_request, server_url); + } + else if (license_received_) { // renewal return Properties::require_explicit_renew_request() ? - UNKNOWN_ERROR : GenerateRenewalRequest(key_request, - server_url); + UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url); } else { CdmInitData pssh_data; @@ -117,14 +170,22 @@ CdmResponseType CdmSession::GenerateKeyRequest( key_request, server_url)) { return KEY_ERROR; - } else { - return KEY_MESSAGE; } + + if (license_type_ == kLicenseTypeOffline) { + offline_pssh_data_ = pssh_data; + offline_key_request_ = *key_request; + offline_release_server_url_ = *server_url; + } + + return KEY_MESSAGE; } } // AddKey() - Accept license response and extract key info. -CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { +CdmResponseType CdmSession::AddKey( + const CdmKeyResponse& key_response, + CdmKeySetId* key_set_id) { if (!crypto_session_) { LOGW("CdmSession::AddKey: Invalid crypto session"); return UNKNOWN_ERROR; @@ -135,17 +196,34 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { return UNKNOWN_ERROR; } - if (license_received_) { + if (license_type_ == kLicenseTypeRelease) { + return ReleaseKey(key_response); + } + else if (license_received_) { // renewal return Properties::require_explicit_renew_request() ? UNKNOWN_ERROR : RenewKey(key_response); } else { CdmResponseType sts = license_parser_.HandleKeyResponse(key_response); - if (sts == KEY_ADDED) - license_received_ = true; + if (sts != KEY_ADDED) + return sts; - return sts; + license_received_ = true; + + if (license_type_ == kLicenseTypeOffline) { + offline_key_response_ = key_response; + key_set_id_ = GenerateKeySetId(offline_pssh_data_); + if (!StoreLicense(true)) { + LOGE("CdmSession::AddKey: Unable to store license"); + ReInit(); + key_set_id_.clear(); + return UNKNOWN_ERROR; + } + } + + *key_set_id = key_set_id_; + return KEY_ADDED; } } @@ -207,17 +285,49 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted, // session keys. CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request, std::string* server_url) { - if (!license_parser_.PrepareKeyRenewalRequest(key_request, - server_url)) { + if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url)) return KEY_ERROR; - } else { - return KEY_MESSAGE; + + if (license_type_ == kLicenseTypeOffline) { + offline_key_renewal_request_ = *key_request; } + return KEY_MESSAGE; } // RenewKey() - Accept renewal response and update key info. CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) { - return license_parser_.HandleKeyRenewalResponse(key_response); + CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(true, + key_response); + if (sts != KEY_ADDED) + return sts; + + if (license_type_ == kLicenseTypeOffline) { + offline_key_renewal_response_ = key_response; + if (!StoreLicense(true)) + return UNKNOWN_ERROR; + } + return KEY_ADDED; +} + +CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request, + std::string* server_url) { + if (license_parser_.PrepareKeyUpdateRequest(false, key_request, + server_url)) { + // Mark license as being released + if (!StoreLicense(false)) + return UNKNOWN_ERROR; + + return KEY_MESSAGE; + } + return UNKNOWN_ERROR; +} + +// ReleaseKey() - Accept release response and release license. +CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { + CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false, + key_response); + DeviceFiles::DeleteLicense(key_set_id_); + return sts; } bool CdmSession::IsKeyValid(const KeyId& key_id) { @@ -227,11 +337,39 @@ bool CdmSession::IsKeyValid(const KeyId& key_id) { } CdmSessionId CdmSession::GenerateSessionId() { - static const std::string kSessionPrefix("Session"); static int session_num = 1; // TODO(rkuroiwa): Want this to be unique. Probably doing Hash(time+init_data) // to get something that is reasonably unique. - return kSessionPrefix + IntToString(++session_num); + return SESSION_ID_PREFIX + IntToString(++session_num); +} + + +CdmSessionId CdmSession::GenerateKeySetId(CdmInitData& pssh_data) { + Clock clock; + int64_t current_time = clock.GetCurrentTime(); + std::string key_set_id; + + while (key_set_id.empty()) { + int random = rand(); + + std::vector hash(SHA256_DIGEST_LENGTH, 0); + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, pssh_data.data(), pssh_data.size()); + SHA256_Update(&sha256, ¤t_time, sizeof(int64_t)); + SHA256_Update(&sha256, &random, sizeof(random)); + SHA256_Final(&hash[0], &sha256); + for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) { + hash[i%(SHA256_DIGEST_LENGTH/4)] ^= hash[i]; + } + hash.resize(SHA256_DIGEST_LENGTH/4); + key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash); + + if (DeviceFiles::LicenseExists(key_set_id)) { // key set collision + key_set_id.clear(); + } + } + return key_set_id; } bool CdmSession::LoadDeviceCertificate(std::string* certificate, @@ -240,6 +378,18 @@ bool CdmSession::LoadDeviceCertificate(std::string* certificate, wrapped_key); } +bool CdmSession::StoreLicense(bool active) { + DeviceFiles::LicenseState state = DeviceFiles::kLicenseStateReleasing; + if (active) + state = DeviceFiles::kLicenseStateActive; + + return DeviceFiles::StoreLicense(key_set_id_, state, offline_pssh_data_, + offline_key_request_, offline_key_response_, + offline_key_renewal_request_, + offline_key_renewal_response_, + offline_release_server_url_); +} + bool CdmSession::AttachEventListener(WvCdmEventListener* listener) { std::pair result = listeners_.insert(listener); return result.second; @@ -258,7 +408,22 @@ void CdmSession::OnTimerEvent() { if (event_occurred) { for (CdmEventListenerIter iter = listeners_.begin(); iter != listeners_.end(); ++iter) { - (*iter)->onEvent(session_id(), event); + CdmSessionId id = (*iter)->session_id(); + if (id.empty() || (id.compare(session_id_) == 0)) { + (*iter)->onEvent(session_id_, event); + } + } + } +} + +void CdmSession::OnKeyReleaseEvent(CdmKeySetId key_set_id) { + if (key_set_id_.compare(key_set_id) == 0) { + for (CdmEventListenerIter iter = listeners_.begin(); + iter != listeners_.end(); ++iter) { + CdmSessionId id = (*iter)->session_id(); + if (id.empty() || (id.compare(session_id_) == 0)) { + (*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT); + } } } } diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 3e9f8e9c..9d4ca854 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -18,10 +18,14 @@ namespace wvcdm { const char* DeviceFiles::kBasePath = "/data/mediadrm/IDM"; const char* DeviceFiles::kPathDelimiter = "/"; const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin"; +const char* DeviceFiles::kLicenseFileNameExt = ".lic"; // Protobuf generated classes. using video_widevine_client::sdk::DeviceCertificate; using video_widevine_client::sdk::HashedFile; +using video_widevine_client::sdk::License; +using video_widevine_client::sdk::License_LicenseState_ACTIVE; +using video_widevine_client::sdk::License_LicenseState_RELEASING; bool DeviceFiles::StoreCertificate(const std::string& certificate, const std::string& wrapped_private_key) { @@ -107,6 +111,148 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate, return true; } +bool DeviceFiles::StoreLicense( + const std::string& key_set_id, + const LicenseState state, + const CdmInitData& pssh_data, + const CdmKeyMessage& license_request, + const CdmKeyResponse& license_message, + const CdmKeyMessage& license_renewal_request, + const CdmKeyResponse& license_renewal, + const std::string& release_server_url) { + // Fill in file information + video_widevine_client::sdk::File file; + + file.set_type(video_widevine_client::sdk::File::LICENSE); + file.set_version(video_widevine_client::sdk::File::VERSION_1); + + License* license = file.mutable_license(); + switch(state) { + case kLicenseStateActive: + license->set_state(License_LicenseState_ACTIVE); + break; + case kLicenseStateReleasing: + license->set_state(License_LicenseState_RELEASING); + break; + default: + LOGW("DeviceFiles::StoreLicense: Unknown license state: %u", state); + return false; + break; + } + license->set_pssh_data(pssh_data); + license->set_license_request(license_request); + license->set_license(license_message); + license->set_renewal_request(license_renewal_request); + license->set_renewal(license_renewal); + license->set_release_server_url(release_server_url); + + std::string serialized_string; + file.SerializeToString(&serialized_string); + + // calculate SHA hash + std::string hash; + if (!Hash(serialized_string, &hash)) { + LOGW("DeviceFiles::StoreLicense: Hash computation failed"); + return false; + } + + // File in hashed file data + HashedFile hashed_file; + hashed_file.set_file(serialized_string); + hashed_file.set_hash(hash); + + hashed_file.SerializeToString(&serialized_string); + + std::string file_name = key_set_id + kLicenseFileNameExt; + return StoreFile(file_name.c_str(), serialized_string); +} + +bool DeviceFiles::RetrieveLicense( + const std::string& key_set_id, + LicenseState* state, + CdmInitData* pssh_data, + CdmKeyMessage* license_request, + CdmKeyResponse* license_message, + CdmKeyMessage* license_renewal_request, + CdmKeyResponse* license_renewal, + std::string* release_server_url) { + std::string serialized_hashed_file; + std::string file_name = key_set_id + kLicenseFileNameExt; + if (!RetrieveFile(file_name.c_str(), &serialized_hashed_file)) + return false; + + HashedFile hashed_file; + if (!hashed_file.ParseFromString(serialized_hashed_file)) { + LOGW("DeviceFiles::RetrieveLicense: Unable to parse hash file"); + return false; + } + + std::string hash; + if (!Hash(hashed_file.file(), &hash)) { + LOGW("DeviceFiles::RetrieveLicense: Hash computation failed"); + return false; + } + + if (hash.compare(hashed_file.hash())) { + LOGW("DeviceFiles::RetrieveLicense: Hash mismatch"); + return false; + } + + video_widevine_client::sdk::File file; + if (!file.ParseFromString(hashed_file.file())) { + LOGW("DeviceFiles::RetrieveLicense: Unable to parse file"); + return false; + } + + if (file.type() != video_widevine_client::sdk::File::LICENSE) { + LOGW("DeviceFiles::RetrieveLicense: Incorrect file type"); + return false; + } + + if (file.version() != video_widevine_client::sdk::File::VERSION_1) { + LOGW("DeviceFiles::RetrieveLicense: Incorrect file version"); + return false; + } + + if (!file.has_license()) { + LOGW("DeviceFiles::RetrieveLicense: License not present"); + return false; + } + + License license = file.license(); + + switch(license.state()) { + case License_LicenseState_ACTIVE: + *state = kLicenseStateActive; + break; + case License_LicenseState_RELEASING: + *state = kLicenseStateReleasing; + break; + default: + LOGW("DeviceFiles::RetrieveLicense: Unrecognized license state: %u", + kLicenseStateUnknown); + *state = kLicenseStateUnknown; + break; + } + *pssh_data = license.pssh_data(); + *license_request = license.license_request(); + *license_message = license.license(); + *license_renewal_request = license.renewal_request(); + *license_renewal = license.renewal(); + *release_server_url = license.release_server_url(); + return true; +} + +bool DeviceFiles::DeleteLicense(const std::string& key_set_id) { + std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt; + return File::Remove(path); +} + +bool DeviceFiles::LicenseExists(const std::string& key_set_id) { + std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt; + return File::Exists(path); +} + bool DeviceFiles::Hash(const std::string& data, std::string* hash) { if (!hash) return false; @@ -147,6 +293,7 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) { return false; } + LOGV("DeviceFiles::StoreFile: success: %s (%db)", path.c_str(), data.size()); return true; } @@ -184,6 +331,8 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) { return false; } + LOGV("DeviceFiles::RetrieveFile: success: %s (%db)", path.c_str(), + data->size()); return true; } diff --git a/libwvdrmengine/cdm/core/src/device_files.proto b/libwvdrmengine/cdm/core/src/device_files.proto index 339ed9c1..d561c831 100644 --- a/libwvdrmengine/cdm/core/src/device_files.proto +++ b/libwvdrmengine/cdm/core/src/device_files.proto @@ -19,10 +19,18 @@ message DeviceCertificate { } message License { - optional bytes key_set_id = 1; + enum LicenseState { + ACTIVE = 1; + RELEASING = 2; + } + + optional LicenseState state = 1; optional bytes pssh_data = 2; optional bytes license_request = 3; optional bytes license = 4; + optional bytes renewal_request = 5; + optional bytes renewal = 6; + optional bytes release_server_url = 7; } message File { @@ -38,7 +46,7 @@ message File { optional FileType type = 1; optional FileVersion version = 2 [default = VERSION_1]; optional DeviceCertificate device_certificate = 3; - repeated License licenses = 4; + optional License license = 4; } message HashedFile { diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 753f9b6b..cf4f8520 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -36,8 +36,6 @@ using video_widevine_server::sdk::License; using video_widevine_server::sdk::License_KeyContainer; using video_widevine_server::sdk::LicenseError; using video_widevine_server::sdk::SignedMessage; -using video_widevine_server::sdk::STREAMING; -using video_widevine_server::sdk::VERSION_2_1; static std::vector ExtractContentKeys(const License& license) { @@ -97,7 +95,7 @@ bool CdmLicense::Init(const std::string& token, bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, const CdmLicenseType license_type, - CdmAppParameterMap& app_parameters, + const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request, std::string* server_url) { if (!session_ || @@ -180,7 +178,20 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, LicenseRequest_ContentIdentification_CENC* cenc_content_id = content_id->mutable_cenc_id(); cenc_content_id->add_pssh(init_data); - cenc_content_id->set_license_type(STREAMING); + + switch (license_type) { + case kLicenseTypeOffline: + cenc_content_id->set_license_type(video_widevine_server::sdk::OFFLINE); + break; + case kLicenseTypeStreaming: + cenc_content_id->set_license_type(video_widevine_server::sdk::STREAMING); + break; + default: + LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %u", + (int)license_type); + return false; + break; + } cenc_content_id->set_request_id(request_id); // TODO(jfore): The time field will be updated once the cdm wrapper @@ -197,7 +208,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, } license_request.set_key_control_nonce(UintToString(nonce)); LOGD("PrepareKeyRequest: nonce=%u", nonce); - license_request.set_protocol_version(VERSION_2_1); + license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1); // License request is complete. Serialize it. std::string serialized_license_req; @@ -215,6 +226,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, } if (license_request_signature.empty()) { + LOGE("CdmLicense::PrepareKeyRequest: License request signature empty"); signed_request->clear(); return false; } @@ -231,22 +243,27 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, return true; } -bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request, - std::string* server_url) { +bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal, + CdmKeyMessage* signed_request, + std::string* server_url) { if (!session_) { + LOGE("CdmLicense::PrepareKeyUpdateRequest: Invalid crypto session"); return false; } if (!signed_request) { - LOGE("CdmLicense::PrepareKeyRenewalRequest : No signed request provided."); + LOGE("CdmLicense::PrepareKeyUpdateRequest: No signed request provided"); return false; } if (!server_url) { - LOGE("CdmLicense::PrepareKeyRenewalRequest : No server url provided."); + LOGE("CdmLicense::PrepareKeyUpdateRequest: No server url provided"); return false; } LicenseRequest license_request; - license_request.set_type(LicenseRequest::RENEWAL); + if (is_renewal) + license_request.set_type(LicenseRequest::RENEWAL); + else + license_request.set_type(LicenseRequest::RELEASE); LicenseRequest_ContentIdentification_ExistingLicense* current_license = license_request.mutable_content_id()->mutable_license(); @@ -259,8 +276,8 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request, return false; } license_request.set_key_control_nonce(UintToString(nonce)); - LOGD("PrepareKeyRenewalRequest: nonce=%u", nonce); - license_request.set_protocol_version(VERSION_2_1); + LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce); + license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1); // License request is complete. Serialize it. std::string serialized_license_req; @@ -272,7 +289,11 @@ bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request, &license_request_signature)) return false; - if (license_request_signature.empty()) return false; + if (license_request_signature.empty()) { + LOGE("CdmLicense::PrepareKeyUpdateRequest: empty license request" + " signature"); + return false; + } // Put serialize license request and signature together SignedMessage signed_message; @@ -369,44 +390,60 @@ CdmResponseType CdmLicense::HandleKeyResponse( } } -CdmResponseType CdmLicense::HandleKeyRenewalResponse( +CdmResponseType CdmLicense::HandleKeyUpdateResponse( + bool is_renewal, const CdmKeyResponse& license_response) { if (!session_) { return KEY_ERROR; } if (license_response.empty()) { - LOGE("CdmLicense::HandleKeyRenewalResponse : Empty license response."); + LOGE("CdmLicense::HandleKeyUpdateResponse : Empty license response."); return KEY_ERROR; } SignedMessage signed_response; - if (!signed_response.ParseFromString(license_response)) + if (!signed_response.ParseFromString(license_response)) { + LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse signed message"); return KEY_ERROR; + } if (signed_response.type() == SignedMessage::ERROR) { return HandleKeyErrorResponse(signed_response); } - if (!signed_response.has_signature()) + if (!signed_response.has_signature()) { + LOGE("CdmLicense::HandleKeyUpdateResponse: signature missing"); return KEY_ERROR; + } License license; - if (!license.ParseFromString(signed_response.msg())) + if (!license.ParseFromString(signed_response.msg())) { + LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse license" + " from signed message"); return KEY_ERROR; + } - if (!license.has_id()) return KEY_ERROR; + if (!license.has_id()) { + LOGE("CdmLicense::HandleKeyUpdateResponse: license id not present"); + return KEY_ERROR; + } if (license.id().version() > license_id_.version()) { // This is the normal case. license_id_.CopyFrom(license.id()); - if (license.policy().has_renewal_server_url() && - license.policy().renewal_server_url().size() > 0) { - server_url_ = license.policy().renewal_server_url(); + if (is_renewal) { + if (license.policy().has_renewal_server_url() && + license.policy().renewal_server_url().size() > 0) { + server_url_ = license.policy().renewal_server_url(); + } } policy_engine_->UpdateLicense(license); + if (!is_renewal) + return KEY_ADDED; + std::vector key_array = ExtractContentKeys(license); if (session_->RefreshKeys(signed_response.msg(), @@ -423,15 +460,67 @@ CdmResponseType CdmLicense::HandleKeyRenewalResponse( // This isn't supposed to happen. // TODO(jfore): Handle wrap? We can miss responses and that should be // considered normal until retries are exhausted. + LOGE("CdmLicense::HandleKeyUpdateResponse: license version: expected > %u," + " actual = %u", license_id_.version(), license.id().version()); return KEY_ERROR; } +bool CdmLicense::RestoreOfflineLicense( + CdmKeyMessage& license_request, + CdmKeyResponse& license_response, + CdmKeyResponse& license_renewal_response) { + + if (license_request.empty() || license_response.empty()) { + LOGE("CdmLicense::RestoreOfflineLicense: key_request or response empty: " + "%u %u", license_request.size(), license_response.size()); + return false; + } + + SignedMessage signed_request; + if (!signed_request.ParseFromString(license_request)) { + LOGE("CdmLicense::RestoreOfflineLicense: license_request parse failed"); + return false; + } + + if (signed_request.type() != SignedMessage::LICENSE_REQUEST) { + LOGE("CdmLicense::RestoreOfflineLicense: license request type: expected = " + "%d, actual = %d", + SignedMessage::LICENSE_REQUEST, + signed_request.type()); + return false; + } + + if (Properties::use_certificates_as_identification()) { + key_request_ = signed_request.msg(); + } + else { + if (!session_->GenerateDerivedKeys(signed_request.msg())) + return false; + } + + CdmResponseType sts = HandleKeyResponse(license_response); + + if (sts != KEY_ADDED) + return false; + + if (!license_renewal_response.empty()) { + sts = HandleKeyUpdateResponse(true, license_renewal_response); + + if (sts != KEY_ADDED) + return false; + } + + return true; +} + CdmResponseType CdmLicense::HandleKeyErrorResponse( const SignedMessage& signed_message) { LicenseError license_error; - if (!license_error.ParseFromString(signed_message.msg())) + if (!license_error.ParseFromString(signed_message.msg())) { + LOGE("CdmLicense::HandleKeyErrorResponse: Unable to parse license error"); return KEY_ERROR; + } switch (license_error.error_code()) { case LicenseError::INVALID_CREDENTIALS: @@ -440,6 +529,8 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse( return DEVICE_REVOKED; case LicenseError::SERVICE_UNAVAILABLE: default: + LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d", + license_error.error_code()); return KEY_ERROR; } } diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 6c10eb21..bed53f37 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -36,14 +36,14 @@ class WvCdmEngineTest : public testing::Test { const std::string& init_data) { wvcdm::CdmAppParameterMap app_parameters; std::string server_url; + std::string key_set_id; EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_, - true, // is_key_system_present - key_system, - init_data, - kLicenseTypeStreaming, - app_parameters, - &key_msg_, - &server_url), wvcdm::KEY_MESSAGE); + key_set_id, + init_data, + kLicenseTypeStreaming, + app_parameters, + &key_msg_, + &server_url), wvcdm::KEY_MESSAGE); } void GenerateRenewalRequest(const std::string& key_system, @@ -125,7 +125,7 @@ class WvCdmEngineTest : public testing::Test { return ""; } - url_request.PostRequest(key_msg_); + url_request.PostRequestChunk(key_msg_); std::string http_response; std::string message_body; int resp_bytes = url_request.GetResponse(http_response); @@ -166,7 +166,9 @@ class WvCdmEngineTest : public testing::Test { EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED); } else { - EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp), wvcdm::KEY_ADDED); + std::string key_set_id; + EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, key_set_id), + wvcdm::KEY_ADDED); } } diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 8b2ed697..7088a629 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -6,6 +6,136 @@ namespace wvcdm { +namespace { + std::string kKeySetId1 = "ksid05e79dab2750370195d1"; + std::string kKeySetId2 = "ksid17fe1e9005934171bbd1"; + std::string kKeySetId3 = "ksid2eadb793100566012934"; + + std::string kPsshData1 = "0801121093789920E8D6520098577DF8F2DD5546"; + std::string kPsshData2 = "0801121030313233343536373839616263646566"; + std::string kPsshData3 = "08011210e02562e04cd55351b14b3d748d36ed8e"; + + std::string kKeyRequest1 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE" + "13AA95C122678053362136BD18D6DDC78C05228E023082010A0282010100B2873F12C61" + "8883986B67A404733168D34676EA3261231B4EB12CB6F862AAE529D21B449D78E7A8FBB" + "9CC1A8AE119D89EC571EA23EE92517AFB86EA089E81CF3A773AEDEDFC1979AEE115D657" + "FE372CBF4178E57F60BEDD15491EF28891ED8663437019C0EF5F79FD0ADDAAE87281DD9" + "10ED10356DD6B606C0DD2452343329151BD23A42060869252892BD2C0368787E880F716" + "438E5835BC93FED65D0017D8758E20E5E614B44BAFAF7286484624CC5EB109D85F1C048" + "9B25A75528C0CD23961067192EF65766EAB5E97FE7EBE25D63DDE41BB34048B8A378EAE" + "AEBC86C44AB4AA757F29B48C9476660DCC4EE656EB35AC309213399CBEF38394FE32B88" + "3F9B02030100012899203001128002435CAFA39AD7DC12A40F943FE8DE10C5DD911059D" + "7B0990742E11419B2E6E797EAE46B5D017F0EF3F429AA43E4F570BBE46F56BBCEACBDFA" + "8495E4451249D39D5FEFC4F5557498B233C4C9C101E2388E107582D3C3FBF266AB9FE96" + "A40405A2098583B33D7AD083E105458DF7AE92FD517D538B4671614DA0D167F9661EE0C" + "574AB95A5BE87B1BEE27659D875A37A1730F84033ACB6BCA8D3C575A2692E7390F062BC" + "E1E58D9C621A1A11CB215615ED0C5C12"; + std::string kKeyRequest2 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE" + "13AA95C122678053362136BD188BDCC78C05228E023082010A0282010100C53F667AAA8" + "27758D3ABF81DAFA223373473EE13E4C500A09CD35CE2620D5DA0D987A237EBFCE1F793" + "D6F88187749A85B3AF4D737899EF19C3029461E95202E9DF91DD5869FE482FCA17160AF" + "7AC380D03880EBE3EA55B7FB3ED5ED6B9FAD19234AA75BB642A9A86AEF7CDA86F7B2EC7" + "C40C31C380F95C1F3C1564B012D530A7971AA656BBD27DAB715F0D5771FB7608453AF88" + "A331E3E5852F6F9B076A95C807307E541C39216D64B02D180A1009AFB60777EF7C72275" + "033ABD6FDF12145FFD379B6CB25F1127A783A40D65BF95AB74A08E39209EF2C0341A92A" + "217FD223734C7D1F0DC0D5B50FBA521FF1F841D1E5ED18DFC13A6D28E5A0A4FACBE6C78" + "31B5020301000128992030011280022DBC4444671C0C4CF0FBB3BB30C80C78FAFA67223" + "C6FE3F0BB495B1959B7472E8A64BC13838B3FB731FAC0609FEAB325FE8B7358C2F63D5E" + "E6C0380B85DBA3FE207B9D6AE7C31467A36A9E8D3D786CC4C383BC2774131D6C099D06D" + "052ED13A4545A0026A4FC949DE3B79D1C0C6A0D581A3D3598E46D3E4AC827C94045D693" + "37CAF103CC8839EA7FF020C0721AB17B140640C5E31C1727073CA48F445DA5EE55521D8" + "85732BAA17BD672B62C717ADBC13F235"; + std::string kKeyRequest3 = "0801128D0C0ACF0B080112EF090AB0020802121007D9FFDE" + "13AA95C122678053362136BD18D6DDC78C05228E023082010A0282010100B2873F12C61" + "8883986B67A404733168D34676EA3261231B4EB12CB6F862AAE529D21B449D78E7A8FBB" + "9CC1A8AE119D89EC571EA23EE92517AFB86EA089E81CF3A773AEDEDFC1979AEE115D657" + "FE372CBF4178E57F60BEDD15491EF28891ED8663437019C0EF5F79FD0ADDAAE87281DD9" + "10ED10356DD6B606C0DD2452343329151BD23A42060869252892BD2C0368787E880F716" + "438E5835BC93FED65D0017D8758E20E5E614B44BAFAF7286484624CC5EB109D85F1C048" + "9B25A75528C0CD23961067192EF65766EAB5E97FE7EBE25D63DDE41BB34048B8A378EAE" + "AEBC86C44AB4AA757F29B48C9476660DCC4EE656EB35AC309213399CBEF38394FE32B88" + "3F9B02030100012899203001128002435CAFA39AD7DC12A40F943FE8DE10C5DD911059D" + "7B0990742E11419B2E6E797EAE46B5D017F0EF3F429AA43E4F570BBE46F56BBCEACBDFA" + "8495E4451249D39D5FEFC4F5557498B233C4C9C101E2388E107582D3C3FBF266AB9FE96" + "A40405A2098583B33D7AD083E105458DF7AE92FD517D538B4671614DA0D167F9661EE0C" + "574AB95A5BE87B1BEE27659D875A37A1730F84033ACB6BCA8D3C575A2692E7390F062BC" + "E1E58D9C621A1A11CB215615ED0C5C12"; + + std::string kKeyResponse1 = "080212C2040A460A0939383736353433323112086D03DC5" + "FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B" + "6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013" + "0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D" + "6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766" + "964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769" + "646576696E65483C503C1A661210EBA4B8B97E17DC6C45D359222B9327121A50583647C" + "BC8240C2760A79DC72DF7FC0216C0943F12307B9AAED429277A6D0024F93CCA17B7F2BA" + "C19875E041F12E6729A98E0DFF2EA9806D6C20AF801A5A3E19DAF918FC9AF230C8765DF" + "B5BE869DF7320011A80010A10E02562E04CD55351B14B3D748D36ED8E12107C9AC11A9D" + "39304821C47969729A43321A2077E79664AF73B13FC6C55B5B9B2000E174E94F7D9E9BB" + "B58A547A82FAAAF46182002280242340A202031C7ADB3C2F80975499E285CEE0AF8343A" + "F3DF50A49EEBCCEB3747C955FEFB12107E021073F71CF814A9CCAA7008B38CD81A80010" + "A100065901A64A25899A5193664ABF9AF621210874AB758C27A29677D62D2EE092905E2" + "1A207C4AD3CB7A80740215598D5E74883"; + std::string kKeyResponse2 = "080212C2040A460A0939383736353433323112080C1913B" + "45E8893481A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B" + "6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013" + "0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D" + "6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766" + "964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769" + "646576696E65483C503C1A661210A2B662DC43891A32E65E0CC31CD597B21A50046489D" + "7EE8A674A4976800FD0553075498F90FD1997A63AECFF3481BF5CA908502676FA7E7E6B" + "02179FE6C42FAE7351EF85C8494BBE0782CB4C7A5AA6E439590C2D39DD5DB2B1A3DA91E" + "3D43FA5ABDD20011A80010A10E02562E04CD55351B14B3D748D36ED8E1210B0EF7CC3B2" + "0E10F4F704DBCF3142AB611A20F684136083E458C6B2977DB4D1FC559F2AFF2C454FEDF" + "CD68F29D50D351D58DC2002280242340A20223C0E4281615524569AF90EC3BE98349B27" + "0016C5DCC4CFDF11CB55C9DCFB231210888EF5E2311B3B3407549903D8F30D771A80010" + "A100065901A64A25899A5193664ABF9AF621210F38F473DC71703C52CC970FCACFA2111" + "1A20067E176521B6176A97157EDB9D06C"; + std::string kKeyResponse3 = "080212C2040A460A0939383736353433323112086D03DC5" + "FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396755464B" + "6963455954335A34626738366C77200128001283010801180120809A9E0128809A9E013" + "0AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F766964656F2D" + "6465762F6865617274626561742F63656E633F736F757263653D594F555455424526766" + "964656F5F69643D45474843364F484E624F6F266F617574683D79612E67747371617769" + "646576696E65483C503C1A661210EBA4B8B97E17DC6C45D359222B9327121A50583647C" + "BC8240C2760A79DC72DF7FC0216C0943F12307B9AAED429277A6D0024F93CCA17B7F2BA" + "C19875E041F12E6729A98E0DFF2EA9806D6C20AF801A5A3E19DAF918FC9AF230C8765DF" + "B5BE869DF7320011A80010A10E02562E04CD55351B14B3D748D36ED8E12107C9AC11A9D" + "39304821C47969729A43321A2077E79664AF73B13FC6C55B5B9B2000E174E94F7D9E9BB" + "B58A547A82FAAAF46182002280242340A202031C7ADB3C2F80975499E285CEE0AF8343A" + "F3DF50A49EEBCCEB3747C955FEFB12107E021073F71CF814A9CCAA7008B38CD81A80010" + "A100065901A64A25899A5193664ABF9AF621210874AB758C27A29677D62D2EE092905E2" + "1A207C4AD3CB7A80740215598D5E74883"; + + std::string kKeyRenewalRequest1 = ""; + std::string kKeyRenewalRequest2 = "0801125E124A1A480A460A0939383736353433323" + "112080C1913B45E8893481A2B596E4B58753250316A5F53785073494E6B377459354E79" + "67396755464B6963455954335A34626738366C772001280018022A0C383231353639373" + "13400000030151A20789770161DD09DD5210E1A399DCA51C7741D2FC83488B731D22F4C" + "B01E74D240"; + std::string kKeyRenewalRequest3 = "0801125E124A1A480A460A0939383736353433323" + "112086D03DC5FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E79" + "67396755464B6963455954335A34626738366C772001280018022A0C313736393630363" + "23739000030151A209ABDAF1E996DFED08BF64B71356DAD8244BFAD335749206C2A1749" + "61CB15B9AD"; + + std::string kKeyRenewalResponse1 = ""; + std::string kKeyRenewalResponse2 = ""; + std::string kKeyRenewalResponse3 = "080212EC010A460A093938373635343332311208" + "6D03DC5FD3BB58031A2B596E4B58753250316A5F53785073494E6B377459354E7967396" + "755464B6963455954335A34626738366C77200128011283010801180120809A9E012880" + "9A9E0130AC02426C68747470733A2F2F6A6D7431372E676F6F676C652E636F6D2F76696" + "4656F2D6465762F6865617274626561742F63656E633F736F757263653D594F55545542" + "4526766964656F5F69643D45474843364F484E624F6F266F617574683D79612E6774737" + "1617769646576696E65483C503C1A16200342120A106B63746C0000012C697A0C870000" + "000820E3DDC78C051A20466E04142A0F410F5BA032BD8B0B77C800E9972C707E034E2ED" + "E83661C6C5386"; + + std::string kKeyReleaseServerUrl1 = "http://hamid.kir.corp.google.com:8888/drm"; + std::string kKeyReleaseServerUrl2 = "https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE"; + std::string kKeyReleaseServerUrl3 = "https://jmt17.google.com/video-dev/license/GetCencLicense"; +} + TEST(DeviceFilesTest, StoreCertificate) { std::string device_base_path = DeviceFiles::GetBasePath(DeviceFiles::kBasePath); @@ -93,4 +223,341 @@ TEST(DeviceFilesTest, RetrieveCertificate) { EXPECT_TRUE(File::Remove(device_base_path)); } +TEST(DeviceFilesTest, StoreLicense) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string license_path_1 = device_base_path + kKeySetId1 + + DeviceFiles::kLicenseFileNameExt; + + if (!File::Exists(device_base_path)) + EXPECT_TRUE(File::CreateDirectory(device_base_path)); + if (File::Exists(license_path_1)) + EXPECT_TRUE(File::Remove(license_path_1)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, + DeviceFiles::kLicenseStateActive, + kPsshData1, kKeyRequest1, + kKeyResponse1, kKeyRenewalRequest1, + kKeyRenewalResponse1, + kKeyReleaseServerUrl1)); + size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + + kKeyRequest1.size() + kKeyResponse1.size() + + kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + + kKeyReleaseServerUrl1.size(); + EXPECT_TRUE(File::Exists(license_path_1)); + EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); +} + +TEST(DeviceFilesTest, StoreLicenseInitial) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string license_path_1 = device_base_path + kKeySetId1 + + DeviceFiles::kLicenseFileNameExt; + + if (!File::Exists(device_base_path)) + EXPECT_TRUE(File::Remove(device_base_path)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, + DeviceFiles::kLicenseStateActive, + kPsshData1, kKeyRequest1, + kKeyResponse1, kKeyRenewalRequest1, + kKeyRenewalResponse1, + kKeyReleaseServerUrl1)); + size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + + kKeyRequest1.size() + kKeyResponse1.size() + + kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + + kKeyReleaseServerUrl1.size(); + EXPECT_TRUE(File::Exists(license_path_1)); + EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); +} + +TEST(DeviceFilesTest, StoreLicenses) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string license_path_1 = device_base_path + kKeySetId1 + + DeviceFiles::kLicenseFileNameExt; + std::string license_path_2 = device_base_path + kKeySetId2 + + DeviceFiles::kLicenseFileNameExt; + std::string license_path_3 = device_base_path + kKeySetId3 + + DeviceFiles::kLicenseFileNameExt; + + if (!File::Exists(device_base_path)) + EXPECT_TRUE(File::Remove(device_base_path)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, + DeviceFiles::kLicenseStateActive, + kPsshData1, kKeyRequest1, + kKeyResponse1, kKeyRenewalRequest1, + kKeyRenewalResponse1, + kKeyReleaseServerUrl1)); + size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + + kKeyRequest1.size() + kKeyResponse1.size() + + kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + + kKeyReleaseServerUrl1.size(); + EXPECT_TRUE(File::Exists(license_path_1)); + EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId2, + DeviceFiles::kLicenseStateReleasing, + kPsshData2, kKeyRequest2, + kKeyResponse2, kKeyRenewalRequest2, + kKeyRenewalResponse2, + kKeyReleaseServerUrl2)); + size = sizeof(DeviceFiles::LicenseState) + kPsshData2.size() + + kKeyRequest2.size() + kKeyResponse2.size() + + kKeyRenewalRequest2.size() + kKeyRenewalResponse2.size() + + kKeyReleaseServerUrl2.size(); + EXPECT_TRUE(File::Exists(license_path_2)); + EXPECT_GT(File::FileSize(license_path_2), static_cast(size)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId3, + DeviceFiles::kLicenseStateReleasing, + kPsshData3, kKeyRequest3, + kKeyResponse3, kKeyRenewalRequest3, + kKeyRenewalResponse3, + kKeyReleaseServerUrl3)); + size = sizeof(DeviceFiles::LicenseState) + kPsshData3.size() + + kKeyRequest3.size() + kKeyResponse3.size() + + kKeyRenewalRequest3.size() + kKeyRenewalResponse3.size() + + kKeyReleaseServerUrl3.size(); + EXPECT_TRUE(File::Exists(license_path_3)); + EXPECT_GT(File::FileSize(license_path_3), static_cast(size)); +} + +TEST(DeviceFilesTest, RetrieveLicenses) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string license_path_1 = device_base_path + kKeySetId1 + + DeviceFiles::kLicenseFileNameExt; + std::string license_path_2 = device_base_path + kKeySetId2 + + DeviceFiles::kLicenseFileNameExt; + std::string license_path_3 = device_base_path + kKeySetId3 + + DeviceFiles::kLicenseFileNameExt; + + if (!File::Exists(device_base_path)) + EXPECT_TRUE(File::Remove(device_base_path)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, + DeviceFiles::kLicenseStateActive, + kPsshData1, kKeyRequest1, + kKeyResponse1, kKeyRenewalRequest1, + kKeyRenewalResponse1, + kKeyReleaseServerUrl1)); + size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + + kKeyRequest1.size() + kKeyResponse1.size() + + kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + + kKeyReleaseServerUrl1.size(); + EXPECT_TRUE(File::Exists(license_path_1)); + EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId2, + DeviceFiles::kLicenseStateReleasing, + kPsshData2, kKeyRequest2, + kKeyResponse2, kKeyRenewalRequest2, + kKeyRenewalResponse2, + kKeyReleaseServerUrl2)); + size = sizeof(DeviceFiles::LicenseState) + kPsshData2.size() + + kKeyRequest2.size() + kKeyResponse2.size() + + kKeyRenewalRequest2.size() + kKeyRenewalResponse2.size() + + kKeyReleaseServerUrl2.size(); + EXPECT_TRUE(File::Exists(license_path_2)); + EXPECT_GT(File::FileSize(license_path_2), static_cast(size)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId3, + DeviceFiles::kLicenseStateReleasing, + kPsshData3, kKeyRequest3, + kKeyResponse3, kKeyRenewalRequest3, + kKeyRenewalResponse3, + kKeyReleaseServerUrl3)); + size = sizeof(DeviceFiles::LicenseState) + kPsshData3.size() + + kKeyRequest3.size() + kKeyResponse3.size() + + kKeyRenewalRequest3.size() + kKeyRenewalResponse3.size() + + kKeyReleaseServerUrl3.size(); + EXPECT_TRUE(File::Exists(license_path_3)); + EXPECT_GT(File::FileSize(license_path_3), static_cast(size)); + + DeviceFiles::LicenseState state; + std::string pssh_data, key_request, key_response, key_renewal_request, + key_renewal_response, release_server_url; + + EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, + &key_request, &key_response, + &key_renewal_request, + &key_renewal_response, + &release_server_url)); + EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive); + EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) + == 0); + EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), + key_request.size()) == 0); + EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), + key_response.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), + key_renewal_request.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), + key_renewal_response.size()) == 0); + + EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId2, &state, &pssh_data, + &key_request, &key_response, + &key_renewal_request, + &key_renewal_response, + &release_server_url)); + EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing); + EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData2.data(), pssh_data.size()) + == 0); + EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest2.data(), + key_request.size()) == 0); + EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse2.data(), + key_response.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest2.data(), + key_renewal_request.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse2.data(), + key_renewal_response.size()) == 0); + + EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId3, &state, &pssh_data, + &key_request, &key_response, + &key_renewal_request, + &key_renewal_response, + &release_server_url)); + EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing); + EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData3.data(), pssh_data.size()) + == 0); + EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest3.data(), + key_request.size()) == 0); + EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse3.data(), + key_response.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest3.data(), + key_renewal_request.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse3.data(), + key_renewal_response.size()) == 0); + +} + +TEST(DeviceFilesTest, UpdateLicenseState) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string license_path_1 = device_base_path + kKeySetId1 + + DeviceFiles::kLicenseFileNameExt; + + if (!File::Exists(device_base_path)) + EXPECT_TRUE(File::Remove(device_base_path)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, + DeviceFiles::kLicenseStateActive, + kPsshData1, kKeyRequest1, + kKeyResponse1, kKeyRenewalRequest1, + kKeyRenewalResponse1, + kKeyReleaseServerUrl1)); + size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + + kKeyRequest1.size() + kKeyResponse1.size() + + kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + + kKeyReleaseServerUrl1.size(); + EXPECT_TRUE(File::Exists(license_path_1)); + EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + + DeviceFiles::LicenseState state; + std::string pssh_data, key_request, key_response, key_renewal_request, + key_renewal_response, release_server_url; + + EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, + &key_request, &key_response, + &key_renewal_request, + &key_renewal_response, + &release_server_url)); + EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive); + EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) + == 0); + EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), + key_request.size()) == 0); + EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), + key_response.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), + key_renewal_request.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), + key_renewal_response.size()) == 0); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, + DeviceFiles::kLicenseStateReleasing, + kPsshData1, kKeyRequest1, + kKeyResponse1, kKeyRenewalRequest1, + kKeyRenewalResponse1, + kKeyReleaseServerUrl1)); + size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + + kKeyRequest1.size() + kKeyResponse1.size() + + kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + + kKeyReleaseServerUrl1.size(); + EXPECT_TRUE(File::Exists(license_path_1)); + EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + + EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, + &key_request, &key_response, + &key_renewal_request, + &key_renewal_response, + &release_server_url)); + EXPECT_TRUE(state == DeviceFiles::kLicenseStateReleasing); + EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) + == 0); + EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), + key_request.size()) == 0); + EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), + key_response.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), + key_renewal_request.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), + key_renewal_response.size()) == 0); +} + +TEST(DeviceFilesTest, DeleteLicense) { + std::string device_base_path = + DeviceFiles::GetBasePath(DeviceFiles::kBasePath); + std::string license_path_1 = device_base_path + kKeySetId1 + + DeviceFiles::kLicenseFileNameExt; + + if (!File::Exists(device_base_path)) + EXPECT_TRUE(File::Remove(device_base_path)); + + EXPECT_TRUE(DeviceFiles::StoreLicense(kKeySetId1, + DeviceFiles::kLicenseStateActive, + kPsshData1, kKeyRequest1, + kKeyResponse1, kKeyRenewalRequest1, + kKeyRenewalResponse1, + kKeyReleaseServerUrl1)); + size_t size = sizeof(DeviceFiles::LicenseState) + kPsshData1.size() + + kKeyRequest1.size() + kKeyResponse1.size() + + kKeyRenewalRequest1.size() + kKeyRenewalResponse1.size() + + kKeyReleaseServerUrl1.size(); + EXPECT_TRUE(File::Exists(license_path_1)); + EXPECT_GT(File::FileSize(license_path_1), static_cast(size)); + + DeviceFiles::LicenseState state; + std::string pssh_data, key_request, key_response, key_renewal_request, + key_renewal_response, release_server_url; + + EXPECT_TRUE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, + &key_request, &key_response, + &key_renewal_request, + &key_renewal_response, + &release_server_url)); + EXPECT_TRUE(state == DeviceFiles::kLicenseStateActive); + EXPECT_TRUE(memcmp(pssh_data.data(), kPsshData1.data(), pssh_data.size()) + == 0); + EXPECT_TRUE(memcmp(key_request.data(), kKeyRequest1.data(), + key_request.size()) == 0); + EXPECT_TRUE(memcmp(key_response.data(), kKeyResponse1.data(), + key_response.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_request.data(), kKeyRenewalRequest1.data(), + key_renewal_request.size()) == 0); + EXPECT_TRUE(memcmp(key_renewal_response.data(), kKeyRenewalResponse1.data(), + key_renewal_response.size()) == 0); + + EXPECT_TRUE(DeviceFiles::DeleteLicense(kKeySetId1)); + + EXPECT_FALSE(DeviceFiles::RetrieveLicense(kKeySetId1, &state, &pssh_data, + &key_request, &key_response, + &key_renewal_request, + &key_renewal_response, + &release_server_url)); + EXPECT_TRUE(File::Remove(device_base_path)); +} + } diff --git a/libwvdrmengine/cdm/core/test/license_request.cpp b/libwvdrmengine/cdm/core/test/license_request.cpp index 45322c29..84fd4821 100644 --- a/libwvdrmengine/cdm/core/test/license_request.cpp +++ b/libwvdrmengine/cdm/core/test/license_request.cpp @@ -44,6 +44,10 @@ void LicenseRequest::GetDrmMessage(const std::string& response, if (drm_msg_pos != std::string::npos) { drm_msg = response.substr(drm_msg_pos); } else { + // TODO(edwinwong, rfrias): hack to get HTTP message body out for + // non-Google Play webservers. Need to clean this up. Possibly test + // for GLS and decide which part is the drm message + drm_msg = response.substr(header_end_pos); LOGE("drm msg not found"); } } else { diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index 3512dbd3..618c2ccf 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -2,6 +2,8 @@ #include "url_request.h" +#include + #include "http_socket.h" #include "log.h" #include "string_conversions.h" @@ -79,7 +81,7 @@ int UrlRequest::GetStatusCode(const std::string& response) { return status_code; } -bool UrlRequest::PostRequest(const std::string& data) { +bool UrlRequest::PostRequestChunk(const std::string& data) { request_.assign("POST /"); request_.append(socket_.resource_path()); request_.append(" HTTP/1.1\r\n"); @@ -103,6 +105,31 @@ bool UrlRequest::PostRequest(const std::string& data) { return true; } +bool UrlRequest::PostRequest(const std::string& data) { + request_.assign("POST /"); + request_.append(socket_.resource_path()); + request_.append(" HTTP/1.1\r\n"); + request_.append("Host: "); + request_.append(socket_.domain_name()); + request_.append("\r\nConnection: Keep-Alive\r\n"); + request_.append("User-Agent: Widevine CDM v1.0\r\n"); + request_.append("Accept-Encoding: gzip,deflate\r\n"); + request_.append("Accept-Language: en-us,fr\r\n"); + request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n"); + std::ostringstream ss; + ss << data.size(); + request_.append("Content-Length: "); + request_.append(ss.str()); + request_.append("\r\n\r\n"); + request_.append(data); + + // terminates with \r\n, then ends with an empty line + request_.append("\r\n\r\n"); + + socket_.Write(request_.c_str(), request_.size()); + return true; +} + bool UrlRequest::PostCertRequestInQueryString(const std::string& data) { request_.assign("POST /"); request_.append(socket_.resource_path()); diff --git a/libwvdrmengine/cdm/core/test/url_request.h b/libwvdrmengine/cdm/core/test/url_request.h index 0d9d4a1e..668a55fc 100644 --- a/libwvdrmengine/cdm/core/test/url_request.h +++ b/libwvdrmengine/cdm/core/test/url_request.h @@ -21,6 +21,7 @@ class UrlRequest { int GetStatusCode(const std::string& response); bool is_connected() const { return is_connected_; } bool PostRequest(const std::string& data); + bool PostRequestChunk(const std::string& data); bool PostCertRequestInQueryString(const std::string& data); private: diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 9217dd46..f6a8d9ac 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -24,6 +24,7 @@ class WvContentDecryptionModule { // Construct a valid license request. virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id, const CdmInitData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, @@ -32,7 +33,12 @@ class WvContentDecryptionModule { // Accept license response and extract key info. virtual CdmResponseType AddKey(const CdmSessionId& session_id, - const CdmKeyResponse& key_data); + const CdmKeyResponse& key_data, + CdmKeySetId& key_set_id); + + // Setup keys for offline usage which were retrived in an earlier key request + virtual CdmResponseType RestoreKey(const CdmSessionId& session_id, + const CdmKeySetId& key_set_id); // Cancel session virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 5171f3ca..7409b61f 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -31,28 +31,48 @@ CdmResponseType WvContentDecryptionModule::CloseSession( CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( const CdmSessionId& session_id, + const CdmKeySetId& key_set_id, const CdmInitData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, std::string* server_url) { - CdmKeySystem key_system; - return cdm_engine_->GenerateKeyRequest(session_id, false, key_system, - init_data, license_type, - app_parameters, key_request, - server_url); + CdmResponseType sts; + if (license_type == kLicenseTypeRelease) { + sts = cdm_engine_->OpenKeySetSession(key_set_id); + if (sts != NO_ERROR) + return sts; + } + sts = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, + init_data, license_type, + app_parameters, key_request, + server_url); + + if ((license_type == kLicenseTypeRelease) && (sts != KEY_MESSAGE)) { + cdm_engine_->CloseKeySetSession(key_set_id); + } + return sts; } CdmResponseType WvContentDecryptionModule::AddKey( const CdmSessionId& session_id, - const CdmKeyResponse& key_data) { - return cdm_engine_->AddKey(session_id, key_data); + const CdmKeyResponse& key_data, + CdmKeySetId& key_set_id) { + CdmResponseType sts = cdm_engine_->AddKey(session_id, key_data, key_set_id); + if ((sts == KEY_ADDED) && session_id.empty()) // license type release + cdm_engine_->CloseKeySetSession(key_set_id); + return sts; +} + +CdmResponseType WvContentDecryptionModule::RestoreKey( + const CdmSessionId& session_id, + const CdmKeySetId& key_set_id) { + return cdm_engine_->RestoreKey(session_id, key_set_id); } CdmResponseType WvContentDecryptionModule::CancelKeyRequest( const CdmSessionId& session_id) { - CdmKeySystem key_system; - return cdm_engine_->CancelKeyRequest(session_id, false, key_system); + return cdm_engine_->CancelKeyRequest(session_id); } CdmResponseType WvContentDecryptionModule::QueryStatus( diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 02b15a90..84963354 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -36,6 +36,16 @@ const std::string kProductionProvisioningServerUrl = "https://www.googleapis.com/" "certificateprovisioning/v1/devicecertificates/create" "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; + +// TODO(edwinwong, rfrias): refactor to set these parameters though config +std::string kServerSdkClientAuth = ""; +wvcdm::KeyId kServerSdkKeyId = wvcdm::a2bs_hex( + "000000347073736800000000" + "edef8ba979d64acea3c827dcd51d21ed00000014" + "0801121030313233343536373839414243444546"); +std::string kServerSdkLicenseServer = "http://kir03fcpg174.widevine.net/" + "widevine/cgi-bin/drm.cgi"; + } // namespace namespace wvcdm { @@ -57,34 +67,45 @@ class WvCdmRequestLicenseTest : public testing::Test { protected: void GenerateKeyRequest(const std::string& key_system, - const std::string& init_data) { + const std::string& init_data, + CdmLicenseType license_type) { wvcdm::CdmAppParameterMap app_parameters; std::string server_url; - EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, - init_data, - kLicenseTypeStreaming, - app_parameters, - &key_msg_, - &server_url), wvcdm::KEY_MESSAGE); + std::string key_set_id; + EXPECT_EQ(wvcdm::KEY_MESSAGE, + decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data, + license_type, app_parameters, + &key_msg_, &server_url)); EXPECT_EQ(0, static_cast(server_url.size())); } void GenerateRenewalRequest(const std::string& key_system, - const std::string& not_used) { + CdmLicenseType license_type) { // TODO application makes a license request, CDM will renew the license // when appropriate. std::string init_data; wvcdm::CdmAppParameterMap app_parameters; std::string server_url; - EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, - init_data, - kLicenseTypeStreaming, - app_parameters, - &key_msg_, - &server_url), wvcdm::KEY_MESSAGE); + std::string key_set_id; + EXPECT_EQ(wvcdm::KEY_MESSAGE, + decryptor_.GenerateKeyRequest(session_id_, key_set_id, init_data, + license_type, app_parameters, + &key_msg_, &server_url)); EXPECT_NE(0, static_cast(server_url.size())); } + void GenerateKeyRelease(CdmKeySetId key_set_id) { + CdmSessionId session_id; + CdmInitData init_data; + wvcdm::CdmAppParameterMap app_parameters; + std::string server_url; + EXPECT_EQ(wvcdm::KEY_MESSAGE, + decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data, + kLicenseTypeRelease, app_parameters, + &key_msg_, &server_url)); + EXPECT_EQ(0, static_cast(server_url.size())); + } + void DumpResponse(const std::string& description, const std::string& response) { if (description.empty()) @@ -176,7 +197,13 @@ class WvCdmRequestLicenseTest : public testing::Test { return ""; } - url_request.PostRequest(key_msg_); + // TODO(edwinwong, rfrias): need a cleaner solution to handle + // HTTP servers that use chunking vs those that do not + if (server_url.compare(kServerSdkLicenseServer) == 0) + url_request.PostRequest(key_msg_); + else + url_request.PostRequestChunk(key_msg_); + std::string http_response; std::string message_body; int resp_bytes = url_request.GetResponse(http_response); @@ -248,20 +275,22 @@ class WvCdmRequestLicenseTest : public testing::Test { std::string resp = GetKeyRequestResponse(server_url, client_auth, 200); - if (is_renewal) { // TODO application makes a license request, CDM will renew the license // when appropriate - EXPECT_EQ(decryptor_.AddKey(session_id_, resp), wvcdm::KEY_ADDED); + EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_), + wvcdm::KEY_ADDED); } else { - EXPECT_EQ(decryptor_.AddKey(session_id_, resp), wvcdm::KEY_ADDED); + EXPECT_EQ(decryptor_.AddKey(session_id_, resp, key_set_id_), + wvcdm::KEY_ADDED); } } wvcdm::WvContentDecryptionModule decryptor_; - std::string key_msg_; - std::string session_id_; + CdmKeyMessage key_msg_; + CdmSessionId session_id_; + CdmKeySetId key_set_id_; }; TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { @@ -304,7 +333,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) { TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); GetKeyRequestResponse(g_license_server, g_client_auth, 200); decryptor_.CloseSession(session_id_); } @@ -313,31 +342,76 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) { decryptor_.OpenSession(g_key_system, &session_id_); std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); - GenerateKeyRequest(g_key_system, wrong_message); + GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming); GetKeyRequestResponse(g_license_server, g_client_auth, 500); decryptor_.CloseSession(session_id_); } -TEST_F(WvCdmRequestLicenseTest, AddKeyTest) { +TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + decryptor_.OpenSession(g_key_system, &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + key_set_id_.clear(); + decryptor_.OpenSession(g_key_system, &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + key_set_id_.clear(); + GenerateKeyRelease(key_set_id); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); +} + TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); - GenerateRenewalRequest(g_key_system, g_key_id); + GenerateRenewalRequest(g_key_system, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true); decryptor_.CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); CdmQueryMap query_info; @@ -405,7 +479,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) { TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); CdmQueryMap query_info; @@ -425,7 +499,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { TEST_F(WvCdmRequestLicenseTest, ClearDecryptionTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); // key 1, clear, 256b @@ -525,7 +599,7 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionNoKeyTest) { TEST_F(WvCdmRequestLicenseTest, DecryptionTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); // key 1, encrypted, 256b @@ -576,7 +650,7 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionTest) { TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); uint8_t data_blocks = 2; @@ -657,7 +731,7 @@ TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) { TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); // key 3, encrypted, 125b, offset 0 @@ -700,7 +774,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) { TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); // key 3, encrypted, 123b, offset 5 @@ -746,7 +820,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) { /* TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) { decryptor_.OpenSession(g_key_system, &session_id_); - GenerateKeyRequest(g_key_system, g_key_id); + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); DecryptionData data; diff --git a/libwvdrmengine/level3/arm/libwvlevel3.a b/libwvdrmengine/level3/arm/libwvlevel3.a index 05a9e458..b491a3c7 100644 Binary files a/libwvdrmengine/level3/arm/libwvlevel3.a and b/libwvdrmengine/level3/arm/libwvlevel3.a differ diff --git a/libwvdrmengine/level3/mips/libwvlevel3.a b/libwvdrmengine/level3/mips/libwvlevel3.a index 0a53ada1..d1daeb6c 100644 Binary files a/libwvdrmengine/level3/mips/libwvlevel3.a and b/libwvdrmengine/level3/mips/libwvlevel3.a differ diff --git a/libwvdrmengine/level3/x86/libwvlevel3.a b/libwvdrmengine/level3/x86/libwvlevel3.a index b02109dc..28c74310 100644 Binary files a/libwvdrmengine/level3/x86/libwvlevel3.a and b/libwvdrmengine/level3/x86/libwvlevel3.a differ diff --git a/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp index e4f6c94d..a2878641 100644 --- a/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp +++ b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp @@ -47,7 +47,7 @@ class WVCryptoPluginTest : public Test { }; TEST_F(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) { - MockCDM cdm; + StrictMock cdm; WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); CdmQueryMap l1Map; @@ -71,7 +71,7 @@ TEST_F(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) { } TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { - MockCDM cdm; + StrictMock cdm; WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); uint8_t keyId[KEY_ID_SIZE]; @@ -182,7 +182,7 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { } TEST_F(WVCryptoPluginTest, CommunicatesSecureBufferRequest) { - MockCDM cdm; + StrictMock cdm; WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); uint8_t keyId[KEY_ID_SIZE]; diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index 0dc0dc7b..060ff918 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -50,7 +50,7 @@ class WVDrmPlugin : public android::DrmPlugin, virtual status_t closeSession(const Vector& sessionId); virtual status_t getKeyRequest( - const Vector& sessionId, + const Vector& scope, const Vector& initData, const String8& mimeType, KeyType keyType, @@ -58,11 +58,11 @@ class WVDrmPlugin : public android::DrmPlugin, Vector& request, String8& defaultUrl); - virtual status_t provideKeyResponse(const Vector& sessionId, + virtual status_t provideKeyResponse(const Vector& scope, const Vector& response, Vector& keySetId); - virtual status_t removeKeys(const Vector& keySetId); + virtual status_t removeKeys(const Vector& sessionId); virtual status_t restoreKeys(const Vector& sessionId, const Vector& keySetId); diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 1329a547..a7fc09a7 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -9,6 +9,7 @@ #include "WVDrmPlugin.h" #include +#include #include #include #include @@ -115,7 +116,7 @@ status_t WVDrmPlugin::closeSession(const Vector& sessionId) { } status_t WVDrmPlugin::getKeyRequest( - const Vector& sessionId, + const Vector& scope, const Vector& initData, const String8& mimeType, KeyType keyType, @@ -123,14 +124,20 @@ status_t WVDrmPlugin::getKeyRequest( Vector& request, String8& defaultUrl) { CdmLicenseType cdmLicenseType; + CdmSessionId cdmSessionId; + CdmKeySetId cdmKeySetId; if (keyType == kKeyType_Offline) { cdmLicenseType = kLicenseTypeOffline; + cdmSessionId.assign(scope.begin(), scope.end()); } else if (keyType == kKeyType_Streaming) { cdmLicenseType = kLicenseTypeStreaming; + cdmSessionId.assign(scope.begin(), scope.end()); + } else if (keyType == kKeyType_Release) { + cdmLicenseType = kLicenseTypeRelease; + cdmKeySetId.assign(scope.begin(), scope.end()); } else { return android::ERROR_DRM_CANNOT_HANDLE; } - CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); // Build PSSH box for PSSH data in initData. static const char psshPrefix[] = { @@ -163,8 +170,8 @@ status_t WVDrmPlugin::getKeyRequest( CdmKeyMessage keyRequest; string cdmDefaultUrl; - CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, psshBox, - cdmLicenseType, + CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, cdmKeySetId, + psshBox, cdmLicenseType, cdmParameters, &keyRequest, &cdmDefaultUrl); @@ -177,31 +184,69 @@ status_t WVDrmPlugin::getKeyRequest( keyRequest.size()); } - return mapAndNotifyOfCdmResponseType(sessionId, res); + if (keyType == kKeyType_Release) { + // When releasing keys, we do not have a session ID. + return mapCdmResponseType(res); + } else { + // For all other requests, we have a session ID. + return mapAndNotifyOfCdmResponseType(scope, res); + } } status_t WVDrmPlugin::provideKeyResponse( - const Vector& sessionId, + const Vector& scope, const Vector& response, Vector& keySetId) { - // TODO: return keySetId for persisted offline content - CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + CdmSessionId cdmSessionId; CdmKeyResponse cdmResponse(response.begin(), response.end()); + CdmKeySetId cdmKeySetId; - CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse); + bool isRequest = (memcmp(scope.array(), SESSION_ID_PREFIX.data(), + SESSION_ID_PREFIX.size()) == 0); + bool isRelease = (memcmp(scope.array(), KEY_SET_ID_PREFIX.data(), + KEY_SET_ID_PREFIX.size()) == 0); - return mapAndNotifyOfCdmResponseType(sessionId, res); + if (isRequest) { + cdmSessionId.assign(scope.begin(), scope.end()); + } else if (isRelease) { + cdmKeySetId.assign(scope.begin(), scope.end()); + } else { + return android::ERROR_DRM_CANNOT_HANDLE; + } + + CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, cdmKeySetId); + + if (isRequest && isCdmResponseTypeSuccess(res)) { + keySetId.clear(); + keySetId.appendArray(reinterpret_cast(cdmKeySetId.data()), + cdmKeySetId.size()); + } + + if (isRelease) { + // When releasing keys, we do not have a session ID. + return mapCdmResponseType(res); + } else { + // For all other requests, we have a session ID. + return mapAndNotifyOfCdmResponseType(scope, res); + } } -status_t WVDrmPlugin::removeKeys(const Vector& keySetId) { - // TODO: remove persisted offline keys associated with keySetId - return android::ERROR_UNSUPPORTED; +status_t WVDrmPlugin::removeKeys(const Vector& sessionId) { + CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + + CdmResponseType res = mCDM->CancelKeyRequest(cdmSessionId); + + return mapAndNotifyOfCdmResponseType(sessionId, res); } status_t WVDrmPlugin::restoreKeys(const Vector& sessionId, const Vector& keySetId) { - // TODO: restore persisted offline keys associated with keySetId - return android::ERROR_UNSUPPORTED; + CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + CdmKeySetId cdmKeySetId(keySetId.begin(), keySetId.end()); + + CdmResponseType res = mCDM->RestoreKey(cdmSessionId, cdmKeySetId); + + return mapAndNotifyOfCdmResponseType(sessionId, res); } status_t WVDrmPlugin::queryKeyStatus( diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 6a21ba7d..8bd09c5a 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -3,6 +3,7 @@ // #include +#include #include #include "gmock/gmock.h" @@ -27,17 +28,22 @@ class MockCDM : public WvContentDecryptionModule { MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&)); - MOCK_METHOD6(GenerateKeyRequest, CdmResponseType(const CdmSessionId&, + MOCK_METHOD7(GenerateKeyRequest, CdmResponseType(const CdmSessionId&, + const CdmKeySetId&, const CdmInitData&, const CdmLicenseType, CdmAppParameterMap&, CdmKeyMessage*, string*)); - MOCK_METHOD2(AddKey, CdmResponseType(const CdmSessionId&, - const CdmKeyResponse&)); + MOCK_METHOD3(AddKey, CdmResponseType(const CdmSessionId&, + const CdmKeyResponse&, + CdmKeySetId&)); MOCK_METHOD1(CancelKeyRequest, CdmResponseType(const CdmSessionId&)); + MOCK_METHOD2(RestoreKey, CdmResponseType(const CdmSessionId&, + const CdmKeySetId&)); + MOCK_METHOD1(QueryStatus, CdmResponseType(CdmQueryMap*)); MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&, @@ -111,6 +117,7 @@ class WVDrmPluginTest : public Test { fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp); fclose(fp); + memcpy(sessionIdRaw, SESSION_ID_PREFIX.data(), SESSION_ID_PREFIX.size()); sessionId.appendArray(sessionIdRaw, kSessionIdSize); cdmSessionId.assign(sessionId.begin(), sessionId.end()); @@ -122,8 +129,8 @@ class WVDrmPluginTest : public Test { }; TEST_F(WVDrmPluginTest, OpensSessions) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) @@ -149,8 +156,8 @@ TEST_F(WVDrmPluginTest, OpensSessions) { } TEST_F(WVDrmPluginTest, ClosesSessions) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); EXPECT_CALL(cdm, CloseSession(cdmSessionId)) @@ -162,19 +169,27 @@ TEST_F(WVDrmPluginTest, ClosesSessions) { } TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const size_t kInitDataSize = 128; uint8_t initDataRaw[kInitDataSize]; static const size_t kRequestSize = 256; uint8_t requestRaw[kRequestSize]; + static const uint32_t kKeySetIdSize = 32; + uint8_t keySetIdRaw[kKeySetIdSize]; FILE* fp = fopen("/dev/urandom", "r"); fread(initDataRaw, sizeof(uint8_t), kInitDataSize, fp); fread(requestRaw, sizeof(uint8_t), kRequestSize, fp); + fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp); fclose(fp); + memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size()); + CdmKeySetId cdmKeySetId(reinterpret_cast(keySetIdRaw), kKeySetIdSize); + Vector keySetId; + keySetId.appendArray(keySetIdRaw, kKeySetIdSize); + Vector initData; initData.appendArray(initDataRaw, kInitDataSize); @@ -209,20 +224,28 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { { InSequence calls; - EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, + EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", ElementsAreArray(psshBox, kPsshBoxSize), kLicenseTypeOffline, cdmParameters, _, _)) - .WillOnce(DoAll(SetArgPointee<4>(cdmRequest), - SetArgPointee<5>(kDefaultUrl), + .WillOnce(DoAll(SetArgPointee<5>(cdmRequest), + SetArgPointee<6>(kDefaultUrl), Return(wvcdm::KEY_MESSAGE))); - EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, + EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, "", ElementsAreArray(psshBox, kPsshBoxSize), kLicenseTypeStreaming, cdmParameters, _, _)) - .WillOnce(DoAll(SetArgPointee<4>(cdmRequest), - SetArgPointee<5>(kDefaultUrl), + .WillOnce(DoAll(SetArgPointee<5>(cdmRequest), + SetArgPointee<6>(kDefaultUrl), + Return(wvcdm::KEY_MESSAGE))); + + EXPECT_CALL(cdm, GenerateKeyRequest("", cdmKeySetId, + ElementsAreArray(psshBox, kPsshBoxSize), + kLicenseTypeRelease, cdmParameters, _, + _)) + .WillOnce(DoAll(SetArgPointee<5>(cdmRequest), + SetArgPointee<6>(kDefaultUrl), Return(wvcdm::KEY_MESSAGE))); } @@ -233,7 +256,6 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { String8("video/h264"), DrmPlugin::kKeyType_Offline, parameters, request, defaultUrl); - ASSERT_EQ(OK, res); EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); EXPECT_STREQ(kDefaultUrl, defaultUrl.string()); @@ -241,44 +263,96 @@ TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { res = plugin.getKeyRequest(sessionId, initData, String8("video/h264"), DrmPlugin::kKeyType_Streaming, parameters, request, defaultUrl); + ASSERT_EQ(OK, res); + EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); + EXPECT_STREQ(kDefaultUrl, defaultUrl.string()); + res = plugin.getKeyRequest(keySetId, initData, String8("video/h264"), + DrmPlugin::kKeyType_Release, parameters, + request, defaultUrl); ASSERT_EQ(OK, res); EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); EXPECT_STREQ(kDefaultUrl, defaultUrl.string()); } TEST_F(WVDrmPluginTest, AddsKeys) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const uint32_t kResponseSize = 256; uint8_t responseRaw[kResponseSize]; + static const uint32_t kKeySetIdSize = 32; + uint8_t keySetIdRaw[kKeySetIdSize]; FILE* fp = fopen("/dev/urandom", "r"); fread(responseRaw, sizeof(uint8_t), kResponseSize, fp); + fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp); fclose(fp); Vector response; response.appendArray(responseRaw, kResponseSize); - // TODO: Do something with the key set ID. - Vector ignoredKeySetId; + memcpy(keySetIdRaw, KEY_SET_ID_PREFIX.data(), KEY_SET_ID_PREFIX.size()); + CdmKeySetId cdmKeySetId(reinterpret_cast(keySetIdRaw), kKeySetIdSize); + Vector keySetId; - EXPECT_CALL(cdm, AddKey(cdmSessionId, ElementsAreArray(responseRaw, - kResponseSize))) - .WillOnce(Return(wvcdm::KEY_ADDED)); + Vector emptyKeySetId; - status_t res = plugin.provideKeyResponse(sessionId, response, - ignoredKeySetId); + EXPECT_CALL(cdm, AddKey(cdmSessionId, + ElementsAreArray(responseRaw, kResponseSize), _)) + .WillOnce(DoAll(SetArgReferee<2>(cdmKeySetId), + Return(wvcdm::KEY_ADDED))); + EXPECT_CALL(cdm, AddKey("", ElementsAreArray(responseRaw, kResponseSize), + cdmKeySetId)) + .Times(1); + + status_t res = plugin.provideKeyResponse(sessionId, response, keySetId); + ASSERT_EQ(OK, res); + ASSERT_THAT(keySetId, ElementsAreArray(keySetIdRaw, kKeySetIdSize)); + + res = plugin.provideKeyResponse(keySetId, response, emptyKeySetId); + ASSERT_EQ(OK, res); + EXPECT_EQ(0u, emptyKeySetId.size()); +} + +TEST_F(WVDrmPluginTest, CancelsKeyRequests) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + EXPECT_CALL(cdm, CancelKeyRequest(cdmSessionId)) + .Times(1); + + status_t res = plugin.removeKeys(sessionId); ASSERT_EQ(OK, res); } -// TODO: Reinstate removeKeys() test once its behavior is finalized. +TEST_F(WVDrmPluginTest, RestoresKeys) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + static const size_t kKeySetIdSize = 32; + uint8_t keySetIdRaw[kKeySetIdSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp); + fclose(fp); + + Vector keySetId; + keySetId.appendArray(keySetIdRaw, kKeySetIdSize); + + EXPECT_CALL(cdm, RestoreKey(cdmSessionId, + ElementsAreArray(keySetIdRaw, kKeySetIdSize))) + .Times(1); + + status_t res = plugin.restoreKeys(sessionId, keySetId); + ASSERT_EQ(OK, res); +} TEST_F(WVDrmPluginTest, QueriesKeyStatus) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); KeyedVector expectedLicenseStatus; @@ -293,7 +367,7 @@ TEST_F(WVDrmPluginTest, QueriesKeyStatus) { EXPECT_CALL(cdm, QueryKeyStatus(cdmSessionId, _)) .WillOnce(DoAll(SetArgPointee<1>(cdmLicenseStatus), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); KeyedVector licenseStatus; @@ -310,8 +384,8 @@ TEST_F(WVDrmPluginTest, QueriesKeyStatus) { } TEST_F(WVDrmPluginTest, GetsProvisioningRequests) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const uint32_t kRequestSize = 256; @@ -340,8 +414,8 @@ TEST_F(WVDrmPluginTest, GetsProvisioningRequests) { } TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const uint32_t kResponseSize = 512; @@ -363,8 +437,8 @@ TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) { } TEST_F(WVDrmPluginTest, GetsSecureStops) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const uint32_t kStopSize = 53; @@ -405,8 +479,8 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) { } TEST_F(WVDrmPluginTest, ReleasesSecureStops) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const uint32_t kMessageSize = 128; @@ -428,8 +502,8 @@ TEST_F(WVDrmPluginTest, ReleasesSecureStops) { } TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); CdmQueryMap l1Map; @@ -493,8 +567,8 @@ TEST_F(WVDrmPluginTest, ReturnsExpectedPropertyValues) { } TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); String8 stringResult; @@ -512,8 +586,8 @@ TEST_F(WVDrmPluginTest, DoesNotGetUnknownProperties) { } TEST_F(WVDrmPluginTest, DoesNotSetProperties) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const uint32_t kValueSize = 32; @@ -534,8 +608,8 @@ TEST_F(WVDrmPluginTest, DoesNotSetProperties) { } TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); Vector keyId; @@ -548,7 +622,7 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) { EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) .Times(AtLeast(1)) @@ -591,8 +665,8 @@ MATCHER_P(IsIV, iv, "") { } TEST_F(WVDrmPluginTest, CallsGenericEncrypt) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const size_t kDataSize = 256; @@ -631,7 +705,7 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) { EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) .Times(AtLeast(1)) @@ -655,8 +729,8 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) { } TEST_F(WVDrmPluginTest, CallsGenericDecrypt) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const size_t kDataSize = 256; @@ -695,7 +769,7 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) { EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) .Times(AtLeast(1)) @@ -719,8 +793,8 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) { } TEST_F(WVDrmPluginTest, CallsGenericSign) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const size_t kDataSize = 256; @@ -761,7 +835,7 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) { EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) .Times(AtLeast(1)) @@ -785,8 +859,8 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) { } TEST_F(WVDrmPluginTest, CallsGenericVerify) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); static const size_t kDataSize = 256; @@ -837,7 +911,7 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) { EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) .Times(AtLeast(1)) @@ -866,8 +940,8 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) { } TEST_F(WVDrmPluginTest, RegistersForEvents) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); EXPECT_CALL(cdm, AttachEventListener(cdmSessionId, &plugin)) @@ -877,7 +951,7 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) { EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) .Times(AtLeast(1)) @@ -892,8 +966,8 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) { } TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; { WVDrmPlugin plugin(&cdm, &crypto); @@ -939,8 +1013,8 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) { } TEST_F(WVDrmPluginTest, MarshalsEvents) { - MockCDM cdm; - MockCrypto crypto; + StrictMock cdm; + StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); sp listener = new MockDrmPluginListener(); @@ -965,7 +1039,7 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) { EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) .Times(AtLeast(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId), - Return(wvcdm::NO_ERROR))); + Return(wvcdm::NO_ERROR))); EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) .Times(AtLeast(1)) diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 6b7885f0..a3d70e51 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -323,8 +323,8 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message, } bool SessionContext::ParseKeyControl( - const std::vector& key_control_string, - KeyControlBlock& key_control_block) { + const std::vector& key_control_string, + KeyControlBlock& key_control_block) { key_control_block.Invalidate(); @@ -337,15 +337,6 @@ bool SessionContext::ParseKeyControl( return false; } - if (!key_control_block.Validate()) { - LOGE("KCB: BAD Signature"); - return false; - } - if (!CheckNonce(key_control_block.nonce())) { - LOGE("KCB: BAD Nonce"); - return false; - } - LOGD("KCB:"); LOGD(" valid: %d", key_control_block.valid()); LOGD(" duration: %d", key_control_block.duration()); @@ -375,6 +366,16 @@ bool SessionContext::ParseKeyControl( const char* cgms_values[4] = {"free", "BAD", "once", "never"}; LOGD(" CGMS = %s", cgms_values[cgms_bits]); + if (!key_control_block.Validate()) { + LOGE("KCB: BAD Signature"); + return false; + } + if ((key_control_block.control_bits() & kControlNonceEnabled) + && (!CheckNonce(key_control_block.nonce()))) { + LOGE("KCB: BAD Nonce"); + return false; + } + return true; } diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_keybox_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_keybox_test.cpp deleted file mode 100644 index 06d345d3..00000000 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_keybox_test.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. - -// -// OEMCrypto unit tests -// -#include -#include -#include -#include -#include - -#include "OEMCryptoCENC.h" -#include "string_conversions.h" -#include "wv_cdm_constants.h" -#include "wv_keybox.h" - -using namespace std; - -namespace wvoec { - -static wvoec_mock::WidevineKeybox kValidKeybox02 = { - // Sample keybox used for test vectors - { - // deviceID - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02 - 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - }, { - // key - 0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0, - 0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6, - }, { - // data - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, - 0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93, - 0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4, - 0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae, - 0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13, - 0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33, - 0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa, - 0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a, - 0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80, - }, { - // magic - 0x6b, 0x62, 0x6f, 0x78, - }, { - // Crc - 0x2a, 0x3b, 0x3e, 0xe4, - } -}; - -static wvoec_mock::WidevineKeybox kValidKeybox03 = { - // Sample keybox used for test vectors - { - // deviceID - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey03 - 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - }, { - // key - 0x25, 0xe5, 0x2a, 0x02, 0x29, 0x68, 0x04, 0xa2, - 0x92, 0xfd, 0x7c, 0x67, 0x0b, 0x67, 0x1f, 0x31, - }, { - // data - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, - 0xf4, 0x0a, 0x0e, 0xa2, 0x0a, 0x71, 0xd5, 0x92, - 0xfa, 0xa3, 0x25, 0xc6, 0x4b, 0x76, 0xf1, 0x64, - 0xf4, 0x60, 0xa0, 0x30, 0x72, 0x23, 0xbe, 0x03, - 0xcd, 0xde, 0x7a, 0x06, 0xd4, 0x01, 0xeb, 0xdc, - 0xe0, 0x50, 0xc0, 0x53, 0x0a, 0x50, 0xb0, 0x37, - 0xe5, 0x05, 0x25, 0x0e, 0xa4, 0xc8, 0x5a, 0xff, - 0x46, 0x6e, 0xa5, 0x31, 0xf3, 0xdd, 0x94, 0xb7, - 0xe0, 0xd3, 0xf9, 0x04, 0xb2, 0x54, 0xb1, 0x64, - }, { - // magic - 0x6b, 0x62, 0x6f, 0x78, - }, { - // Crc - 0xa1, 0x99, 0x5f, 0x46, - } -}; - -// Define CAN_INSTALL_KEYBOX if you are compiling with the reference -// implementation of OEMCrypto, or if your version of OEMCrypto supports -// OEMCrypto_InstallKeybox and OEMCrypto_WrapKeybox. -#if defined(CAN_INSTALL_KEYBOX) -// The Below tests are based on a specific keybox which is installed for testing. - -class OEMCryptoKeyboxTest : public ::testing::Test { - - protected: - virtual void SetUp() { - } - - void install_keybox(wvoec_mock::WidevineKeybox& keybox, bool good) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()) - << "OEMCrypto_Initialize failed."; - OEMCryptoResult sts; - uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)]; - size_t length = sizeof(wvoec_mock::WidevineKeybox); - sts = OEMCrypto_WrapKeybox(reinterpret_cast(&keybox), - sizeof(keybox), - wrapped, - &length, - NULL, 0); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox)); - if( good ) { - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - } else { - // Can return error now, or return error on IsKeyboxValid. - } - } - - virtual void TearDown() { - OEMCrypto_Terminate(); - } - public: - -}; - -TEST_F(OEMCryptoKeyboxTest, DefaultKeybox) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()) - << "OEMCrypto_Initialize failed."; - OEMCryptoResult sts; - sts = OEMCrypto_IsKeyboxValid(); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); -} - -TEST_F(OEMCryptoKeyboxTest, GoodKeybox) { - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; - OEMCryptoResult sts; - install_keybox(keybox, true); - sts = OEMCrypto_IsKeyboxValid(); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - keybox = kValidKeybox03; - install_keybox(keybox, true); - sts = OEMCrypto_IsKeyboxValid(); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); -} - -TEST_F(OEMCryptoKeyboxTest, BadCRCKeybox) { - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; - keybox.crc_[1] = 42; - OEMCryptoResult sts; - install_keybox(keybox, false); - sts = OEMCrypto_IsKeyboxValid(); - ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); -} - -TEST_F(OEMCryptoKeyboxTest, BadMagicKeybox) { - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; - keybox.magic_[1] = 42; - OEMCryptoResult sts; - install_keybox(keybox, false); - sts = OEMCrypto_IsKeyboxValid(); - ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts); -} - - -TEST_F(OEMCryptoKeyboxTest, BadDataKeybox) { - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; - keybox.data_[1] = 42; - OEMCryptoResult sts; - install_keybox(keybox, false); - sts = OEMCrypto_IsKeyboxValid(); - ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); -} - -#endif // CAN_INSTALL_KEYBOX - -} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 9f6540a1..16463919 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -934,9 +934,9 @@ class Session { enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4"); } - void LoadTestKeys(uint32_t duration, uint32_t control) { + void LoadTestKeys(uint32_t duration, uint32_t control, uint32_t nonce) { MessageData data; - FillSimpleMessage(&data, duration, control); + FillSimpleMessage(&data, duration, control, nonce); MessageData encrypted; EncryptMessage(data, &encrypted); std::vector signature; @@ -1059,7 +1059,8 @@ class Session { } } - void FillSimpleMessage(MessageData* data, uint32_t duration, uint32_t control) { + void FillSimpleMessage(MessageData* data, uint32_t duration, uint32_t control, + uint32_t nonce) { OEMCrypto_GetRandom(data->mac_key_iv, sizeof(data->mac_key_iv)); OEMCrypto_GetRandom(data->mac_keys, sizeof(data->mac_keys)); for (unsigned int i = 0; i < kNumKeys; i++) { @@ -1072,7 +1073,7 @@ class Session { sizeof(data->keys[i].control_iv)); memcpy(data->keys[i].control.verification, "kctl", 4); data->keys[i].control.duration = htonl(duration); - data->keys[i].control.nonce = htonl(nonce_); + data->keys[i].control.nonce = htonl(nonce); data->keys[i].control.control_bits = htonl(control); } // For the canned decryption content, The first key is: @@ -1918,7 +1919,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyNoNonce) { Session& s = createSession("ONE"); s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, 0); + s.LoadTestKeys(kDuration, 0, 42); s.close(); testTearDown(); } @@ -1930,7 +1931,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithNonce) { s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(0, wvoec_mock::kControlNonceEnabled); + s.LoadTestKeys(0, wvoec_mock::kControlNonceEnabled, s.get_nonce()); s.close(); testTearDown(); } @@ -1946,7 +1947,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange1) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -1978,7 +1979,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange2) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2011,7 +2012,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange3) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2045,7 +2046,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange4) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2079,7 +2080,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange5) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2113,7 +2114,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange6) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2147,7 +2148,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange7) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2181,12 +2182,8 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadNonce) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); - data.keys[0].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); - data.keys[1].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); - data.keys[2].control.control_bits = htonl(wvoec_mock::kControlNonceEnabled); - data.keys[1].control.nonce = 42; // This one is bad. - + s.FillSimpleMessage(&data, 0, wvoec_mock::kControlNonceEnabled, + 42); // bad nonce. MessageData encrypted; s.EncryptMessage(data, &encrypted); std::vector signature; @@ -2218,7 +2215,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadVerification) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); data.keys[1].control.verification[2] = 'Z'; MessageData encrypted; @@ -2252,7 +2249,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeysBadSignature) { s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2287,7 +2284,7 @@ TEST_F(DISABLED_TestKeybox, LoadKeysWithNoDerivedKeys) { // s.GenerateDerivedKeys(); MessageData data; - s.FillSimpleMessage(&data, 0, 0); + s.FillSimpleMessage(&data, 0, 0, 0); MessageData encrypted; s.EncryptMessage(data, &encrypted); @@ -2321,11 +2318,10 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox { Session& s = createSession("ONE"); s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled); + s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce()); uint32_t nonce; s.GenerateNonce(&nonce); - s.RefreshTestKeys(key_count, wvoec_mock::kControlNonceEnabled, nonce, - true); + s.RefreshTestKeys(key_count, wvoec_mock::kControlNonceEnabled, nonce, true); s.close(); } @@ -2333,7 +2329,7 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox { Session& s = createSession("ONE"); s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, 0); + s.LoadTestKeys(kDuration, 0, 0); uint32_t nonce; s.GenerateNonce(&nonce); s.RefreshTestKeys(key_count,0, 0, true); @@ -2344,7 +2340,7 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox { Session& s = createSession("ONE"); s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled); + s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce()); uint32_t nonce = s.get_nonce(); s.RefreshTestKeys(key_count, wvoec_mock::kControlNonceEnabled, nonce, false); @@ -2354,7 +2350,7 @@ class DISABLED_RefreshKeyTest : public DISABLED_TestKeybox { Session& s = createSession("ONE"); s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled); + s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce()); uint32_t nonce; s.GenerateNonce(&nonce); nonce = 42; @@ -2395,7 +2391,7 @@ TEST_F(DISABLED_TestKeybox, Decrypt) { s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, 0); + s.LoadTestKeys(kDuration, 0, 0); // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); @@ -2452,7 +2448,7 @@ TEST_F(DISABLED_TestKeybox, DecryptZeroDuration) { s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(0, 0); + s.LoadTestKeys(0, 0, 0); // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); @@ -2509,7 +2505,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) { s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, 0); + s.LoadTestKeys(kDuration, 0, 0); // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); @@ -2568,7 +2564,7 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) { s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, 0); + s.LoadTestKeys(kDuration, 0, 0); // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); @@ -2658,7 +2654,7 @@ TEST_F(DISABLED_TestKeybox, DecryptSecureToClear) { s.open(); s.GenerateDerivedKeys(); s.LoadTestKeys(kDuration, wvoec_mock::kControlObserveDataPath - | wvoec_mock::kControlDataPathSecure); + | wvoec_mock::kControlDataPathSecure, 0); // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); @@ -2714,7 +2710,7 @@ TEST_F(DISABLED_TestKeybox, KeyDuration) { Session& s = createSession("ONE"); s.open(); s.GenerateDerivedKeys(); - s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled); + s.LoadTestKeys(kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce()); // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); @@ -3168,7 +3164,7 @@ TEST_F(DISABLED_TestKeybox, CertificateDecrypt) { s.open(); s.InstallRSASessionTestKey(wrapped_rsa_key); - s.LoadTestKeys(kDuration, 0); + s.LoadTestKeys(kDuration, 0, 0); // Select the key (from FillSimpleMessage) vector keyId = wvcdm::a2b_hex("000000000000000000000000"); @@ -3228,7 +3224,7 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox { void MakeFourKeys(Session* s) { - s->FillSimpleMessage(&message_data_, kDuration, 0); + s->FillSimpleMessage(&message_data_, kDuration, 0, 0); message_data_.keys[0].control.control_bits = htonl(wvoec_mock::kControlAllowEncrypt); message_data_.keys[1].control.control_bits = htonl(wvoec_mock::kControlAllowDecrypt); message_data_.keys[2].control.control_bits = htonl(wvoec_mock::kControlAllowSign);