Source release v2.1.1-0-738 + third_party libs

Change-Id: I76e298f8092951d4214c776d6bbcad6b763eb5b2
This commit is contained in:
Joey Parrish
2014-05-30 16:57:58 -07:00
parent 66794025d4
commit 557c42130a
340 changed files with 278998 additions and 2842 deletions

View File

@@ -4,6 +4,7 @@
#define WVCDM_CORE_CDM_ENGINE_H_
#include "certificate_provisioning.h"
#include "initialization_data.h"
#include "oemcrypto_adapter.h"
#include "wv_cdm_types.h"
@@ -35,7 +36,7 @@ class CdmEngine {
// Construct a valid license request
CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const CdmInitData& init_data,
const InitializationData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
@@ -76,16 +77,29 @@ class CdmEngine {
CdmQueryMap* key_info);
// Provisioning related methods
CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType GetProvisioningRequest(
CdmCertificateType cert_type,
const std::string& cert_authority,
CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
CdmResponseType HandleProvisioningResponse(
CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key);
// Secure stop related methods
CdmResponseType GetSecureStops(CdmSecureStops* secure_stops);
CdmResponseType ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message);
// Decryption and key related methods
// Accept encrypted buffer and return decrypted data.
CdmResponseType Decrypt(const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters);
size_t SessionSize() const { return sessions_.size(); }
// Is the key known to any session?
bool IsKeyLoaded(const KeyId& key_id);
bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
@@ -96,11 +110,6 @@ class CdmEngine {
bool DetachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener);
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// widevine pssh
static bool ExtractWidevinePssh(const CdmInitData& init_data,
CdmInitData* output);
// Timer expiration method
void OnTimerEvent();

View File

@@ -7,6 +7,7 @@
#include "crypto_session.h"
#include "device_files.h"
#include "initialization_data.h"
#include "license.h"
#include "oemcrypto_adapter.h"
#include "policy_engine.h"
@@ -33,7 +34,7 @@ class CdmSession {
const CdmSessionId& session_id() { return session_id_; }
CdmResponseType GenerateKeyRequest(const CdmInitData& init_data,
CdmResponseType GenerateKeyRequest(const InitializationData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
@@ -106,7 +107,7 @@ class CdmSession {
CdmLicenseType license_type_;
// license type offline related information
CdmInitData offline_pssh_data_;
CdmInitData offline_init_data_;
CdmKeyMessage offline_key_request_;
CdmKeyResponse offline_key_response_;
CdmKeyMessage offline_key_renewal_request_;

View File

@@ -13,14 +13,18 @@ class CdmSession;
class CertificateProvisioning {
public:
CertificateProvisioning() {};
CertificateProvisioning() : cert_type_(kCertificateWidevine) {};
~CertificateProvisioning() {};
// Provisioning related methods
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
CdmCertificateType cert_type,
const std::string& cert_authority,
CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key);
private:
void ComposeJsonRequestAsQueryString(const std::string& message,
@@ -30,6 +34,7 @@ class CertificateProvisioning {
const std::string& end_substr,
std::string* result);
CryptoSession crypto_session_;
CdmCertificateType cert_type_;
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
};

View File

@@ -72,8 +72,8 @@ class CryptoSession {
std::string* deriv_context);
void GenerateEncryptContext(const std::string& input_context,
std::string* deriv_context);
bool GenerateSignature(const std::string& message, bool use_rsa,
std::string* signature);
bool GenerateSignature(const std::string& message, std::string* signature);
bool GenerateRsaSignature(const std::string& message, std::string* signature);
size_t GetOffset(std::string message, std::string field);
bool SetDestinationBufferType();

View File

@@ -0,0 +1,42 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#ifndef CORE_INCLUDE_INITIALIZATION_DATA_H_
#define CORE_INCLUDE_INITIALIZATION_DATA_H_
#include <string>
#include "wv_cdm_types.h"
namespace wvcdm {
class WvCdmEngineTest;
class InitializationData {
public:
InitializationData(const std::string& type,
const CdmInitData& data = CdmInitData());
bool is_supported() const { return is_cenc_ || is_webm_; }
bool is_cenc() const { return is_cenc_; }
bool is_webm() const { return is_webm_; }
bool IsEmpty() const { return data_.empty(); }
const std::string& type() const { return type_; }
const CdmInitData& data() const { return data_; }
private:
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH.
bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output);
std::string type_;
CdmInitData data_;
bool is_cenc_;
bool is_webm_;
CORE_DISALLOW_COPY_AND_ASSIGN(InitializationData);
};
} // namespace wvcdm
#endif // CORE_INCLUDE_INITIALIZATION_DATA_H_

View File

@@ -5,6 +5,7 @@
#include <set>
#include "initialization_data.h"
#include "wv_cdm_types.h"
namespace video_widevine_server {
@@ -27,7 +28,7 @@ class CdmLicense {
bool Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine);
bool PrepareKeyRequest(const CdmInitData& pssh_data,
bool PrepareKeyRequest(const InitializationData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id,
@@ -42,7 +43,7 @@ class CdmLicense {
bool RestoreOfflineLicense(CdmKeyMessage& license_request,
CdmKeyResponse& license_response,
CdmKeyResponse& license_renewal_response);
bool HasInitData() { return !init_data_.empty(); }
bool HasInitData() { return !stored_init_data_.empty(); }
bool IsKeyLoaded(const KeyId& key_id);
private:
@@ -54,12 +55,16 @@ class CdmLicense {
CdmResponseType HandleKeyErrorResponse(
const video_widevine_server::sdk::SignedMessage& signed_message);
template<typename T> bool PrepareContentId(const CdmLicenseType license_type,
const std::string& request_id,
T* content_id);
CryptoSession* session_;
PolicyEngine* policy_engine_;
std::string server_url_;
std::string token_;
std::string service_certificate_;
std::string init_data_;
std::string stored_init_data_;
bool initialized_;
std::set<KeyId> loaded_keys_;

View File

@@ -26,6 +26,7 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
SecurityLevel level);
uint32_t OEMCrypto_APIVersion(SecurityLevel level);
const char* OEMCrypto_SecurityLevel(SecurityLevel level);
bool OEMCrypto_SupportsUsageTable(SecurityLevel level);
} // namespace wvcdm
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_

View File

@@ -26,12 +26,6 @@ class Properties {
public:
static void Init();
static inline bool begin_license_usage_when_received() {
return begin_license_usage_when_received_;
}
static inline bool require_explicit_renew_request() {
return require_explicit_renew_request_;
}
static inline bool oem_crypto_use_secure_buffers() {
return oem_crypto_use_secure_buffers_;
}
@@ -39,13 +33,12 @@ class Properties {
static inline bool oem_crypto_use_userspace_buffers() {
return oem_crypto_use_userspace_buffers_;
}
static inline bool oem_crypto_require_usage_tables() {
return oem_crypto_require_usage_tables_;
}
static inline bool use_certificates_as_identification() {
return use_certificates_as_identification_;
}
static inline bool extract_pssh_data() { return extract_pssh_data_; }
static inline bool decrypt_with_empty_session_support() {
return decrypt_with_empty_session_support_;
}
static inline bool security_level_path_backward_compatibility_support() {
return security_level_path_backward_compatibility_support_;
}
@@ -73,12 +66,6 @@ class Properties {
private:
static const CdmClientPropertySet* GetCdmClientPropertySet(
const CdmSessionId& session_id);
static void set_begin_license_usage_when_received(bool flag) {
begin_license_usage_when_received_ = flag;
}
static void set_require_explicit_renew_request(bool flag) {
require_explicit_renew_request_ = flag;
}
static void set_oem_crypto_use_secure_buffers(bool flag) {
oem_crypto_use_secure_buffers_ = flag;
}
@@ -88,28 +75,23 @@ class Properties {
static void set_oem_crypto_use_userspace_buffers(bool flag) {
oem_crypto_use_userspace_buffers_ = flag;
}
static void set_oem_crypto_require_usage_tables(bool flag) {
oem_crypto_require_usage_tables_ = flag;
}
static void set_use_certificates_as_identification(bool flag) {
use_certificates_as_identification_ = flag;
}
static void set_extract_pssh_data(bool flag) { extract_pssh_data_ = flag; }
static void set_decrypt_with_empty_session_support(bool flag) {
decrypt_with_empty_session_support_ = flag;
}
static void set_security_level_path_backward_compatibility_support(
bool flag) {
security_level_path_backward_compatibility_support_ = flag;
}
private:
static bool begin_license_usage_when_received_;
static bool require_explicit_renew_request_;
static bool oem_crypto_use_secure_buffers_;
static bool oem_crypto_use_fifo_;
static bool oem_crypto_use_userspace_buffers_;
static bool oem_crypto_require_usage_tables_;
static bool use_certificates_as_identification_;
static bool extract_pssh_data_;
static bool decrypt_with_empty_session_support_;
static bool security_level_path_backward_compatibility_support_;
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;

View File

@@ -11,6 +11,7 @@
namespace wvcdm {
std::vector<uint8_t> a2b_hex(const std::string& b);
std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& b);
std::string a2bs_hex(const std::string& b);
std::string b2a_hex(const std::vector<uint8_t>& b);
std::string b2a_hex(const std::string& b);
@@ -20,6 +21,7 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& bin_input);
std::string HexEncode(const uint8_t* bytes, unsigned size);
std::string IntToString(int value);
std::string UintToString(unsigned int value);
int64_t htonll64(int64_t x);
}; // namespace wvcdm

View File

@@ -53,6 +53,12 @@ static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2";
static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3";
static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown";
static const std::string ISO_BMFF_VIDEO_MIME_TYPE = "video/mp4";
static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4";
static const std::string WEBM_VIDEO_MIME_TYPE = "video/webm";
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
static const std::string CENC_INIT_DATA_FORMAT = "cenc";
static const std::string WEBM_INIT_DATA_FORMAT = "webm";
} // namespace wvcdm
#endif // WVCDM_CORE_WV_CDM_CONSTANTS_H_

View File

@@ -23,6 +23,8 @@ typedef uint32_t CryptoSessionId;
typedef std::string CryptoKeyId;
typedef std::map<std::string, std::string> CdmAppParameterMap;
typedef std::map<std::string, std::string> CdmQueryMap;
typedef std::vector<std::string> CdmSecureStops;
typedef std::vector<uint8_t> CdmSecureStopReleaseMessage;
typedef std::string CdmProvisioningRequest;
typedef std::string CdmProvisioningResponse;
@@ -67,6 +69,11 @@ enum CdmSecurityLevel {
kSecurityLevelUnknown
};
enum CdmCertificateType {
kCertificateWidevine,
kCertificateX509,
};
struct CdmDecryptionParameters {
bool is_encrypted;
bool is_secure;

View File

@@ -5,7 +5,6 @@
#include <iostream>
#include <sstream>
#include "buffer_reader.h"
#include "cdm_session.h"
#include "license_protocol.pb.h"
#include "log.h"
@@ -94,8 +93,9 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
return KEY_ERROR;
}
delete iter->second;
CdmSession* session = iter->second;
sessions_.erase(session_id);
delete session;
return NO_ERROR;
}
@@ -117,7 +117,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id,
const CdmKeySetId& key_set_id,
const CdmInitData& init_data,
const InitializationData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
@@ -433,6 +433,8 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CdmEngine::GetProvisioningRequest(
CdmCertificateType cert_type,
const std::string& cert_authority,
CdmProvisioningRequest* request,
std::string* default_url) {
if (!request || !default_url) {
@@ -441,6 +443,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
}
return cert_provisioning_.GetProvisioningRequest(
cert_provisioning_requested_security_level_,
cert_type,
cert_authority,
request,
default_url);
}
@@ -453,12 +457,35 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CdmEngine::HandleProvisioningResponse(
CdmProvisioningResponse& response) {
CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key) {
if (response.empty()) {
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
return UNKNOWN_ERROR;
}
return cert_provisioning_.HandleProvisioningResponse(response);
if (NULL == cert) {
LOGE("CdmEngine::HandleProvisioningResponse: invalid certificate "
"destination");
return UNKNOWN_ERROR;
}
if (NULL == wrapped_key) {
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
"destination");
return UNKNOWN_ERROR;
}
return cert_provisioning_.HandleProvisioningResponse(response, cert,
wrapped_key);
}
CdmResponseType CdmEngine::GetSecureStops(
CdmSecureStops* secure_stops) {
return NO_ERROR;
}
CdmResponseType CdmEngine::ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message) {
return NO_ERROR;
}
CdmResponseType CdmEngine::Decrypt(
@@ -489,8 +516,6 @@ CdmResponseType CdmEngine::Decrypt(
CdmSessionMap::iterator iter;
if (session_id.empty()) {
if (!Properties::decrypt_with_empty_session_support()) return KEY_ERROR;
// Loop through the sessions to find the session containing the key_id.
for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyLoaded(*parameters.key_id)) {
@@ -501,7 +526,7 @@ CdmResponseType CdmEngine::Decrypt(
iter = sessions_.find(session_id);
}
if (iter == sessions_.end()) {
LOGE("CdmEngine::Decrypt: session_id not found[%d] = %s",
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.size(),
session_id.c_str());
return KEY_ERROR;
@@ -578,94 +603,6 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
return (key_system.find("widevine") != std::string::npos);
}
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// widevine pssh
bool CdmEngine::ExtractWidevinePssh(
const CdmInitData& init_data, CdmInitData* output) {
BufferReader reader(
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length());
static const uint8_t kWidevineSystemId[] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
};
// one PSSH blob consists of:
// 4 byte size of the PSSH atom, inclusive
// "pssh"
// 4 byte flags, value 0
// 16 byte system id
// 4 byte size of PSSH data, exclusive
while (1) {
// size of PSSH atom, used for skipping
uint32_t size;
if (!reader.Read4(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
return false;
}
// "pssh"
std::vector<uint8_t> pssh;
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)) {
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<uint8_t> system_id;
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))) {
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)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
return false;
}
output->clear();
if (!reader.ReadString(output, pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
return false;
}
return true;
}
// we did not find a matching record
return false;
}
void CdmEngine::OnTimerEvent() {
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {

View File

@@ -78,7 +78,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
DeviceFiles::LicenseState license_state;
if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_pssh_data_,
if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_init_data_,
&offline_key_request_, &offline_key_response_,
&offline_key_renewal_request_,
&offline_key_renewal_response_,
@@ -114,7 +114,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
}
CdmResponseType CdmSession::GenerateKeyRequest(
const CdmInitData& init_data, const CdmLicenseType license_type,
const InitializationData& init_data, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url) {
if (reinitialize_session_) {
@@ -140,20 +140,16 @@ CdmResponseType CdmSession::GenerateKeyRequest(
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);
return GenerateRenewalRequest(key_request, server_url);
} else {
if (init_data.empty() && !license_parser_.HasInitData()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return KEY_ERROR;
}
CdmInitData pssh_data = init_data;
if (Properties::extract_pssh_data()) {
if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) {
return KEY_ERROR;
}
if (init_data.IsEmpty() && !license_parser_.HasInitData()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR;
}
if (Properties::use_certificates_as_identification()) {
@@ -166,14 +162,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
}
if (!license_parser_.PrepareKeyRequest(pssh_data, license_type,
if (!license_parser_.PrepareKeyRequest(init_data, license_type,
app_parameters, session_id_,
key_request, server_url)) {
return KEY_ERROR;
}
if (license_type_ == kLicenseTypeOffline) {
offline_pssh_data_ = pssh_data;
offline_init_data_ = init_data.data();
offline_key_request_ = *key_request;
offline_release_server_url_ = *server_url;
}
@@ -198,9 +194,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
if (license_type_ == kLicenseTypeRelease) {
return ReleaseKey(key_response);
} else if (license_received_) { // renewal
return Properties::require_explicit_renew_request()
? UNKNOWN_ERROR
: RenewKey(key_response);
return RenewKey(key_response);
} else {
CdmResponseType sts = license_parser_.HandleKeyResponse(key_response);
@@ -296,18 +290,7 @@ CdmResponseType CdmSession::CancelKeyRequest() {
CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
return UNKNOWN_ERROR;
CdmResponseType status = crypto_session_->Decrypt(params);
if (UNKNOWN_ERROR == status) {
// Decrypt failed - check status of license and keys.
Clock clock;
int64_t current_time = clock.GetCurrentTime();
if (policy_engine_.IsLicenseDurationExpired(current_time) ||
policy_engine_.IsPlaybackDurationExpired(current_time)) {
return NEED_KEY;
}
}
return status;
return crypto_session_->Decrypt(params);
}
// License renewal
@@ -404,7 +387,7 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
return false;
return handle.StoreLicense(
key_set_id_, state, offline_pssh_data_, offline_key_request_,
key_set_id_, state, offline_init_data_, offline_key_request_,
offline_key_response_, offline_key_renewal_request_,
offline_key_renewal_response_, offline_release_server_url_);
}

View File

@@ -6,12 +6,13 @@
#include "license_protocol.pb.h"
#include "log.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace {
// WHAT: URL for Google Provisioning Server.
// WHY: The provisioning server supplies the certificate that is needed
// to communicate with the License Server.
// URL for Google Provisioning Server.
// The provisioning server supplies the certificate that is needed
// to communicate with the License Server.
const std::string kProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
@@ -21,6 +22,7 @@ const std::string kProvisioningServerUrl =
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ProvisioningOptions;
using video_widevine_server::sdk::ProvisioningRequest;
using video_widevine_server::sdk::ProvisioningResponse;
using video_widevine_server::sdk::SignedProvisioningMessage;
@@ -58,6 +60,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
*/
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SecurityLevel requested_security_level,
CdmCertificateType cert_type,
const std::string& cert_authority,
CdmProvisioningRequest* request,
std::string* default_url) {
if (!default_url) {
@@ -95,6 +99,24 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
provisioning_request.set_nonce(the_nonce);
ProvisioningOptions* options = provisioning_request.mutable_options();
switch (cert_type) {
case kCertificateWidevine:
options->set_certificate_type(
video_widevine_server::sdk::ProvisioningOptions_CertificateType_RSA_WIDEVINE);
break;
case kCertificateX509:
options->set_certificate_type(
video_widevine_server::sdk::ProvisioningOptions_CertificateType_X509);
break;
default:
LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type);
return UNKNOWN_ERROR;
}
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
@@ -164,7 +186,9 @@ bool CertificateProvisioning::ParseJsonResponse(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
CdmProvisioningResponse& response) {
CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key) {
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
@@ -229,6 +253,12 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
crypto_session_.Close();
if (cert_type_ == kCertificateX509) {
*cert = provisioning_response.device_certificate();
*wrapped_key = wrapped_rsa_key;
return NO_ERROR;
}
const std::string& device_certificate =
provisioning_response.device_certificate();

View File

@@ -235,7 +235,6 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
sizeof(request_id_base_));
++request_id_index_;
return NO_ERROR;
}
void CryptoSession::Close() {
@@ -271,9 +270,9 @@ bool CryptoSession::PrepareRequest(const std::string& message,
if (!Properties::use_certificates_as_identification() || is_provisioning) {
if (!GenerateDerivedKeys(message)) return false;
if (!GenerateSignature(message, false, signature)) return false;
if (!GenerateSignature(message, signature)) return false;
} else {
if (!GenerateSignature(message, true, signature)) return false;
if (!GenerateRsaSignature(message, signature)) return false;
}
return true;
@@ -285,12 +284,13 @@ bool CryptoSession::PrepareRenewalRequest(const std::string& message,
AutoLock auto_lock(crypto_lock_);
if (!signature) {
LOGE("CryptoSession::PrepareRenewalRequest : No output destination "
"provided.");
LOGE(
"CryptoSession::PrepareRenewalRequest : No output destination "
"provided.");
return false;
}
if (!GenerateSignature(message, false, signature)) {
if (!GenerateSignature(message, signature)) {
return false;
}
@@ -316,8 +316,9 @@ void CryptoSession::GenerateMacContext(const std::string& input_context,
void CryptoSession::GenerateEncryptContext(const std::string& input_context,
std::string* deriv_context) {
if (!deriv_context) {
LOGE("CryptoSession::GenerateEncryptContext : No output destination "
"provided.");
LOGE(
"CryptoSession::GenerateEncryptContext : No output destination "
"provided.");
return;
}
@@ -380,7 +381,7 @@ CdmResponseType CryptoSession::LoadKeys(const std::string& message,
OEMCryptoResult sts = OEMCrypto_LoadKeys(
oec_session_id_, msg, message.size(),
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
enc_mac_key_iv, enc_mac_key, num_keys, &load_key_array[0]);
enc_mac_key_iv, enc_mac_key, num_keys, &load_key_array[0], NULL, 0);
if (OEMCrypto_SUCCESS == sts) {
return KEY_ADDED;
@@ -502,48 +503,77 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
return true;
}
bool CryptoSession::GenerateSignature(const std::string& message, bool use_rsa,
bool CryptoSession::GenerateSignature(const std::string& message,
std::string* signature) {
LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_);
if (!signature) return false;
size_t length = 0;
OEMCryptoResult sts = OEMCrypto_SUCCESS;
if (use_rsa) {
sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), NULL, &length);
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
} else {
sts = OEMCrypto_GenerateSignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), NULL, &length);
}
signature->resize(length);
if (use_rsa) {
sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
} else {
sts = OEMCrypto_GenerateSignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
}
size_t length = signature->size();
OEMCryptoResult sts = OEMCrypto_GenerateSignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
if (OEMCrypto_SUCCESS != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
}
// Retry with proper-sized signature buffer
signature->resize(length);
sts = OEMCrypto_GenerateSignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
if (OEMCrypto_SUCCESS != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
}
}
// Trim signature buffer
signature->resize(length);
return true;
}
bool CryptoSession::GenerateRsaSignature(const std::string& message,
std::string* signature) {
LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_);
if (!signature) return false;
size_t length = signature->size();
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length, kSign_RSASSA_PSS);
if (OEMCrypto_SUCCESS != sts) {
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
// Retry with proper-sized signature buffer
signature->resize(length);
sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length, kSign_RSASSA_PSS);
if (OEMCrypto_SUCCESS != sts) {
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
}
// Trim signature buffer
signature->resize(length);
return true;

View File

@@ -459,15 +459,17 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get base path");
LOGW(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get base path");
return;
}
std::vector<std::string> security_dirs;
if (!Properties::GetSecurityLevelDirectories(&security_dirs)) {
LOGW("DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get security directories");
LOGW(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get security directories");
return;
}
@@ -482,8 +484,9 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
}
if (pos == std::string::npos) {
LOGV("DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level "
"specific path not found. Check properties?");
LOGV(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level "
"specific path not found. Check properties?");
return;
}

View File

@@ -0,0 +1,125 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "initialization_data.h"
#include <string.h>
#include "buffer_reader.h"
#include "log.h"
#include "properties.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
InitializationData::InitializationData(const std::string& type,
const CdmInitData& data)
: type_(type), is_cenc_(false), is_webm_(false) {
if (type == ISO_BMFF_VIDEO_MIME_TYPE ||
type == ISO_BMFF_AUDIO_MIME_TYPE ||
type == CENC_INIT_DATA_FORMAT) {
is_cenc_ = true;
} else if (type == WEBM_VIDEO_MIME_TYPE ||
type == WEBM_AUDIO_MIME_TYPE ||
type == WEBM_INIT_DATA_FORMAT) {
is_webm_ = true;
}
if (is_supported()) {
if (is_cenc()) {
ExtractWidevinePssh(data, &data_);
} else {
data_ = data;
}
}
}
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH.
bool InitializationData::ExtractWidevinePssh(
const CdmInitData& init_data, CdmInitData* output) {
BufferReader reader(
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length());
// TODO(kqyang): Extracted from an actual init_data;
// Need to find out where it comes from.
static const uint8_t kWidevineSystemId[] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
};
// one PSSH blob consists of:
// 4 byte size of the PSSH atom, inclusive
// "pssh"
// 4 byte flags, value 0
// 16 byte system id
// 4 byte size of PSSH data, exclusive
while (1) {
// size of PSSH atom, used for skipping
uint32_t size;
if (!reader.Read4(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
return false;
}
// "pssh"
std::vector<uint8_t> pssh;
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)) {
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<uint8_t> system_id;
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))) {
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)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
return false;
}
output->clear();
if (!reader.ReadString(output, pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
return false;
}
return true;
}
// we did not find a matching record
return false;
}
} // namespace wvcdm

View File

@@ -84,6 +84,7 @@ using video_widevine_server::sdk::EncryptedClientIdentification;
using video_widevine_server::sdk::LicenseRequest;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_WebM;
using video_widevine_server::sdk::
LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine_server::sdk::License;
@@ -157,7 +158,7 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
return true;
}
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id,
@@ -167,7 +168,12 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return false;
}
if (init_data.empty() && init_data_.empty()) {
if (!init_data.is_supported()) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return false;
}
if (init_data.IsEmpty() && stored_init_data_.empty()) {
LOGE("CdmLicense::PrepareKeyRequest: empty init data provided");
return false;
}
@@ -192,7 +198,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
serialized_service_certificate = service_certificate_;
if (privacy_mode_enabled && serialized_service_certificate.empty()) {
init_data_ = init_data;
stored_init_data_ = init_data.data();
return PrepareServiceCertificateRequest(signed_request, server_url);
}
@@ -259,15 +265,18 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
DeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(serialized_service_certificate)) {
LOGE("CdmLicense::PrepareKeyRequest: unable to parse retrieved "
"service certificate");
LOGE(
"CdmLicense::PrepareKeyRequest: unable to parse retrieved "
"service certificate");
return false;
}
if (service_certificate.type() !=
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
LOGE("CdmLicense::PrepareKeyRequest: retrieved certificate not of type"
" service, %d", service_certificate.type());
LOGE(
"CdmLicense::PrepareKeyRequest: retrieved certificate not of type"
" service, %d",
service_certificate.type());
return false;
}
encrypted_client_id->set_service_id(service_certificate.service_id());
@@ -304,33 +313,44 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
LicenseRequest_ContentIdentification* content_id =
license_request.mutable_content_id();
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id();
if (init_data.is_cenc()) {
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id();
if (!init_data.empty()) {
cenc_content_id->add_pssh(init_data);
} else if (privacy_mode_enabled && !init_data_.empty()) {
cenc_content_id->add_pssh(init_data_);
if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data.data());
} else if (privacy_mode_enabled && !stored_init_data_.empty()) {
cenc_content_id->add_pssh(stored_init_data_);
} else {
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
return false;
}
if (!PrepareContentId(license_type, request_id, cenc_content_id)) {
return false;
}
} else if (init_data.is_webm()) {
LicenseRequest_ContentIdentification_WebM* webm_content_id =
content_id->mutable_webm_id();
if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data.data());
} else if (privacy_mode_enabled && !stored_init_data_.empty()) {
webm_content_id->set_header(stored_init_data_);
} else {
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
return false;
}
if (!PrepareContentId(license_type, request_id, webm_content_id)) {
return false;
}
} else {
LOGD("CdmLicense::PrepareKeyRequest: init data not available");
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
init_data.type().c_str());
return false;
}
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 = %d",
license_type);
return false;
break;
}
cenc_content_id->set_request_id(request_id);
// The time field will be updated once the cdm wrapper
// has been updated to pass us in the time.
license_request.set_request_time(0);
@@ -427,8 +447,9 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
return false;
if (license_request_signature.empty()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: empty license request"
" signature");
LOGE(
"CdmLicense::PrepareKeyUpdateRequest: empty license request"
" signature");
return false;
}
@@ -456,8 +477,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE("CdmLicense::HandleKeyResponse: unable to parse signed license"
" response");
LOGE(
"CdmLicense::HandleKeyResponse: unable to parse signed license"
" response");
return KEY_ERROR;
}
@@ -510,9 +532,10 @@ CdmResponseType CdmLicense::HandleKeyResponse(
}
if (mac_key_iv.size() != KEY_IV_SIZE || mac_key.size() != MAC_KEY_SIZE) {
LOGE("CdmLicense::HandleKeyResponse: mac key/iv size error"
"(key/iv size expected: %d/%d, actual: %d/%d",
MAC_KEY_SIZE, KEY_IV_SIZE, mac_key.size(), mac_key_iv.size());
LOGE(
"CdmLicense::HandleKeyResponse: mac key/iv size error"
"(key/iv size expected: %d/%d, actual: %d/%d",
MAC_KEY_SIZE, KEY_IV_SIZE, mac_key.size(), mac_key_iv.size());
return KEY_ERROR;
}
}
@@ -575,8 +598,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
" from signed message");
LOGE(
"CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
" from signed message");
return KEY_ERROR;
}
@@ -611,9 +635,10 @@ bool CdmLicense::RestoreOfflineLicense(
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());
LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
"%u %u",
license_request.size(), license_response.size());
return false;
}
@@ -624,9 +649,10 @@ bool CdmLicense::RestoreOfflineLicense(
}
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
LOGE("CdmLicense::RestoreOfflineLicense: license request type: expected = "
"%d, actual = %d",
SignedMessage::LICENSE_REQUEST, signed_request.type());
LOGE(
"CdmLicense::RestoreOfflineLicense: license request type: expected = "
"%d, actual = %d",
SignedMessage::LICENSE_REQUEST, signed_request.type());
return false;
}
@@ -656,13 +682,15 @@ bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
return false;
}
if (!signed_request) {
LOGE("CdmLicense::PrepareServiceCertificateRequest: no signed request"
" provided");
LOGE(
"CdmLicense::PrepareServiceCertificateRequest: no signed request"
" provided");
return false;
}
if (!server_url) {
LOGE("CdmLicense::PrepareServiceCertificateRequest: no server url"
" provided");
LOGE(
"CdmLicense::PrepareServiceCertificateRequest: no server url"
" provided");
return false;
}
SignedMessage signed_message;
@@ -678,8 +706,9 @@ CdmResponseType CdmLicense::HandleServiceCertificateResponse(
SignedDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::HandleServiceCertificateResponse: unable to parse"
"signed device certificate");
LOGE(
"CdmLicense::HandleServiceCertificateResponse: unable to parse"
"signed device certificate");
return KEY_ERROR;
}
@@ -688,32 +717,36 @@ CdmResponseType CdmLicense::HandleServiceCertificateResponse(
&kServiceCertificateCAPublicKey[0],
&kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]);
if (!root_ca_key.Init(ca_public_key)) {
LOGE("CdmLicense::HandleServiceCertificateResponse: public key "
"initialization failed");
LOGE(
"CdmLicense::HandleServiceCertificateResponse: public key "
"initialization failed");
return KEY_ERROR;
}
if (!root_ca_key.VerifySignature(
signed_service_certificate.device_certificate(),
signed_service_certificate.signature())) {
LOGE("CdmLicense::HandleServiceCertificateResponse: service "
"certificate verification failed");
LOGE(
"CdmLicense::HandleServiceCertificateResponse: service "
"certificate verification failed");
return KEY_ERROR;
}
DeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(
signed_service_certificate.device_certificate())) {
LOGE("CdmLicense::HandleServiceCertificateResponse: unable to parse "
"retrieved service certificate");
LOGE(
"CdmLicense::HandleServiceCertificateResponse: unable to parse "
"retrieved service certificate");
return KEY_ERROR;
}
if (service_certificate.type() !=
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
LOGE("CdmLicense::HandleServiceCertificateResponse: certificate not of type"
" service, %d",
service_certificate.type());
LOGE(
"CdmLicense::HandleServiceCertificateResponse: certificate not of type"
" service, %d",
service_certificate.type());
return KEY_ERROR;
}
@@ -743,6 +776,28 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
}
}
template<typename T>
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
const std::string& request_id,
T* content_id) {
switch (license_type) {
case kLicenseTypeOffline:
content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
break;
case kLicenseTypeStreaming:
content_id->set_license_type(
video_widevine_server::sdk::STREAMING);
break;
default:
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
license_type);
return false;
}
content_id->set_request_id(request_id);
return true;
}
bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}

View File

@@ -276,12 +276,29 @@ message SessionState {
// Public protocol buffer definitions for Widevine Device Certificate
// Provisioning protocol.
// ProvisioningOptions specifies the type of certificate to specify and
// in the case of X509 certificates, the certificate authority to use.
message ProvisioningOptions {
enum CertificateType {
RSA_WIDEVINE = 0; // Default. The original certificate type.
X509 = 1; // X.509 certificate.
}
optional CertificateType certificate_type = 1;
// It is recommended that the certificate_authority specify the X.509
// Subject of the signing certificate.
optional string certificate_authority = 2;
}
// Provisioning request sent by client devices to provisioning service.
message ProvisioningRequest {
// Device root of trust and other client identification. Required.
optional ClientIdentification client_id = 1;
// Nonce value used to prevent replay attacks. Required.
optional bytes nonce = 2;
// Options for type of certificate to generate. Optional.
optional ProvisioningOptions options = 3;
}
// Provisioning response sent by the provisioning server to client devices.

View File

@@ -0,0 +1,127 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
* This should be used when liboemcrypto.so is linked with the CDM code at
* compile time.
* An implementation should compile either oemcrypto_adapter_dynamic.cpp or
* oemcrypto_adapter_static.cpp, but not both.
* This version contains shim code to allow an older, version 8 API, oemcrypto,
* to be linked with CDM.
*
******************************************************************************/
#include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h"
extern "C"
OEMCryptoResult OEMCrypto_LoadKeys_V8(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
const uint8_t* enc_mac_keys_iv,
const uint8_t* enc_mac_keys,
size_t num_keys,
const OEMCrypto_KeyObject* key_array);
extern "C"
OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t *signature_length);
namespace wvcdm {
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
SecurityLevel level) {
return ::OEMCrypto_OpenSession(session);
}
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
return ::OEMCrypto_IsKeyboxValid();
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level) {
return ::OEMCrypto_GetDeviceID(deviceID, idLength);
}
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
SecurityLevel level) {
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level) {
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
return ::OEMCrypto_APIVersion();
}
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
return ::OEMCrypto_SecurityLevel();
}
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys,
const OEMCrypto_KeyObject* key_array,
const uint8_t* pst, size_t pst_length) {
return OEMCrypto_LoadKeys_V8(session, message, message_length, signature,
signature_length, enc_mac_key_iv, enc_mac_key,
num_keys, key_array);
}
extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme) {
return OEMCrypto_GenerateRSASignature_V8(session, message, message_length,
signature, signature_length);
}
extern "C"
OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current,
OEMCrypto_HDCP_Capability *maximum) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" bool OEMCrypto_SupportsUsageTable() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst,
size_t pst_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
const uint8_t *pst,
size_t pst_length,
OEMCrypto_PST_Report *buffer,
size_t *buffer_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length,
const uint8_t *message,
size_t message_length,
const uint8_t *signature,
size_t signature_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}; // namespace wvcdm

View File

@@ -158,33 +158,18 @@ void PolicyEngine::UpdateLicense(
policy_max_duration_seconds_ = policy_.license_duration_seconds();
}
if (Properties::begin_license_usage_when_received())
playback_start_time_ = current_time;
// Update state
if (Properties::begin_license_usage_when_received()) {
if (policy_.renew_with_usage()) {
license_state_ = kLicenseStateNeedRenewal;
}
else {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
}
if (license_state_ == kLicenseStateInitial) {
license_state_ = kLicenseStateInitialPendingUsage;
}
else {
if (license_state_ == kLicenseStateInitial) {
license_state_ = kLicenseStateInitialPendingUsage;
}
else {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
}
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
}
}
void PolicyEngine::BeginDecryption() {
if ((playback_start_time_ == 0) &&
(!Properties::begin_license_usage_when_received())) {
if (playback_start_time_ == 0) {
switch (license_state_) {
case kLicenseStateInitialPendingUsage:
case kLicenseStateNeedRenewal:

View File

@@ -9,27 +9,21 @@ const char* kSecurityLevelDirs[] = {"L1/", "L3/"};
} // namespace
namespace wvcdm {
bool Properties::begin_license_usage_when_received_;
bool Properties::require_explicit_renew_request_;
bool Properties::oem_crypto_use_secure_buffers_;
bool Properties::oem_crypto_use_fifo_;
bool Properties::oem_crypto_use_userspace_buffers_;
bool Properties::oem_crypto_require_usage_tables_;
bool Properties::use_certificates_as_identification_;
bool Properties::extract_pssh_data_;
bool Properties::decrypt_with_empty_session_support_;
bool Properties::security_level_path_backward_compatibility_support_;
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
void Properties::Init() {
begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived;
require_explicit_renew_request_ = kPropertyRequireExplicitRenewRequest;
oem_crypto_use_secure_buffers_ = kPropertyOemCryptoUseSecureBuffers;
oem_crypto_use_fifo_ = kPropertyOemCryptoUseFifo;
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
oem_crypto_require_usage_tables_ = kPropertyOemCryptoRequireUsageTable;
use_certificates_as_identification_ =
kPropertyUseCertificatesAsIdentification;
extract_pssh_data_ = kExtractPsshData;
decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport;
security_level_path_backward_compatibility_support_ =
kSecurityLevelPathBackwardCompatibilitySupport;
session_property_set_.reset(new CdmClientPropertySetMap());

View File

@@ -2,6 +2,7 @@
#include "string_conversions.h"
#include <arpa/inet.h>
#include <ctype.h>
#include <iostream>
#include <stdio.h>
@@ -50,6 +51,16 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
return array;
}
// converts an ascii hex string(2 bytes per digit) into a decimal byte string
// dump the string with the label.
std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& byte) {
std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]"
<< std::endl << std::endl;
return a2b_hex(byte);
}
std::string a2bs_hex(const std::string& byte) {
std::vector<uint8_t> array = a2b_hex(byte);
return std::string(array.begin(), array.end());
@@ -157,4 +168,21 @@ std::string UintToString(unsigned int value) {
return out_string;
}
int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order)
union {
uint32_t array[2];
int64_t number;
} mixed;
mixed.number = 1;
if (mixed.array[0] == 1) {
mixed.number = x; // Little Endian.
uint32_t temp = mixed.array[0];
mixed.array[0] = htonl(mixed.array[1]);
mixed.array[1] = htonl(temp);
return mixed.number;
} else {
return x; // Big Endian.
}
}
}; // namespace wvcdm

View File

@@ -33,8 +33,8 @@ const std::string kTestData =
// Arbitrary encoded test vectors
const std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh");
const std::string kOneByteOverB64Data("SGVsbG8gR29vZ2xlcg==");
const std::string kTwoBytesOverB64Data("SGVsbG8gR29vZ2xlcnM=");
const std::string kOneByteOverB64Data("SGVsbG8gRnJpZW5kIQ==");
const std::string kTwoBytesOverB64Data("SGVsbG8gRnJpZW5kISE=");
const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
@@ -70,4 +70,18 @@ TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest,
::testing::ValuesIn(kBase64TestVectors));
class HtoNLL64Test : public ::testing::Test {};
TEST_F(HtoNLL64Test, PositiveNumber) {
uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8};
int64_t *network_byte_order = reinterpret_cast<int64_t *>(data);
int64_t host_byte_order = htonll64(*network_byte_order);
EXPECT_EQ(0x0102030405060708, host_byte_order);
}
TEST_F(HtoNLL64Test, NegativeNumber) {
uint8_t data[8] = {0xfe, 2, 3, 4, 5, 6, 7, 8};
int64_t *network_byte_order = reinterpret_cast<int64_t *>(data);
int64_t host_byte_order = htonll64(*network_byte_order);
EXPECT_EQ(-0x01FdFcFbFaF9F8F8, host_byte_order);
}
} // namespace wvcdm

View File

@@ -3,6 +3,8 @@
#include <errno.h>
#include <getopt.h>
#include <string>
#if defined(CHROMIUM_BUILD)
#include "base/at_exit.h"
#include "base/message_loop/message_loop.h"
@@ -10,12 +12,14 @@
#include "cdm_engine.h"
#include "config_test_env.h"
#include "gtest/gtest.h"
#include "initialization_data.h"
#include "license_request.h"
#include "log.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "url_request.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
namespace {
@@ -25,18 +29,19 @@ const int kHttpOk = 200;
// Default license server, can be configured using --server command line option
// Default key id (pssh), can be configured using --keyid command line option
std::string g_client_auth;
wvcdm::KeyId g_key_id;
wvcdm::KeyId g_key_id_pssh;
wvcdm::KeyId g_key_id_unwrapped;
wvcdm::CdmKeySystem g_key_system;
std::string g_license_server;
std::string g_port;
wvcdm::KeyId g_wrong_key_id;
int g_use_full_path = 0; // cannot use boolean in getopt_long
// WHAT: This is an RSA certificate message from the provisioning server.
// The client sends this certificate to a license server for
// device authentication by the license server.
// WHY: This certificate is used to test the CDM engine's provisioning
// response handling.
// This is an RSA certificate message from the provisioning server.
// The client sends this certificate to a license server for device
// authentication by the license server.
// This certificate is used to test the CDM engine's provisioning
// response handling.
static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
"{\"signedResponse\": {"
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
@@ -85,6 +90,10 @@ static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm";
} // namespace
namespace wvcdm {
@@ -100,16 +109,13 @@ class WvCdmEngineTest : public testing::Test {
}
protected:
void GenerateKeyRequest(const std::string& key_system,
const std::string& key_id) {
void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string) {
CdmAppParameterMap app_parameters;
std::string server_url;
std::string init_data = key_id;
CdmKeySetId key_set_id;
if (!Properties::extract_pssh_data()) {
EXPECT_TRUE(CdmEngine::ExtractWidevinePssh(key_id, &init_data));
}
InitializationData init_data(init_data_type_string, key_id);
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_.GenerateKeyRequest(session_id_,
@@ -121,8 +127,7 @@ class WvCdmEngineTest : public testing::Test {
&server_url));
}
void GenerateRenewalRequest(const std::string& key_system,
const std::string& init_data) {
void GenerateRenewalRequest() {
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_.GenerateRenewalRequest(session_id_,
&key_msg_,
@@ -161,20 +166,18 @@ class WvCdmEngineTest : public testing::Test {
}
void VerifyNewKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data){
const std::string& client_auth){
std::string resp = GetKeyRequestResponse(server_url,
client_auth);
CdmKeySetId key_set_id;
EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, &key_set_id), KEY_ADDED);
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.AddKey(session_id_, resp, &key_set_id));
}
void VerifyRenewalKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data) {
const std::string& client_auth) {
std::string resp = GetKeyRequestResponse(server_url,
client_auth);
EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED);
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
}
CdmEngine cdm_engine_;
@@ -187,38 +190,54 @@ TEST(WvCdmProvisioningTest, ProvisioningTest) {
CdmEngine cdm_engine;
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
cdm_engine.GetProvisioningRequest(&prov_request, &provisioning_server_url);
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse);
cdm_engine.GetProvisioningRequest(cert_type, cert_authority,
&prov_request, &provisioning_server_url);
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse,
&cert, &wrapped_key);
}
TEST_F(WvCdmEngineTest, BaseMessageTest) {
GenerateKeyRequest(g_key_system, g_key_id);
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, DISABLED_BaseWebmMessageTest) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
TEST_F(WvCdmEngineTest, WrongMessageTest) {
std::string wrong_message = a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message);
GenerateKeyRequest(wrong_message, kCencMimeType);
// We should receive a response with no license, i.e. the extracted license
// response message should be empty.
ASSERT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth));
}
TEST_F(WvCdmEngineTest, NormalDecryption) {
GenerateKeyRequest(g_key_system, g_key_id);
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, DISABLED_NormalDecryptionWebm) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
}
TEST_F(WvCdmEngineTest, LicenseRenewal) {
GenerateKeyRequest(g_key_system, g_key_id);
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
GenerateRenewalRequest(g_key_system, g_key_id);
GenerateRenewalRequest();
VerifyRenewalKeyResponse(server_url_.empty() ? g_license_server : server_url_,
g_client_auth,
g_key_id);
g_client_auth);
}
} // namespace wvcdm
@@ -227,14 +246,14 @@ int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
wvcdm::InitLogging(argc, argv);
wvcdm::ConfigTestEnv config(wvcdm::kGoogleLicenseServerTest);
wvcdm::ConfigTestEnv config(wvcdm::kGooglePlayServer);
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id());
// The following variables are configurable through command line options.
g_license_server.assign(config.license_server());
g_key_id.assign(config.key_id());
g_key_id_pssh.assign(config.key_id());
g_port.assign(config.port());
std::string license_server(g_license_server);
@@ -254,8 +273,8 @@ int main(int argc, char **argv) {
while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) {
switch (opt) {
case 'k': {
g_key_id.clear();
g_key_id.assign(optarg);
g_key_id_pssh.clear();
g_key_id_pssh.assign(optarg);
break;
}
case 'p': {
@@ -298,7 +317,7 @@ int main(int argc, char **argv) {
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
std::cout << "configure the key id or pssh, in hex format" << std::endl;
std::cout << std::setw(30) << std::left << " default keyid:";
std::cout << g_key_id << std::endl;
std::cout << g_key_id_pssh << std::endl;
std::cout << std::setw(30) << std::left << " --use_full_path";
std::cout << "specify server url is not a proxy server" << std::endl;
@@ -309,12 +328,17 @@ int main(int argc, char **argv) {
std::cout << std::endl;
std::cout << "Server: " << g_license_server << std::endl;
std::cout << "Port: " << g_port << std::endl;
std::cout << "KeyID: " << g_key_id << std::endl << std::endl;
std::cout << "KeyID: " << g_key_id_pssh << std::endl << std::endl;
g_key_id = wvcdm::a2bs_hex(g_key_id);
g_key_id_pssh = wvcdm::a2bs_hex(g_key_id_pssh);
config.set_license_server(g_license_server);
config.set_port(g_port);
config.set_key_id(g_key_id);
config.set_key_id(g_key_id_pssh);
// Extract the key ID from the PSSH box.
wvcdm::InitializationData extractor(wvcdm::CENC_INIT_DATA_FORMAT,
g_key_id_pssh);
g_key_id_unwrapped = extractor.data();
#if defined(CHROMIUM_BUILD)
base::AtExitManager exit;

View File

@@ -3,91 +3,69 @@
#include "config_test_env.h"
namespace {
// WHAT: URL of provisioning server (returned by GetProvisioningRequest())
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// WHAT: URL of test provisioning server - This is a placeholder for
// an alternate provisioning server.
// WHY: request_license_test uses this url.
const std::string kProductionTestProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// Google test License Server parameters
// This is a test server that has limited content and relatively
// open access controls. It is maintained for integration testing of
// CDM and OEMCrypto implementations.
// WHAT: URL for test license server.
const std::string kGgLicenseServer =
"http://widevine-proxy.appspot.com/proxy";
// WHAT: Test client authorization string.
// WHY: Needed to pass client info to server.
const std::string kGgClientAuth = "";
// WHAT: License info for test content. This is a valid
// license for test content, registered with the
// test license server.
// Video ID: CJTHyHGbKKA
// KeyId: 20ea8aa187aa50a7aa66723d7225c18f
// ContentID: 0894c7c8719b28a0
//
// SD Video KeyID (142-144): 20EA8AA187AA50A7AA66723D7225C18F
// HD Video KeyID (145,146): 1BEFCD2630185DFEA1A36E5C91C0BA63
// Audio KeyID (148,149,150): 812A73CD62A45336B22FD13D00102106
const std::string kGgKeyId =
// Youtube Content Protection license server data
const std::string kYtCpLicenseServer =
"http://wv-ref-eme-player.appspot.com/proxy";
const std::string kYtCpClientAuth = "";
const std::string kYtCpKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"08011210812A73CD62A45336B22FD13D00102106"; // pssh data
"0801121030313233343536373839616263646566"; // pssh data
// WHAT: An invalid license id, expected to fail
// Youtube license server data
const std::string kYtLicenseServer =
"https://www.youtube.com/api/drm/"
"widevine?video_id=03681262dc412c06&source=YOUTUBE";
const std::string kYtClientAuth = "";
const std::string kYtKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121093789920E8D6520098577DF8F2DD5546"; // pssh data
// Google Play license server data
const std::string kGpLicenseServer =
"https://jmt17.google.com/video/license/GetCencLicense";
// Test client authorization string.
// NOTE: Append a userdata attribute to place a unique marker that the
// server team can use to track down specific requests during debugging
// e.g., "<existing-client-auth-string>&userdata=<your-ldap>.<your-tag>"
// "<existing-client-auth-string>&userdata=jbmr2.dev"
const std::string kGpClientAuth =
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
const std::string kGpKeyId =
"000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data
// An invalid key id, expected to fail
const std::string kWrongKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
// Sample license server parameters
// NOTE: This data is not valid. It will be replaced with license
// server-specific data supplied by server administrators.
// URL of provisioning server (returned by GetProvisioningRequest())
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// WHAT: URL for license server.
const std::string kTtCpLicenseServer =
"http://widevine-proxy.appspot.com/invalid";
// Return production-rooted certificates that have test bit set,
// request_license_test uses this url.
const std::string kProductionTestProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1exttest/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// WHAT: Client authorization string for license server request
// WHY: May be needed to pass client info to server.
const std::string kTtCpClientAuth = "";
const std::string kServerSdkLicenseServer =
"http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi";
// WHAT: License ID for test license server. This is not
// a valid license ID unless the ID is registered
// with the server. A valid license ID will be supplied
// by server administrators.
const std::string kTtCpKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121030313233343536373839616263646566"; // pssh data
// WHAT: Table of license servers useable for testing.
// WHY: Allow testing against multiple license servers.
// Fields:
// id - enum for identifying/selecting this license server
// url - url of license server
// client_tag - default client authorization string
// key_id - license id for test license on this server
// port - http port
// use_chunked_transfer - server communication setting
// use_secure_transfer - server communication setting
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{ wvcdm::kGoogleLicenseServerTest, kGgLicenseServer,
kGgClientAuth, kGgKeyId, kDefaultHttpsPort, true, true },
{ wvcdm::kPartnerLicenseServer, kTtCpLicenseServer,
kTtCpClientAuth, kTtCpKeyId, kDefaultHttpPort, false, false }
{ wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
kDefaultHttpsPort, true, true },
{ wvcdm::kYouTubeContentProtectionServer, kYtCpLicenseServer,
kYtCpClientAuth, kYtCpKeyId, kDefaultHttpPort, false, false }
};
} // namespace
@@ -100,7 +78,6 @@ ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id)
license_server_(license_servers[server_id].url),
port_(license_servers[server_id].port),
provisioning_server_url_(kProductionProvisioningServerUrl),
provisioning_test_server_url_(kProductionTestProvisioningServerUrl),
use_chunked_transfer_(license_servers[server_id].use_chunked_transfer),
use_secure_transfer_(license_servers[server_id].use_secure_transfer),
wrong_key_id_(kWrongKeyId) {}

View File

@@ -12,12 +12,9 @@ const std::string kDefaultHttpPort = "80";
}
namespace wvcdm {
//WHAT: Index into table of alternate license servers (license_servers[]).
//WHY: Allow testing against multiple license server.
typedef enum {
kGoogleLicenseServerTest,
kPartnerLicenseServer
kGooglePlayServer,
kYouTubeContentProtectionServer
} LicenseServerId;
// Configures default test environment.
@@ -44,9 +41,6 @@ class ConfigTestEnv {
const std::string& provisioning_server_url() const {
return provisioning_server_url_;
}
const std::string& provisioning_test_server_url() const {
return provisioning_test_server_url_;
}
bool use_chunked_transfer() { return use_chunked_transfer_; }
bool use_secure_transfer() { return use_secure_transfer_; }
const KeyId& wrong_key_id() const { return wrong_key_id_; }
@@ -67,7 +61,6 @@ class ConfigTestEnv {
std::string license_server_;
std::string port_;
std::string provisioning_server_url_;
std::string provisioning_test_server_url_;
bool use_chunked_transfer_;
bool use_secure_transfer_;
KeyId wrong_key_id_;

View File

@@ -28,9 +28,9 @@ const uint32_t kCertificateLen = 700;
const uint32_t kWrappedKeyLen = 500;
const uint32_t kProtobufEstimatedLen = 75;
// WHAT: Structurally valid test certificate.
// WHY: The data elements in this module are used to test the storage
// and retrieval of certificates and licenses
// Structurally valid test certificate.
// The data elements in this module are used to test the storage and
// retrieval of certificates and licenses
const std::string kTestCertificate =
"124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33"
"347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E"
@@ -53,9 +53,9 @@ const std::string kTestCertificate =
"701B122D340A3D145436137002687E4C470D2F6F4C357A3245384D737B734E2274301179"
"402473486311156E5A0C78644C593273";
// WHAT: A Wrapped Private Key
// WHY: The data elements in this module are used to test the storage
// and retrieval of certificates and licenses
// A Wrapped Private Key
// The data elements in this module are used to test the storage and
// retrieval of certificates and licenses
const std::string kTestWrappedPrivateKey =
"4F724B065326371A2F5F6F51467C2E26555C453B5C7C1B4F2738454B782E3E7B5340435A"
"66374D0612052C521A233D7A67194871751C78575E5177070130264C4F037633320E667B"
@@ -72,9 +72,9 @@ const std::string kTestWrappedPrivateKey =
"263043020E1E6760123D51056F2F1E482F2E3D021B27677D3E7E3C0C11757C3448275E08"
"382E111263644C6D224714706D760A054A586E17505C3429575A41043F184209";
// WHAT: The test certificate in file storage format.
// WHY: The data elements in this module are used to test the storage
// and retrieval of certificates and licenses
// The test certificate in file storage format.
// The data elements in this module are used to test the storage and
// retrieval of certificates and licenses
const std::string kTestCertificateFileData =
"0ABD09080110011AB6090ABC05124B035F3D256A656F0E505A085E7A6C482B61035E0C4A"
"540F7803137F4C3B45206B7F33347F4D7A005E56400F0955011F4E07072D0D4678181746"
@@ -126,10 +126,9 @@ struct LicenseInfo {
size_t kNumberOfLicenses = 3;
// WHAT: Sample license data and related data for storage and
// use for offline playback. The license data and URLs
// in this test are not real.
// WHY: Test storage and retrieval of license-related data.
// Sample license data and related data for storage and use for offline
// playback. The license data and URLs in this test are not real. Test
// storage and retrieval of license-related data.
LicenseInfo license_test_data[] = {
// license 0
@@ -708,10 +707,9 @@ LicenseInfo license_test_data[] = {
"72702E676F6F676C652E636F6D3A383838382F64726D12205CD2C43C618C"
"CA27BBCB2EEBDE32B57CBD51B424FD85DAB715B7F5A87546FD40")}};
// WHAT: Sample license data and related data for storage and
// use for offline playback. The license data and URLs
// in this test are not real.
// WHY: Test license-related functions.
// Sample license data and related data for storage and use for offline
// playback. The license data and URLs in this test are not real.
// The data is used to test license-related functions.
LicenseInfo license_update_test_data[] = {
// active license
{"key_set_id_: ksid2A048BC7FAEC885A", DeviceFiles::kLicenseStateActive,
@@ -811,7 +809,7 @@ LicenseInfo license_update_test_data[] = {
"08B2C467355129"),
"https://test.google.com/license/GetCencLicense",
wvcdm::a2bs_hex(
"0A9F15080210012298150801121408011210303132333435363738394142"
"0AA41508021001229D150801121408011210303132333435363738394142"
"434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E"
"C5335E346F591BC4D07A7D5076189EDFB68F05228E023082010A02820101"
"00CC1715C81AD3F6F279C686F826E6D7C8961EB13318367D06B4061BBC57"
@@ -900,15 +898,16 @@ LicenseInfo license_update_test_data[] = {
"32610802123B0A190A09393837363534333231120892BE96420F0D5BF320"
"02280112001A16200342120A106B63746C0000000071FEF30B0000000020"
"F4DFB68F051A2000351030900858FCFD6977B67803ADFD1280AA661E6B0B"
"D30B08B2C4673551293A29687474703A2F2F68616D69642E6B69722E636F"
"72702E676F6F676C652E636F6D3A383838382F64726D1220E9BF6AE79B64"
"B788838B5EDDEBEF9E20FD8CFFDEB037DEFEE982DF21A2D32031")},
"D30B08B2C4673551293A2E68747470733A2F2F746573742E676F6F676C65"
"2E636F6D2F6C6963656E73652F47657443656E634C6963656E736512200A"
"1C78D0E574D0827C3AE78A05EEC90BAC31D10686EC19EB0599F75B2D1AB4"
"C5"
)},
// license being released. all fields are identical except for license
// state and hashed file data
{"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "",
wvcdm::a2bs_hex(
"0A9F15080210012298150802121408011210303132333435363738394142"
"0AA41508021001229D150802121408011210303132333435363738394142"
"434445461A9D0E080112950C0AD70B080112EF090AB002080212103E560E"
"C5335E346F591BC4D07A7D5076189EDFB68F05228E023082010A02820101"
"00CC1715C81AD3F6F279C686F826E6D7C8961EB13318367D06B4061BBC57"
@@ -997,9 +996,10 @@ LicenseInfo license_update_test_data[] = {
"32610802123B0A190A09393837363534333231120892BE96420F0D5BF320"
"02280112001A16200342120A106B63746C0000000071FEF30B0000000020"
"F4DFB68F051A2000351030900858FCFD6977B67803ADFD1280AA661E6B0B"
"D30B08B2C4673551293A29687474703A2F2F68616D69642E6B69722E636F"
"72702E676F6F676C652E636F6D3A383838382F64726D1220003650AD34C0"
"CB1D348F83B6694E4983DA8BCF9AEFAA4A4B23022DA08CF3DA44")}};
"D30B08B2C4673551293A2E68747470733A2F2F746573742E676F6F676C65"
"2E636F6D2F6C6963656E73652F47657443656E634C6963656E736512208F"
"7186A244EF561E3B07DC459BC681A0798B180667EA448327F6BBBD30212A"
"49")}};
} // namespace

View File

@@ -45,10 +45,10 @@ class FileTest : public testing::Test {
TEST_F(FileTest, FileExists) {
File file;
EXPECT_TRUE(file.Exists(test_vectors::kFileExists));
EXPECT_TRUE(file.Exists(test_vectors::kDirExists));
EXPECT_FALSE(file.Exists(test_vectors::kFileDoesNotExist));
EXPECT_FALSE(file.Exists(test_vectors::kDirDoesNotExist));
EXPECT_TRUE(file.Exists(test_vectors::kExistentFile));
EXPECT_TRUE(file.Exists(test_vectors::kExistentDir));
EXPECT_FALSE(file.Exists(test_vectors::kNonExistentFile));
EXPECT_FALSE(file.Exists(test_vectors::kNonExistentDir));
}
TEST_F(FileTest, CreateDirectory) {

View File

@@ -8,7 +8,7 @@
#include "url_request.h"
namespace {
// Random URL for tests.
// Arbitrary URL for tests.
const std::string kHttpsTestServer("https://www.google.com");
std::string gTestServer(kHttpsTestServer);
std::string gTestData("Hello");
@@ -123,7 +123,7 @@ TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) {
EXPECT_TRUE(domain_name_.empty());
EXPECT_TRUE(resource_path_.empty());
// Test with random numeric URL
// Test with arbitrary numeric URL
socket_.GetDomainNameAndPathFromUrl("http://10.11.12.13:8888/drm",
domain_name_, resource_path_);
EXPECT_STREQ("10.11.12.13", domain_name_.c_str());

View File

@@ -0,0 +1,150 @@
// Copyright 2012 Google Inc. All Rights Reserved.
#include "crypto_session.h"
#include "license.h"
#include "gtest/gtest.h"
#include "initialization_data.h"
#include "policy_engine.h"
#include "string_conversions.h"
namespace {
// The test data is based on key box Eureka-Dev-G1-0001520
// This unit test should run on oemcrypto mock with the same key box
static const char* kInitData = "0801121093789920E8D6520098577DF8F2DD5546";
static const char* kSignedRequest =
"080112790A4C0800124800000002000001241F344DB9DFF087F01D917910F39B"
"60DC7797CD97789EE82516DC07A478CB70B8C08C299293150AA8E01D2DC9808C"
"98DAA16E40A0E55DFE3618C7584DD3C7BE4212250A230A140801121093789920"
"E8D6520098577DF8F2DD554610011A09393837363534333231180120001A20FA"
"E2DDCD7F1ACA4B728EC957FEE802F8A5541557ACA784EE0D05BFCC0E65FEA1";
static const char* kValidResponse =
"080212D9020A190A093938373635343332311208C434AB9240A9EF2420012800"
"120E0801180120809A9E0128809A9E011A461210B72EEBF582B04BDB15C2E0E3"
"20B21C351A30E51FC1D27F70DB8E0DDF8C051BD6E251A44599DBCE4E1BE663FD"
"3AFAB191A7DD5736841FB04CE558E7F17BD9812A2DBA20011A6E0A1093789920"
"E8D6520098577DF8F2DD55461210367E8714B6F10087AFDE542EDC5C91541A20"
"ED51D4E84D81C8CBD8E2046EE079F8A2016268A2F192B902FDA241FEEB10C014"
"200242240A109209D46191B8752147C9F6A1CE2BEE6E12107910F39B60DC7797"
"CD97789EE82516DC1A6E0A107B1328EB61B554E293F75B1E3E94CC3B1210676F"
"69BBDA35EE972B77BC1328A087391A20D2B9FA92B164F5F6362CAD9200A11661"
"B8F71E9CE671A3A252D34586526B68FA200242240A109D7B13420FD6217666CC"
"CD43860FAA3A1210DBCE4E1BE663FD3AFAB191A7DD57368420E9FDCE86051A20"
"C6279E32FD2CB9067229E87AFF4B2DE14A077CDF8F061DAEE2CC2D1BCDEF62D0";
static const char* kInvalidResponse =
"0802128D020A190A093938373635343332311208BA68C949396C438C20012800"
"120E0801180120809A9E0128809A9E011A4612105021EB9AEDC1F73E96DE7DCC"
"6D7D72401A300A82E118C0BF0DB230FCADE3F49A9777DDD392322240FEF32C97"
"F85428E2F6CCFA638B5481464ADBCF199CEC2FCF3AFB20011A480A1093789920"
"E8D6520098577DF8F2DD55461210EE52C59B99050A36E10569AFB34D1DA41A20"
"C61FCB8019AC9ADE99FF8FCA99ED35E2331B6488A35102F9379AA42C87A22DC7"
"20021A480A107B1328EB61B554E293F75B1E3E94CC3B12101BBF5286B859E349"
"2E4A47A24C06AC1B1A2061F21836A04E558BEE0244EF41C165F60CF23C580275"
"3175D48BAF1C6CA5759F200220A2BCCA86051A203FD4671075D9DEC6486A9317"
"70669993306831EDD57D77F34EFEB467470BA364";
const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm";
}
namespace wvcdm {
class LicenseTest : public ::testing::Test {
protected:
virtual void SetUp() {
session_ = new CryptoSession();
EXPECT_TRUE(session_ != NULL);
std::string token;
EXPECT_TRUE(session_->GetToken(&token));
EXPECT_TRUE(session_->Open());
EXPECT_TRUE(license_.Init(token, session_, &policy_engine_));
}
virtual void TearDown() {
session_->Close();
delete session_;
}
CryptoSession* session_;
CdmLicense license_;
PolicyEngine policy_engine_;
};
TEST(LicenseTestSession, InitNullSession) {
CdmLicense license;
EXPECT_FALSE(license.Init("Dummy", NULL, NULL));
}
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_PrepareIsoBmffKeyRequest) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;
CdmSessionId session_id;
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
}
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_PrepareWebmKeyRequest) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;
CdmSessionId session_id;
InitializationData init_data(kWebmMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
}
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
std::string signed_request;
CdmAppParameterMap app_parameters;
CdmSessionId session_id;
std::string server_url;
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse)));
}
// TODO(rfrias): Fix or remove test.
TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
std::string signed_request;
CdmAppParameterMap app_parameters;
CdmSessionId session_id;
std::string server_url;
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
EXPECT_FALSE(license_.HandleKeyResponse(a2bs_hex(kInvalidResponse)));
}
// TODO(kqyang): add unit test cases for PrepareKeyRenewalRequest
// and HandleRenewalKeyResponse
}