Merges to android Pi release (part 8)

These are a set of CLs merged from the wv cdm repo to the android
	   repo.

* Android build fixes

  Author: Rahul Frias <rfrias@google.com>

  [ Merge of http://go/wvgerrit/36322 ]

* Address android compilation errors and warnings

  Author: Rahul Frias <rfrias@google.com>

  [ Merge of http://go/wvgerrit/36300 ]

* Gyp cleanup and OpenSSL v10.1 support.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/36001 ]

  OpenSSL 10.1 has a small number of incompatible changes.

  A desktop system upgrade exposed some issue in the build scripts.
  Specifically, the linux build was using both third_party/protobufs (2.6.1)
  and the version installed on the system (3.0 in this case). The linux
  cdm.gyp depended on cdm/cdm.gyp which caused that plus some
  additional issues.

  These changes are necessary to support g++ version:
    g++ (Debian 6.3.0-18) 6.3.0 20170516

  Also did some cosmetic rework on run_current_tests to make it easier
  to figure out what is going on when something fails.

  Also tweaked some of the compiler settings for g++ support (revisit
  this later).

* Refactored Service Certificate encryption to allow encryption of arbitrary data.

  Author: Thomas Inskip <tinskip@google.com>

  [ Merge of http://go/wvgerrit/36141 ]

* Send cdm test requests to UAT.

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/36221 ]

  This change resolves the all of the
  CdmDecryptTest/CdmTestWithDecryptParam.DecryptToClearBuffer
  tests.

  The license servers will return different keys and keyids.
  Sending the request to staging returned key ids and keys that were
  not matching what was expected in the unit tests.

* Fix for building L3 OEMCrypto with clang and libc++

  Author: yucliu <yucliu@google.com>

  [ Merge of http://go/wvgerrit/35740 ]

  1. Include <time.h> for time(time_t*).
  2. Create endian check union on stack. Clang may create const union
  somewhere else, which may cause crash.

* Remove error result when a sublicense session does
  not exist. This is not considered an error.

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/36080 ]

* Set default mock handler for GetSupportedCertificateTypes
  for all unit tests and removed the use of StrictMock from
  MockCryptoSession.

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/35922 ]

  The handler for this was only set for one test and resulted
  in a number of failures.

* Set default handler for GetHdcpCapabilities. For
  now the default action is to call the real
  GetHdcpCapabilities of crypto_session.

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/36140 ]

  I also changed the mock to a NiceMock to silence
  responses to unexpected calls to GetHdcpCapabilities.

  The default handler can be overridden as needed in
  the individual tests.

  This resolves the policy engine test failures.

* Finalize merge of cdm_partner_3.4 to master.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/35360 ]

  This is the final set of updates to merge all v3.4.1
  changes into master.

* Embedded license: Sublicense rotation.

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/35360 ]

  Handle sublicense rotation event.

* Embedded license: Initial license phase.

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/34280 ]

  Initial license phase - key loading subsession.

* Embedded license: generate session data.

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/33722 ]

  Generate session data and add it to the license request for
  any embedded license material.

* Resolve missing symbol when building cd-cdm

  Author: Jeff Fore <jfore@google.com>

  [ Merge of http://go/wvgerrit/35840 ]

* C++11: Replace OVERRIDE def with override keyword

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/35400 ]

BUG: 71650075
Test: Not currently passing. Will be addressed in a subsequent
      commit in the chain.

Change-Id: I37d0cb17f255ac6389030047d616ad69f895748c
This commit is contained in:
Rahul Frias
2018-01-10 15:33:26 -08:00
parent 80659961ac
commit 1884cf738e
52 changed files with 1641 additions and 617 deletions

View File

@@ -17,7 +17,7 @@ LOCAL_C_INCLUDES += \
LOCAL_HEADER_LIBRARIES := \
libutils_headers
LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static
LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto
SRC_DIR := src
CORE_SRC_DIR := core/src

View File

@@ -17,6 +17,9 @@ class CryptoKey {
const std::string& key_data_iv() const { return key_data_iv_; }
const std::string& key_control() const { return key_control_; }
const std::string& key_control_iv() const { return key_control_iv_; }
const std::string& sub_session_key_id() const {return sub_session_key_id_;}
const std::string& sub_session_key() const {return sub_session_key_;}
const std::string& track_label() const { return track_label_; }
CdmCipherMode cipher_mode() const { return cipher_mode_; }
void set_key_id(const std::string& key_id) { key_id_ = key_id; }
void set_key_data(const std::string& key_data) { key_data_ = key_data; }
@@ -28,6 +31,15 @@ class CryptoKey {
void set_cipher_mode(CdmCipherMode cipher_mode) {
cipher_mode_ = cipher_mode;
}
void set_sub_session_key_id(const std::string& sub_session_key_id) {
sub_session_key_id_ = sub_session_key_id;
}
void set_sub_session_key(const std::string& sub_session_key) {
sub_session_key_ = sub_session_key;
}
void set_track_label(const std::string& track_label) {
track_label_ = track_label;
}
bool HasKeyControl() const { return key_control_.size() >= 16; }
@@ -37,6 +49,9 @@ class CryptoKey {
std::string key_data_;
std::string key_control_;
std::string key_control_iv_;
std::string sub_session_key_id_;
std::string track_label_;
std::string sub_session_key_;
CdmCipherMode cipher_mode_;
};

View File

@@ -11,6 +11,8 @@
#include "lock.h"
#include "metrics_collections.h"
#include "oemcrypto_adapter.h"
#include "OEMCryptoCENC.h"
#include "scoped_ptr.h"
#include "timer_metric.h"
#include "wv_cdm_types.h"
@@ -22,6 +24,35 @@ class UsageTableHeader;
typedef std::map<std::string, CryptoKey*> CryptoKeyMap;
typedef std::map<std::string, CryptoSessionId> SubLicenseSessionMap;
class KeySession {
protected:
KeySession(metrics::CryptoMetrics* metrics) : metrics_(metrics) {}
public:
typedef enum { kDefault, kSubLicense } KeySessionType;
virtual ~KeySession() {}
virtual KeySessionType Type() = 0;
virtual bool GenerateDerivedKeys(const std::string& message) = 0;
virtual bool GenerateDerivedKeys(const std::string& message,
const std::string& session_key) = 0;
virtual OEMCryptoResult LoadKeys(const std::string& message,
const std::string& signature,
const std::string& mac_key_iv,
const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
CdmCipherMode* cipher_mode,
const std::string& srm_requirement) = 0;
virtual OEMCryptoResult SelectKey(const std::string& key_id) = 0;
virtual OEMCryptoResult Decrypt(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc& buffer_descriptor,
OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0;
protected:
metrics::CryptoMetrics* metrics_;
};
class CryptoSession {
public:
typedef OEMCrypto_HDCP_Capability HdcpCapability;
@@ -84,8 +115,6 @@ class CryptoSession {
virtual bool GenerateDerivedKeys(const std::string& message);
virtual bool GenerateDerivedKeys(const std::string& message,
const std::string& session_key);
virtual bool RewrapCertificate(const std::string& signed_message,
const std::string& signature,
const std::string& nonce,
@@ -95,7 +124,7 @@ class CryptoSession {
std::string* wrapped_private_key);
// Media data path
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& params);
// Usage related methods
virtual bool UsageInformationSupport(bool* has_support);
@@ -181,6 +210,10 @@ class CryptoSession {
virtual metrics::CryptoMetrics* GetCryptoMetrics() { return metrics_; }
virtual CdmResponseType AddSubSession(const std::string& sub_session_key_id);
// TODO(jfore): exists is set based on whether a sub session exists. For now,
// that is not assumed to be an error.
virtual bool GenerateSubSessionNonce(const std::string& sub_session_key_id,
bool* exists, uint32_t* nonce);
private:
bool GetProvisioningMethod(CdmClientTokenType* token_type);
@@ -188,13 +221,9 @@ class CryptoSession {
void Terminate();
bool GetTokenFromKeybox(std::string* token);
bool GetTokenFromOemCert(std::string* token);
void GenerateMacContext(const std::string& input_context,
std::string* deriv_context);
void GenerateEncryptContext(const std::string& input_context,
std::string* deriv_context);
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();
bool RewrapDeviceRSAKey(
@@ -230,7 +259,7 @@ class CryptoSession {
static void IncrementIV(uint64_t increase_by, std::vector<uint8_t>* iv_out);
static const size_t kAes128BlockSize = 16; // Block size for AES_CBC_128
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
static Lock crypto_lock_;
static bool initialized_;
static int session_count_;
@@ -240,10 +269,13 @@ class CryptoSession {
bool open_;
CdmClientTokenType pre_provision_token_type_;
std::string oem_token_; // Cached OEMCrypto Public Key
std::string oem_token_; // Cached OEMCrypto Public Key
bool update_usage_table_after_close_session_;
CryptoSessionId oec_session_id_;
SubLicenseSessionMap sub_license_oec_sessions_;
// Used for sub license sessions.
std::string wrapped_key_;
scoped_ptr<KeySession> key_session_;
OEMCryptoBufferType destination_buffer_type_;
bool is_destination_buffer_type_valid_;

View File

@@ -22,6 +22,7 @@ class CryptoSession;
class PolicyEngine;
class ServiceCertificate;
class CdmSession;
class CryptoKey;
class CdmLicense {
public:
@@ -45,6 +46,7 @@ class CdmLicense {
const CdmKeyResponse& license_response);
virtual CdmResponseType HandleKeyUpdateResponse(
bool is_renewal, const CdmKeyResponse& license_response);
virtual CdmResponseType HandleSubLicense(const InitializationData& init_data);
virtual bool RestoreOfflineLicense(
const CdmKeyMessage& license_request,
@@ -113,6 +115,11 @@ class CdmLicense {
// For testing
// CdmLicense takes ownership of the clock.
CdmLicense(const CdmSessionId& session_id, Clock* clock);
// For sublicense key embedding. This key array will be initilized with any
// sub session keys we may have received in a license response. These keys
// may be used to support key rotation.
std::vector<CryptoKey> sub_session_key_array_;
#if defined(UNIT_TEST)
friend class CdmLicenseTest;
#endif

View File

@@ -50,6 +50,8 @@ class PolicyEngine {
// permits playback.
virtual void SetLicense(const video_widevine::License& license);
virtual void UpdateLicenseKeys(const video_widevine::License& license);
// SetLicenseForRelease is used when releasing a license. The keys in this
// license will be ignored, and any old keys will be expired.
virtual void SetLicenseForRelease(

View File

@@ -109,6 +109,7 @@ class Properties {
FRIEND_TEST(CdmSessionTest, InitFailCryptoError);
FRIEND_TEST(CdmSessionTest, InitNeedsProvisioning);
FRIEND_TEST(CdmLicenseTest, PrepareKeyRequestValidation);
FRIEND_TEST(SubLicenseTest, VerifySubSessionData);
#endif
private:

View File

@@ -38,6 +38,13 @@ class ServiceCertificate {
virtual CdmResponseType VerifySignedMessage(const std::string& message,
const std::string& signature);
// Encrypt data using RSA with OAEP padding.
// |plaintext| is the data to be encrypted. |ciphertext| is a pointer to a
// string to contain the decrypted data on return, and may not be null.
// returns NO_ERROR if successful or an appropriate error code otherwise.
virtual CdmResponseType EncryptRsaOaep(const std::string& plaintext,
std::string* ciphertext);
// Encrypt the ClientIdentification message for a provisioning or
// licensing request. Encryption is performed using the current
// service certificate. Return a failure if the service certificate is

View File

@@ -318,6 +318,7 @@ enum CdmResponseType {
USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE,
RELEASE_ALL_USAGE_INFO_ERROR_6, /* 275 */
RELEASE_ALL_USAGE_INFO_ERROR_7,
LICENSE_REQUEST_INVALID_SUBLICENSE,
};
enum CdmKeyStatus {
@@ -341,6 +342,7 @@ enum CdmLicenseType {
// Like Streaming, but stricter. Does not permit storage of any kind.
// Named after the 'temporary' session type in EME, which has this behavior.
kLicenseTypeTemporary,
kLicenseTypeSubSession
};
enum SecurityLevel {

View File

@@ -245,7 +245,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(
}
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::CloseSession");
LOGV("CdmEngine::CloseSession: %s", session_id.c_str());
if (!session_map_.CloseSession(session_id)) {
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
return SESSION_NOT_FOUND_1;

View File

@@ -363,6 +363,8 @@ CdmResponseType CdmSession::GenerateKeyRequest(
case kLicenseTypeRelease:
is_release_ = true;
break;
case kLicenseTypeSubSession:
return license_parser_->HandleSubLicense(init_data);
default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type);
@@ -390,6 +392,17 @@ CdmResponseType CdmSession::GenerateKeyRequest(
return KEY_REQUEST_ERROR_1;
}
std::vector<video_widevine::SubLicense> embedded_key_data =
init_data.ExtractEmbeddedKeys();
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
CdmResponseType sts = crypto_session_->AddSubSession(
embedded_key_data[i].sub_session_key_id());
if (NO_ERROR != sts) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate sub session");
return sts;
}
}
app_parameters_ = app_parameters;
CdmResponseType status = license_parser_->PrepareKeyRequest(
init_data, license_type,
@@ -450,12 +463,13 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
!provider_session_token.empty() &&
usage_table_header_ != nullptr) {
if (sts != KEY_ADDED) {
CdmResponseType sts =
CdmResponseType delete_sts =
usage_table_header_->DeleteEntry(usage_entry_number_,
file_handle_.get(),
crypto_metrics_);
if (sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d", sts);
if (delete_sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d",
delete_sts);
}
}
}
@@ -891,8 +905,11 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (usage_support_type_ != kUsageEntrySupport ||
!has_provider_session_token() ||
usage_table_header_ == nullptr) {
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected usage type "
"supported: %d", usage_support_type_);
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected state, "
"usage support type: %d, PST present: %s, usage table header available"
": %s", usage_support_type_,
has_provider_session_token() ? "yes" : "no",
usage_table_header_ == nullptr ? "no" : "yes");
return INCORRECT_USAGE_SUPPORT_TYPE_2;
}

View File

@@ -83,7 +83,8 @@ using video_widevine::SignedProvisioningMessage;
*/
bool CertificateProvisioning::GetProvisioningTokenType(
ClientIdentification::TokenType* token_type) {
switch (crypto_session_.GetPreProvisionTokenType()) {
CdmClientTokenType token = crypto_session_.GetPreProvisionTokenType();
switch (token) {
case kClientTokenKeybox:
*token_type = ClientIdentification::KEYBOX;
return true;
@@ -93,6 +94,8 @@ bool CertificateProvisioning::GetProvisioningTokenType(
case kClientTokenDrmCert:
default:
// shouldn't happen
LOGE("CertificateProvisioning::GetProvisioningTokenType: unexpected "
"provisioning type: %d", token);
return false;
}
}
@@ -107,7 +110,7 @@ bool CertificateProvisioning::SetSpoidParameter(
const std::string& origin, const std::string& spoid,
ProvisioningRequest* request) {
if (!request) {
LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer "
LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer "
"passed to method.");
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,10 +14,6 @@
namespace {
const char kKeyFormatVersionsSeparator = '/';
const char kColon = ':';
const char kDoubleQuote = '\"';
const char kLeftBracket = '[';
const char kRightBracket = ']';
const std::string kBase64String = "base64,";
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;

View File

@@ -36,7 +36,6 @@ const uint32_t kFourCcCbcs = 0x63626373;
const uint32_t kFourCcLittleEndianCbc1 = 0x31636263;
const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
const uint32_t kFourCcCenc = 0x63656e63;
const uint32_t kFourCcCens = 0x63656e73;
} // namespace
@@ -49,17 +48,41 @@ using video_widevine::ClientIdentification_NameValue;
using video_widevine::DrmDeviceCertificate;
using video_widevine::EncryptedClientIdentification;
using video_widevine::License;
using video_widevine::License_KeyContainer;
using video_widevine::LicenseError;
using video_widevine::LicenseIdentification;
using video_widevine::LicenseRequest;
using video_widevine::LicenseRequest_ContentIdentification;
using video_widevine::LicenseRequest_ContentIdentification_CencDeprecated;
using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated;
using video_widevine::LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated;
using video_widevine::License_KeyContainer;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::SignedMessage;
static std::vector<CryptoKey> ExtractSubSessionKeys(const License& license) {
std::vector<CryptoKey> key_array;
// Extract sub session key(s)
for (int i = 0; i < license.key_size(); ++i) {
CryptoKey key;
switch (license.key(i).type()) {
case License_KeyContainer::SUB_SESSION:
key.set_key_data(license.key(i).key());
key.set_key_data_iv(license.key(i).iv());
key.set_key_id(license.key(i).id());
key.set_track_label(license.key(i).track_label());
key_array.push_back(key);
break;
default:
// Ignore all but SUB_SESSION key types.
break;
}
}
return key_array;
}
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> key_array;
@@ -88,6 +111,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
if (license.has_protection_scheme()) {
four_cc = license.protection_scheme();
}
key.set_track_label(license.key(i).track_label());
switch (four_cc) {
// b/30713238: Android N assumed that the "protection scheme" Four
// CC code, after being extracted from the protobuf, was host byte
@@ -116,11 +140,28 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
}
break;
default:
// Ignore SIGNING key types as they are not content related
// Ignore SIGNING and SUB_SESSION key types as they are not content
// related.
break;
}
}
std::vector<CryptoKey> sub_session_keys = ExtractSubSessionKeys(license);
// Match the track label from the key arrays and add sub_license_key_id to
// the content key array.
LOGV("Received %d subsession keys", sub_session_keys.size());
if (!sub_session_keys.empty()) {
for (size_t i = 0; i < key_array.size(); ++i) {
if (key_array[i].track_label().empty()) continue;
for (size_t x = 0; x < sub_session_keys.size(); ++x) {
if (sub_session_keys[x].track_label() == key_array[i].track_label()) {
key_array[i].set_sub_session_key_id(sub_session_keys[x].key_id());
key_array[i].set_sub_session_key(sub_session_keys[x].key_data());
}
}
}
}
return key_array;
}
@@ -222,8 +263,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
status = PrepareClientId(app_parameters, &license_request);
if (NO_ERROR != status) return status;
status = PrepareContentId(init_data, license_type, request_id,
&license_request);
status =
PrepareContentId(init_data, license_type, request_id, &license_request);
if (NO_ERROR != status) return status;
license_request.set_type(LicenseRequest::NEW);
@@ -238,6 +279,37 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
}
license_request.set_key_control_nonce(nonce);
LOGD("PrepareKeyRequest: nonce=%u", nonce);
// Prepare the request for any embedded keys that may exist in the
// initialization data.
std::vector<video_widevine::SubLicense> embedded_key_data =
init_data.ExtractEmbeddedKeys();
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
bool exists = false;
if (!crypto_session_->GenerateSubSessionNonce(
embedded_key_data[i].sub_session_key_id(), &exists, &nonce)) {
if (exists) {
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
}
SignedMessage signed_sub_license;
License_KeyContainer keyc;
// Parse the sub license for this track to extract the label.
if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) ||
!keyc.ParseFromString(signed_sub_license.msg()) ||
keyc.track_label().empty()) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
}
LicenseRequest::SubSessionData* sub_session_data =
license_request.add_sub_session_data();
sub_session_data->set_sub_session_key_id(
embedded_key_data[i].sub_session_key_id());
sub_session_data->set_nonce(nonce);
sub_session_data->set_track_label(keyc.track_label());
}
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
@@ -333,7 +405,8 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
}
// TODO(rfrias): Refactor to avoid needing to call CdmSession
if (cdm_session) {
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return status;
}
@@ -446,7 +519,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
LOGE("CdmLicense::HandleKeyResponse: no session keys present");
return SESSION_KEYS_NOT_FOUND;
}
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return GENERATE_DERIVED_KEYS_ERROR;
@@ -595,6 +667,48 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
}
}
CdmResponseType CdmLicense::HandleSubLicense(
const InitializationData& init_data) {
std::vector<video_widevine::SubLicense> subkeys =
init_data.ExtractEmbeddedKeys();
std::set<KeyId> loaded_keys;
// Build a license with the rotated keys.
License license;
for (size_t i = 0; i < subkeys.size(); ++i) {
SignedMessage sm;
if (!sm.ParseFromString(subkeys[i].key_msg())) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
}
License_KeyContainer keyc;
if (!keyc.ParseFromString(sm.msg())) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
}
std::vector<CryptoKey> keys;
keys.resize(1);
keys[0].set_key_id(keyc.id());
keys[0].set_key_data(keyc.key());
keys[0].set_key_data_iv(keyc.iv());
keys[0].set_key_control(keyc.key_control().key_control_block());
keys[0].set_key_control_iv(keyc.key_control().iv());
keys[0].set_track_label(keyc.track_label());
//TODO: passing empty cipher_mode and srm_req params - OK?
CdmResponseType result = crypto_session_->LoadKeys(
sm.msg(), sm.signature(), std::string(), std::string(), keys,
std::string(), std::string());
if (result != KEY_ADDED) {
LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d",
result);
return result;
}
loaded_keys.insert(keyc.id());
*license.add_key() = keyc;
}
loaded_keys_.swap(loaded_keys);
policy_engine_->UpdateLicenseKeys(license);
return KEY_MESSAGE;
}
bool CdmLicense::RestoreOfflineLicense(
const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
@@ -642,7 +756,8 @@ bool CdmLicense::RestoreOfflineLicense(
}
if (!provider_session_token_.empty()) {
if (cdm_session) {
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return false;
}
@@ -1067,8 +1182,7 @@ CdmResponseType CdmLicense::PrepareContentId(
template <typename T>
bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
const std::string& request_id,
T* content_id) {
const std::string& request_id, T* content_id) {
switch (license_type) {
case kLicenseTypeOffline:
content_id->set_license_type(video_widevine::OFFLINE);

View File

@@ -18,7 +18,6 @@ namespace {
const int kCdmPolicyTimerDurationSeconds = 1;
const int kClockSkewDelta = 5; // seconds
const int64_t kHdcpCheckInterval = 10;
} // namespace
@@ -169,6 +168,14 @@ void PolicyEngine::SetLicense(const License& license) {
UpdateLicense(license);
}
void PolicyEngine::UpdateLicenseKeys(const video_widevine::License& license) {
// Use the current policy and set the new keys.
video_widevine::License loadable = license;
loadable.mutable_policy()->CopyFrom(policy_);
license_keys_->SetFromLicense(loadable);
NotifyKeysChange(kKeyStatusUsable);
}
void PolicyEngine::SetLicenseForRelease(const License& license) {
license_id_.Clear();
license_id_.CopyFrom(license.id());

View File

@@ -88,38 +88,60 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
return false;
}
EVP_CIPHER_CTX ctx;
if (EVP_EncryptInit(&ctx, EVP_aes_128_cbc(),
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX ctx_struct;
EVP_CIPHER_CTX* evp_cipher_ctx = &ctx_struct;
#else
EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new();
#endif
if (EVP_EncryptInit(evp_cipher_ctx, EVP_aes_128_cbc(),
reinterpret_cast<uint8_t*>(&key_[0]),
reinterpret_cast<uint8_t*>(&(*iv)[0])) == 0) {
LOGE("AesCbcKey::Encrypt: AES CBC setup failure: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
return false;
}
out->resize(in.size() + AES_BLOCK_SIZE);
int out_length = out->size();
if (EVP_EncryptUpdate(
&ctx, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
evp_cipher_ctx, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
reinterpret_cast<uint8_t*>(const_cast<char*>(in.data())),
in.size()) == 0) {
LOGE("AesCbcKey::Encrypt: encryption failure: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
return false;
}
int padding = 0;
if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
if (EVP_EncryptFinal_ex(evp_cipher_ctx,
reinterpret_cast<uint8_t*>(&(*out)[out_length]),
&padding) == 0) {
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
return false;
}
EVP_CIPHER_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
out->resize(out_length + padding);
return true;
}
@@ -195,11 +217,16 @@ static int LogOpenSSLError(const char* msg, size_t /* len */, void* /* ctx */) {
static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
const std::string &signature) {
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_MD_CTX ctx_struct;
EVP_MD_CTX* evp_md_ctx = &ctx_struct;
EVP_MD_CTX_init(evp_md_ctx);
#else
EVP_MD_CTX* evp_md_ctx = EVP_MD_CTX_new();
#endif
EVP_PKEY_CTX *pctx = NULL;
if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */,
if (EVP_DigestVerifyInit(evp_md_ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */,
pkey) != 1) {
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
goto err;
@@ -221,13 +248,13 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
goto err;
}
if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) {
if (EVP_DigestVerifyUpdate(evp_md_ctx, message.data(), message.size()) != 1) {
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyFinal(
&ctx, const_cast<uint8_t *>(
evp_md_ctx, const_cast<uint8_t *>(
reinterpret_cast<const uint8_t *>(signature.data())),
signature.size()) != 1) {
LOGE(
@@ -236,12 +263,20 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
goto err;
}
EVP_MD_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_MD_CTX_cleanup(evp_md_ctx);
#else
EVP_MD_CTX_free(evp_md_ctx);
#endif
return true;
err:
ERR_print_errors_cb(LogOpenSSLError, NULL);
EVP_MD_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_MD_CTX_cleanup(evp_md_ctx);
#else
EVP_MD_CTX_free(evp_md_ctx);
#endif
return false;
}

View File

@@ -200,14 +200,22 @@ CdmResponseType ServiceCertificate::VerifySignedMessage(
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
CdmResponseType ServiceCertificate::EncryptRsaOaep(const std::string& plaintext,
std::string* ciphertext) {
if (!public_key_) {
LOGE("Service certificate not set.");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (!public_key_->Encrypt(plaintext, ciphertext))
return CLIENT_ID_RSA_ENCRYPT_ERROR;
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
encrypted_client_id->set_provider_id(provider_id_);
encrypted_client_id->set_service_certificate_serial_number(serial_number_);
@@ -227,8 +235,9 @@ CdmResponseType ServiceCertificate::EncryptClientId(
if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR;
if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR;
if (!public_key_->Encrypt(key, &enc_key))
return CLIENT_ID_RSA_ENCRYPT_ERROR;
CdmResponseType encrypt_result = EncryptRsaOaep(key, &enc_key);
if (encrypt_result != NO_ERROR)
return encrypt_result;
encrypted_client_id->set_encrypted_client_id_iv(iv);
encrypted_client_id->set_encrypted_privacy_key(enc_key);

View File

@@ -225,7 +225,7 @@ class WvCdmEnginePreProvTestStaging : public WvCdmEnginePreProvTest {
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingLicense);
CommonSetup(kContentProtectionStagingServer);
}
};
@@ -237,7 +237,7 @@ class WvCdmEnginePreProvTestProd : public WvCdmEnginePreProvTest {
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionProdLicense);
CommonSetup(kContentProtectionProductionServer);
}
};
@@ -249,32 +249,20 @@ class WvCdmEnginePreProvTestUat : public WvCdmEnginePreProvTest {
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionUatLicense);
CommonSetup(kContentProtectionUatServer);
}
};
class WvCdmEnginePreProvTestStagingProv30 : public WvCdmEnginePreProvTest {
class WvCdmEnginePreProvTestUatBinary : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStagingProv30() {}
WvCdmEnginePreProvTestUatBinary() {}
virtual ~WvCdmEnginePreProvTestStagingProv30() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingPlusProv30);
}
};
class WvCdmEnginePreProvTestStagingProv30Binary : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStagingProv30Binary() {}
virtual ~WvCdmEnginePreProvTestStagingProv30Binary() {}
virtual ~WvCdmEnginePreProvTestUatBinary() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
// Override default setting of provisioning_messages_are_binary property
CommonSetup(kContentProtectionUatPlusProv30, true);
CommonSetup(kContentProtectionUatServer, true);
}
protected:
@@ -340,25 +328,13 @@ class WvCdmEnginePreProvTestStagingProv30Binary : public WvCdmEnginePreProvTest
};
class WvCdmEnginePreProvTestUatProv30 : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestUatProv30() {}
virtual ~WvCdmEnginePreProvTestUatProv30() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingPlusProv30);
}
};
class WvCdmEngineTest : public WvCdmEnginePreProvTest {
public:
WvCdmEngineTest() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingLicense);
CommonSetup(kContentProtectionStagingServer);
}
virtual void SetUp() {
@@ -602,11 +578,7 @@ TEST_F(WvCdmEnginePreProvTestStaging, DISABLED_ProvisioningTest) {
Provision();
}
TEST_F(WvCdmEnginePreProvTestStagingProv30, ProvisioningTest) {
Provision();
}
TEST_F(WvCdmEnginePreProvTestStagingProv30Binary, ProvisioningTest) {
TEST_F(WvCdmEnginePreProvTestUatBinary, DISABLED_ProvisioningTest) {
Provision();
}

View File

@@ -15,11 +15,104 @@ namespace {
const std::string kWidevineKeySystem = "com.widevine.alpha";
// For staging servers
// NOTE: This matches the service cert returned by the staging
// server. This is the one that the staging provisioning server uses.
// Content Protection license server (Production) data
const std::string kCpProductionLicenseServer =
"https://widevine-proxy.appspot.com/proxy";
const std::string kCpProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// NOTE: Provider ID = staging.google.com
const std::string kCpProductionServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522"
"8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417"
"7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781"
"5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D"
"0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6"
"AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0"
"40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A"
"0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6"
"283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0"
"D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301"
"00013A1273746167696E672E676F6F676C652E636F6D128003983E303526"
"75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA"
"EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA"
"97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91"
"5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0"
"32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0"
"EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28"
"8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932"
"1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784"
"C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742"
"BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940"
"383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8"
"38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A"
"250A4EB9C84AB3E6539F6B6FDF56899EA29914";
// Content Protection license server (UAT) data
const std::string kCpUatLicenseServer =
"https://proxy.uat.widevine.com/proxy";
// TODO(rfrias): replace when b62880305 is addressed. For now use production URL
const std::string kCpUatProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// NOTE: Provider ID = staging.google.com
const std::string kCpUatServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522"
"8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417"
"7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781"
"5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D"
"0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6"
"AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0"
"40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A"
"0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6"
"283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0"
"D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301"
"00013A1273746167696E672E676F6F676C652E636F6D128003983E303526"
"75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA"
"EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA"
"97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91"
"5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0"
"32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0"
"EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28"
"8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932"
"1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784"
"C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742"
"BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940"
"383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8"
"38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A"
"250A4EB9C84AB3E6539F6B6FDF56899EA29914";
const std::string kCpClientAuth = "";
const std::string kCpKeyId =
"00000042" // blob size
"70737368" // "pssh"
"00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000022" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573" // "streaming_clip1"
"74220f73747265616d696e675f636c69"
"7031";
const std::string kCpOfflineKeyId =
"00000040" // blob size
"70737368" // "pssh"
"00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000020" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573" // "offline_clip2"
"74220d6f66666c696e655f636c697032";
// Content Protection license server (staging) data
const std::string kCpStagingLicenseServer =
"https://proxy.staging.widevine.com/proxy";
const std::string kCpStagingProvisioningServerUrl =
"https://staging-www.sandbox.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// NOTE: Provider ID = license.widevine.com
const std::string kStagingServiceCertificate =
const std::string kCpStagingServiceCertificate =
"0ac102080312101705b917cc1204868b06333a2f772a8c1882b482920522"
"8e023082010a028201010099ed5b3b327dab5e24efc3b62a95b598520ad5"
"bccb37503e0645b814d876b8df40510441ad8ce3adb11bb88c4e725a5e4a"
@@ -44,95 +137,25 @@ const std::string kStagingServiceCertificate =
"f9b4342cc8df543cb1a1182f7c5fff33f10490faca5b25360b76015e9c5a"
"06ab8ee02f00d2e8d5986104aacc4dd475fd96ee9ce4e326f21b83c70585"
"77b38732cddabc6a6bed13fb0d49d38a45eb87a5f4";
// NOTE: Provider ID = staging.google.com
const std::string kProdServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522"
"8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417"
"7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781"
"5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D"
"0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6"
"AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0"
"40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A"
"0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6"
"283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0"
"D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301"
"00013A1273746167696E672E676F6F676C652E636F6D128003983E303526"
"75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA"
"EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA"
"97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91"
"5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0"
"32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0"
"EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28"
"8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932"
"1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784"
"C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742"
"BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940"
"383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8"
"38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A"
"250A4EB9C84AB3E6539F6B6FDF56899EA29914";
// For UAT License servers
// NOTE: This matches the service cert returned by the UAT server.
// NOTE: Provider ID = staging.google.com
const std::string kUatServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522"
"8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417"
"7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781"
"5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D"
"0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6"
"AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0"
"40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A"
"0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6"
"283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0"
"D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301"
"00013A1273746167696E672E676F6F676C652E636F6D128003983E303526"
"75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA"
"EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA"
"97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91"
"5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0"
"32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0"
"EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28"
"8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932"
"1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784"
"C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742"
"BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940"
"383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8"
"38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A"
"250A4EB9C84AB3E6539F6B6FDF56899EA29914";
// Content Protection license server (Production) data
const std::string kCpProdLicenseServer =
"https://widevine-proxy.appspot.com/proxy";
// Content Protection license server (UAT) data
const std::string kCpUatLicenseServer =
"https://proxy.uat.widevine.com/proxy";
const std::string kCpClientAuth = "";
const std::string kCpKeyId =
"00000042" // blob size
const CdmInitData kCpStagingSrmOuputProtectionRequired =
"0000003d" // blob size
"70737368" // "pssh"
"00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000022" // pssh data size
"0000001d" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573"
"74220f73747265616d696e675f636c69"
"7031";
const std::string kCpOfflineKeyId =
"00000040" // blob size
"74220a74656172735f73726d32"; // "tears_srm2"
const CdmInitData kCpStagingSrmOuputProtectionRequested =
"0000003d" // blob size
"70737368" // "pssh"
"00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000020" // pssh data size
"0000001d" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573"
"74220d6f66666c696e655f636c697032";
// Content Protection license server (staging) data
const std::string kCpStagingLicenseServer =
"https://proxy.staging.widevine.com/proxy";
"74220a74656172735f73726d32"; // "tears_srm1"
const CdmInitData kEmptyData;
// Google Play license server data
const std::string kGpLicenseServer =
@@ -172,44 +195,18 @@ const std::string kWrongKeyId =
"0901121094889920e8d6520098577df8"
"f2dd5546";
// URL of provisioning server (overrides value from GetProvisioningRequest())
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const std::string kStagingProvisioningServerUrl =
"https://staging-www.sandbox.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, kStagingProvisioningServerUrl, ""},
{kContentProtectionProdLicense, kCpProdLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kProductionProvisioningServerUrl, kProdServiceCertificate},
{kContentProtectionUatLicense, kCpUatLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kProductionProvisioningServerUrl, kProdServiceCertificate},
{kContentProtectionStagingLicense, kCpStagingLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionProdPlusProv30, kCpProdLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionUatPlusProv30, kCpUatLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionStagingPlusProv30, kCpStagingLicenseServer,
kStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, kCpProductionProvisioningServerUrl, ""},
{kContentProtectionProductionServer, kCpProductionLicenseServer,
kCpProductionServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kCpProductionProvisioningServerUrl, kCpProductionServiceCertificate},
{kContentProtectionUatServer, kCpUatLicenseServer, kCpUatServiceCertificate,
kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kCpUatProvisioningServerUrl, kCpUatServiceCertificate},
{kContentProtectionStagingServer, kCpStagingLicenseServer,
kCpStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kCpStagingProvisioningServerUrl, kCpStagingServiceCertificate},
};
} // namespace
@@ -254,4 +251,35 @@ void ConfigTestEnv::Init(ServerConfigurationId server_id) {
wrong_key_id_ = kWrongKeyId;
}
const CdmInitData ConfigTestEnv::GetInitData(ContentId content_id) {
switch (content_id) {
case kContentIdStreaming:
return wvcdm::a2bs_hex(kCpKeyId);
case kContentIdOffline:
return wvcdm::a2bs_hex(kCpOfflineKeyId);
case kContentIdStagingSrmOuputProtectionRequested:
return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequested);
case kContentIdStagingSrmOuputProtectionRequired:
return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequired);
default:
return kEmptyData;
}
}
const std::string& ConfigTestEnv::GetLicenseServerUrl(
ServerConfigurationId server_configuration_id) {
switch (server_configuration_id) {
case kGooglePlayServer:
return kGpLicenseServer;
case kContentProtectionUatServer:
return kCpUatLicenseServer;
case kContentProtectionStagingServer:
return kCpStagingLicenseServer;
case kContentProtectionProductionServer:
return kCpProductionLicenseServer;
default:
return kEmptyData;
}
}
} // namespace wvcdm

View File

@@ -25,17 +25,24 @@
namespace wvcdm {
typedef enum {
kGooglePlayServer, // not tested recently
kContentProtectionProdLicense,
kContentProtectionUatLicense,
kContentProtectionStagingLicense,
kContentProtectionProdPlusProv30,
kContentProtectionUatPlusProv30,
kContentProtectionStagingPlusProv30,
kContentProtectionUatServer,
kContentProtectionStagingServer,
kContentProtectionProductionServer,
} ServerConfigurationId;
// Identifies content used in tests. Specify Prod/Uat/Staging if content
// has been registered across license services.
enum ContentId {
kContentIdStreaming,
kContentIdOffline,
kContentIdStagingSrmOuputProtectionRequested,
kContentIdStagingSrmOuputProtectionRequired,
};
// Configures default test environment.
class ConfigTestEnv {
public:
typedef struct {
ServerConfigurationId id;
std::string license_server_url;
@@ -68,6 +75,10 @@ class ConfigTestEnv {
}
const KeyId& wrong_key_id() const { return wrong_key_id_; }
static const CdmInitData GetInitData(ContentId content_id);
static const std::string& GetLicenseServerUrl(
ServerConfigurationId server_configuration_id);
void set_key_id(KeyId& key_id) { key_id_.assign(key_id); }
void set_key_system(CdmKeySystem& key_system) {
key_system_.assign(key_system);
@@ -75,6 +86,9 @@ class ConfigTestEnv {
void set_license_server(std::string& license_server) {
license_server_.assign(license_server);
}
void set_provisioning_server(std::string& provisioning_server) {
provisioning_server_.assign(provisioning_server);
}
private:
void Init(ServerConfigurationId server_id);

View File

@@ -2,6 +2,7 @@
#include "http_socket.h"
#include <cstring>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
@@ -36,13 +37,14 @@ bool Tokenize(const std::string& source, const std::string& delim,
}
SSL_CTX* InitSslContext() {
const SSL_METHOD* method;
SSL_CTX* ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = TLSv1_2_client_method();
ctx = SSL_CTX_new(method);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
const SSL_METHOD* method = TLSv1_2_client_method();
#else
const SSL_METHOD* method = TLS_client_method();
#endif
SSL_CTX* ctx = SSL_CTX_new(method);
if (!ctx) LOGE("failed to create SSL context");
int ret = SSL_CTX_set_cipher_list(
ctx, "ALL:!RC4-MD5:!RC4-SHA:!ECDHE-ECDSA-RC4-SHA:!ECDHE-RSA-RC4-SHA");

View File

@@ -3,6 +3,8 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include "clock.h"
#include "crypto_session.h"
#include "initialization_data.h"
@@ -79,6 +81,38 @@ const std::string kLicenseRequestSignature = a2bs_hex(
"9AE18B91516E0CDD0B581590DDDEA2A2527E2C9ABA273629B586A9D22D451A827E332CFC3E"
"9BEDB6CF3D8713F9E11675DF1F5DB9038DBBECAB9D1683F8722CAF6E18EC8C04AEE5");
const std::string kSubLicensePssh = a2bs_hex(
"000002317073736800000000edef8ba979d64acea3c827dcd51d21ed000002010801120d54"
"6573744b6579415544494f30120a546573744b6579534430120a546573744b65794844301a"
"0048e3dc959b0650025a9b010a101f10e4700b1a5b731c545fe2405cea1b12860112620a0d"
"546573744b6579415544494f3012102f4b661d1064b5ea82efcd3ef850f45b1a10a02c5cea"
"5182383c064c06abbc79bfa8200242240a1023acc9a0ef2bca66af2a1307cc9edeb21210e1"
"f1d352b4c6b1aad1fd78423db256946205415544494f1a20aaf4537f09332c502a88f43a18"
"a3e21ec28bbde675c5d87054fbca06f98e98015a95010a10c034cf6ae181b8d07f2e79142d"
"792bb3128001125c0a0a546573744b657953443012106d37a0a201afdef8a494f89a4b0772"
"4a1a10457db86b73bf87177a5cc61c0d04b690200242240a1001fe432d2a8afb7054ae76a3"
"9c2727e612108761b7e0ba354ee4132117a9de12abd3620253441a20e3f37529cb795b35a0"
"d186e4ce7187f08dda5f1df136ddb92eb0a65a899635005a95010a1021cdec9b2105c6b643"
"e71f68e5302c85128001125c0a0a546573744b6579484430121012a3e3afe1e23be2c3fc55"
"fddad877451a108f31ff0865f4d4fb41d96414297f7728200242240a1061e3cbca755b36c8"
"e7d6dda03af20e4f1210a2fa2fc5d10c9c84ddc5511446ce77e9620248441a20a945699aef"
"49355b0214b636edb7670bbe350c58b69cd52f207953b380a52df2");
const std::string kSubSessionKeyID1 =
a2bs_hex("1f10e4700b1a5b731c545fe2405cea1b");
const std::string kSubSessionKeyID2 =
a2bs_hex("c034cf6ae181b8d07f2e79142d792bb3");
const std::string kSubSessionKeyID3 =
a2bs_hex("21cdec9b2105c6b643e71f68e5302c85");
const CryptoSession::SupportedCertificateTypes kDefaultSupportedCertTypes = {
true,
true,
true
};
class MockCryptoSession : public CryptoSession {
public:
MockCryptoSession() : CryptoSession(NULL) { }
@@ -90,6 +124,9 @@ class MockCryptoSession : public CryptoSession {
MOCK_METHOD1(GetApiVersion, bool(uint32_t*));
MOCK_METHOD1(GenerateNonce, bool(uint32_t*));
MOCK_METHOD3(PrepareRequest, bool(const std::string&, bool, std::string*));
MOCK_METHOD3(GenerateSubSessionNonce,
bool(const std::string& sub_session_key_id, bool* exists,
uint32_t* nonce));
};
class MockPolicyEngine : public PolicyEngine {
@@ -129,12 +166,17 @@ using ::testing::UnorderedElementsAre;
class CdmLicenseTest : public ::testing::Test {
protected:
CdmLicenseTest(const std::string& pssh = (kCencInitDataHdr + kCencPssh))
: pssh_(pssh) {}
virtual void SetUp() {
clock_ = new MockClock();
crypto_session_ = new MockCryptoSession();
init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT,
kCencInitDataHdr + kCencPssh);
init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT, pssh_);
policy_engine_ = new MockPolicyEngine(crypto_session_);
ON_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull()))
.WillByDefault(
DoAll(SetArgPointee<0>(kDefaultSupportedCertTypes), Return(true)));
}
virtual void TearDown() {
@@ -145,7 +187,7 @@ class CdmLicenseTest : public ::testing::Test {
if (clock_) delete clock_;
}
void CreateCdmLicense() {
virtual void CreateCdmLicense() {
cdm_license_ = new CdmLicense(kCdmSessionId, clock_);
clock_ = NULL;
}
@@ -156,6 +198,12 @@ class CdmLicenseTest : public ::testing::Test {
MockInitializationData* init_data_;
MockPolicyEngine* policy_engine_;
ServiceCertificate service_cert_;
std::string pssh_;
};
class SubLicenseTest : public CdmLicenseTest {
protected:
SubLicenseTest() : CdmLicenseTest(kSubLicensePssh) {}
};
TEST_F(CdmLicenseTest, InitSuccess) {
@@ -169,8 +217,8 @@ TEST_F(CdmLicenseTest, InitSuccess) {
TEST_F(CdmLicenseTest, InitFail_EmptyToken) {
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(&service_cert_, "", kClientTokenDrmCert,
"", crypto_session_, policy_engine_));
EXPECT_FALSE(cdm_license_->Init(&service_cert_, "", kClientTokenDrmCert, "",
crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) {
@@ -191,19 +239,14 @@ TEST_F(CdmLicenseTest, InitWithNullServiceCert) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(NULL, kToken, kClientTokenDrmCert,
"", crypto_session_, policy_engine_));
EXPECT_TRUE(cdm_license_->Init(NULL, kToken, kClientTokenDrmCert, "",
crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
bool usage_information_support = true;
CryptoSession::HdcpCapability current_hdcp_version = HDCP_NO_DIGITAL_OUTPUT;
CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1;
CryptoSession::SupportedCertificateTypes device_supported_certs = {
true,
true,
true
};
uint32_t crypto_session_api_version = 9;
EXPECT_CALL(*crypto_session_, IsOpen())
@@ -216,8 +259,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version),
SetArgPointee<1>(max_hdcp_version), Return(true)));
EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(device_supported_certs), Return(true)));
EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull()));
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true)));
@@ -319,4 +361,89 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
EXPECT_EQ(kNonce, license_request.key_control_nonce());
}
TEST_F(SubLicenseTest, VerifySubSessionData) {
bool usage_information_support = true;
CryptoSession::HdcpCapability current_hdcp_version = HDCP_NO_DIGITAL_OUTPUT;
CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1;
uint32_t crypto_session_api_version = 9;
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
EXPECT_CALL(*crypto_session_, GenerateRequestId(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true)));
EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(usage_information_support), Return(true)));
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version),
SetArgPointee<1>(max_hdcp_version), Return(true)));
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true)));
EXPECT_CALL(*clock_, GetCurrentTime()).WillOnce(Return(kLicenseStartTime));
EXPECT_CALL(*crypto_session_, GenerateNonce(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kNonce), Return(true)));
EXPECT_CALL(*crypto_session_, PrepareRequest(_, Eq(false), NotNull()))
.WillOnce(
DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true)));
EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull()));
// SubLicense session data calls.
// TODO(jfore): These calls are being invoked twice each. This should not
// present a functional problem, but we should investigate why.
EXPECT_CALL(*crypto_session_,
GenerateSubSessionNonce(kSubSessionKeyID1, NotNull(), NotNull()))
.WillRepeatedly(
DoAll(SetArgPointee<1>(true), SetArgPointee<2>(0), Return(true)));
EXPECT_CALL(*crypto_session_,
GenerateSubSessionNonce(kSubSessionKeyID2, NotNull(), NotNull()))
.WillRepeatedly(
DoAll(SetArgPointee<1>(true), SetArgPointee<2>(1), Return(true)));
EXPECT_CALL(*crypto_session_,
GenerateSubSessionNonce(kSubSessionKeyID3, NotNull(), NotNull()))
.WillRepeatedly(
DoAll(SetArgPointee<1>(true), SetArgPointee<2>(2), Return(true)));
CreateCdmLicense();
// TODO(gmorgan) fix below - no default service certificate
//service_cert_.Init(kDefaultServiceCertificate);
EXPECT_TRUE(cdm_license_->Init(
&service_cert_, kToken, kClientTokenDrmCert, kEmptyString,
crypto_session_, policy_engine_));
CdmAppParameterMap app_parameters;
CdmKeyMessage signed_request;
Properties::set_use_certificates_as_identification(true);
std::string server_url;
EXPECT_EQ(cdm_license_->PrepareKeyRequest(*init_data_, kLicenseTypeStreaming,
app_parameters, &signed_request,
&server_url),
KEY_MESSAGE);
EXPECT_TRUE(!signed_request.empty());
SignedMessage signed_message;
EXPECT_TRUE(signed_message.ParseFromString(signed_request));
LicenseRequest license_request;
EXPECT_TRUE(license_request.ParseFromString(signed_message.msg()));
EXPECT_EQ(3, license_request.sub_session_data().size());
for (int i = 0; i < license_request.sub_session_data().size(); ++i) {
const video_widevine::LicenseRequest_SubSessionData& sl =
license_request.sub_session_data(i);
EXPECT_EQ(static_cast<unsigned>(i), sl.nonce());
switch (i) {
case 0:
EXPECT_EQ(kSubSessionKeyID1, sl.sub_session_key_id());
EXPECT_EQ("AUDIO", sl.track_label());
break;
case 1:
EXPECT_EQ(kSubSessionKeyID2, sl.sub_session_key_id());
EXPECT_EQ("SD", sl.track_label());
break;
case 3:
EXPECT_EQ(kSubSessionKeyID3, sl.sub_session_key_id());
EXPECT_EQ("HD", sl.track_label());
break;
}
}
}
} // namespace wvcdm

View File

@@ -60,6 +60,10 @@ class HdcpOnlyMockCryptoSession : public CryptoSession {
CryptoSession(NULL) {}
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
bool DoRealGetHdcpCapabilities(HdcpCapability* current,
HdcpCapability* max) {
return CryptoSession::GetHdcpCapabilities(current, max);
}
};
class MockCdmEventListener : public WvCdmEventListener {
@@ -85,6 +89,7 @@ using video_widevine::OFFLINE;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::MockFunction;
using ::testing::Pair;
using ::testing::Return;
@@ -125,6 +130,12 @@ class PolicyEngineTest : public ::testing::Test {
policy->set_renewal_delay_seconds(0);
policy->set_renewal_retry_interval_seconds(kLicenseRenewalRetryInterval);
policy->set_renew_with_usage(false);
ON_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillByDefault(
Invoke(
&crypto_session_,
&HdcpOnlyMockCryptoSession::DoRealGetHdcpCapabilities));
}
void InjectMockClock() {
@@ -152,7 +163,7 @@ class PolicyEngineTest : public ::testing::Test {
expected_has_new_usable_key));
}
StrictMock<HdcpOnlyMockCryptoSession> crypto_session_;
NiceMock<HdcpOnlyMockCryptoSession> crypto_session_;
StrictMock<MockCdmEventListener> mock_event_listener_;
MockClock* mock_clock_;
scoped_ptr<PolicyEngine> policy_engine_;

View File

@@ -608,6 +608,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE:
*os << "USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE";
break;
case LICENSE_REQUEST_INVALID_SUBLICENSE:
*os << "LICENSE_REQUEST_INVALID_SUBLICENSE";
break;
default:
*os << "Unknown CdmResponseType";
break;

View File

@@ -250,7 +250,8 @@ TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveLicenses) {
EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true));
// TODO: Why not being called?
//EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader,
kEmptyUsageEntryInfoVector))
.Times(2)

View File

@@ -11,6 +11,7 @@
#include "cdm_identifier.h"
#include "file_store.h"
#include "lock.h"
#include "metrics.pb.h"
#include "timer.h"
#include "wv_cdm_types.h"
@@ -125,7 +126,8 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// Validate a passed-in service certificate
virtual bool IsValidServiceCertificate(const std::string& certificate);
// Retrieve the serialized metrics from the CDM.
// Retrieve the serialized metrics from CdmEngine and CdmSession instances
// that have been closed.
virtual void GetSerializedMetrics(std::string* serialized_metrics);
private:
@@ -142,12 +144,17 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// Finds the CdmEngine instance for the given session id, returning NULL if
// not found.
CdmEngine* GetCdmForSessionId(const std::string& session_id);
// Closes CdmEngine instances that don't have any open sessions. Also stores
// metrics data for closed CdmEngine instances.
// Callers must acquire the cdms_lock_ before calling this method.
void CloseCdmsWithoutSessions();
uint32_t GenerateSessionSharingId();
// timer related methods to drive policy decisions
void EnablePolicyTimer();
void DisablePolicyTimer(bool force);
void DisablePolicyTimer(); // Disable if all cdm engines are closed.
void ForceDisablePolicyTimer(); // Force disable the policy timer.
void OnTimerEvent();
static Lock session_sharing_id_generation_lock_;
@@ -162,6 +169,9 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
// This contains weak pointers to the CDM instances contained in |cdms_|.
std::map<std::string, CdmEngine*> cdm_by_session_id_;
// The metrics for cdm engines and sessions that have been closed.
drm_metrics::MetricsGroup metrics_;
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule);
};

View File

@@ -141,7 +141,7 @@ class CryptoMetrics {
EventMetric<OEMCryptoResult> oemcrypto_rewrap_device_rsa_key_;
EventMetric<OEMCryptoResult> oemcrypto_rewrap_device_rsa_key_30_;
EventMetric<CdmSecurityLevel, SecurityLevel> oemcrypto_security_level_;
EventMetric<uint8_t, SecurityLevel> oemcrypto_security_patch_level_;
EventMetric<uint16_t, SecurityLevel> oemcrypto_security_patch_level_;
EventMetric<OEMCryptoResult> oemcrypto_select_key_;
EventMetric<OEMCryptoResult, SecurityLevel> oemcrypto_supports_usage_table_;
EventMetric<OEMCryptoResult> oemcrypto_update_usage_table_;

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include "log.h"
#include "metrics.pb.h"
using drm_metrics::MetricsGroup;
@@ -438,6 +439,10 @@ EngineMetrics::EngineMetrics() :
EngineMetrics::~EngineMetrics() {
AutoLock kock(session_metrics_lock_);
std::vector<SessionMetrics*>::iterator i;
if (!session_metrics_list_.empty()) {
LOGV("EngineMetrics::~EngineMetrics. Session count: %d",
session_metrics_list_.size());
}
for (i = session_metrics_list_.begin(); i != session_metrics_list_.end();
i++) {
delete *i;

View File

@@ -24,7 +24,7 @@ Lock WvContentDecryptionModule::session_sharing_id_generation_lock_;
WvContentDecryptionModule::WvContentDecryptionModule() {}
WvContentDecryptionModule::~WvContentDecryptionModule() {
DisablePolicyTimer(true);
ForceDisablePolicyTimer();
}
bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) {
@@ -72,6 +72,7 @@ CdmResponseType WvContentDecryptionModule::OpenSession(
CdmResponseType WvContentDecryptionModule::CloseSession(
const CdmSessionId& session_id) {
LOGV("WvContentDecryptionModule::CloseSession. id: %s", session_id.c_str());
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
// TODO(rfrias): Avoid reusing the error codes from CdmEngine.
if (!cdm_engine) return SESSION_NOT_FOUND_1;
@@ -85,7 +86,9 @@ CdmResponseType WvContentDecryptionModule::CloseSession(
if (sts == NO_ERROR) {
cdm_by_session_id_.erase(session_id);
}
DisablePolicyTimer(false);
DisablePolicyTimer();
return sts;
}
@@ -404,21 +407,10 @@ bool WvContentDecryptionModule::IsValidServiceCertificate(
void WvContentDecryptionModule::GetSerializedMetrics(
std::string* serialized_metrics) {
drm_metrics::MetricsGroup metric_group;
{
AutoLock auto_lock(cdms_lock_);
for (auto it = cdms_.begin(); it != cdms_.end(); it++) {
metrics::EngineMetrics* engine_metrics =
it->second.cdm_engine->GetMetrics();
if (engine_metrics) {
// Serialize the metrics from the engine and any completed sessions.
// Clear the metrics from any completed sessions.
engine_metrics->Serialize(
metric_group.add_metric_sub_group(), true, true);
}
}
}
metric_group.SerializeToString(serialized_metrics);
AutoLock auto_lock(cdms_lock_);
CloseCdmsWithoutSessions();
metrics_.SerializeToString(serialized_metrics);
metrics_.Clear();
}
WvContentDecryptionModule::CdmInfo::CdmInfo()
@@ -449,30 +441,66 @@ CdmEngine* WvContentDecryptionModule::GetCdmForSessionId(
return it->second;
}
// This method requires that the caller first acquire cdms_lock_.
void WvContentDecryptionModule::CloseCdmsWithoutSessions() {
for (auto it = cdms_.begin(); it != cdms_.end();) {
if (it->second.cdm_engine->SessionSize() != 0) {
++it;
} else {
// Retrieve the metrics from the engine and any completed
// sessions. Clear the metrics from any completed sessions.
metrics::EngineMetrics* engine_metrics =
it->second.cdm_engine->GetMetrics();
// engine_metrics should never be null.
if (engine_metrics != NULL) {
engine_metrics->Serialize(
metrics_.add_metric_sub_group(),
false, // Report complete AND incomplete sessions.
true); // Clear session metrics after reporting.
} else {
// Engine metrics should never be null.
LOGI("WvContentDecryptionModule::CloseCdmsWithoutSessions."
"engine_metrics was unexpectedly NULL.");
}
// The CDM is no longer used for this identifier, delete it.
it = cdms_.erase(it);
}
}
}
void WvContentDecryptionModule::EnablePolicyTimer() {
AutoLock auto_lock(policy_timer_lock_);
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
}
void WvContentDecryptionModule::DisablePolicyTimer(bool force) {
bool has_sessions = false;
void WvContentDecryptionModule::DisablePolicyTimer() {
bool cdms_is_empty = false;
{
AutoLock auto_lock(cdms_lock_);
for (auto it = cdms_.begin(); it != cdms_.end();) {
if (it->second.cdm_engine->SessionSize() != 0) {
has_sessions = true;
++it;
} else {
// The CDM is no longer used for this identifier, delete it.
it = cdms_.erase(it);
}
}
CloseCdmsWithoutSessions();
cdms_is_empty = cdms_.empty();
}
AutoLock auto_lock(policy_timer_lock_);
if ((!has_sessions || force) && policy_timer_.IsRunning())
if(cdms_is_empty) {
if (policy_timer_.IsRunning()) {
policy_timer_.Stop();
}
}
}
void WvContentDecryptionModule::ForceDisablePolicyTimer() {
{
AutoLock auto_lock(cdms_lock_);
CloseCdmsWithoutSessions();
}
AutoLock auto_lock(policy_timer_lock_);
if (policy_timer_.IsRunning()) {
policy_timer_.Stop();
}
}
void WvContentDecryptionModule::OnTimerEvent() {

View File

@@ -11,6 +11,10 @@ test_name := buffer_reader_test
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk
test_name := cdm_feature_test
test_src_dir := .
include $(LOCAL_PATH)/unit-test.mk
test_name := cdm_engine_test
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk
@@ -75,6 +79,10 @@ test_name := usage_table_header_unittest
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk
test_name := wv_cdm_metrics_test
test_src_dir := .
include $(LOCAL_PATH)/unit-test.mk
test_name := distribution_test
test_src_dir := ../metrics/test
include $(LOCAL_PATH)/unit-test.mk

View File

@@ -48,7 +48,7 @@ wvcdm::CdmKeySystem g_key_system;
std::string g_license_server;
wvcdm::KeyId g_wrong_key_id;
wvcdm::ServerConfigurationId g_license_server_id =
wvcdm::kContentProtectionUatLicense;
wvcdm::kContentProtectionUatServer;
// TODO(rfrias): refactor to print out the decryption test names
struct SubSampleInfo {
@@ -1591,9 +1591,9 @@ int main(int argc, char** argv) {
if (!license_id.compare("gp")) {
g_license_server_id = wvcdm::kGooglePlayServer;
} else if (!license_id.compare("cp")) {
g_license_server_id = wvcdm::kContentProtectionUatLicense;
g_license_server_id = wvcdm::kContentProtectionUatServer;
} else if (!license_id.compare("st")) {
g_license_server_id = wvcdm::kContentProtectionStagingLicense;
g_license_server_id = wvcdm::kContentProtectionStagingServer;
} else {
std::cout << "Invalid license server id" << optarg << std::endl;
show_usage = true;

View File

@@ -66,8 +66,8 @@ wvcdm::KeyId g_key_id;
wvcdm::CdmKeySystem g_key_system;
std::string g_license_server;
wvcdm::KeyId g_wrong_key_id;
wvcdm::ServerConfigurationId g_license_server_id =
wvcdm::kContentProtectionUatLicense;
wvcdm::ServerConfigurationId g_server_configuration_id =
wvcdm::kContentProtectionUatServer;
std::string g_service_certificate;
// TODO(rfrias): refactor to print out the decryption test names
@@ -991,7 +991,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
protected:
void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) {
ConfigTestEnv config(g_license_server_id, false);
ConfigTestEnv config(g_server_configuration_id, false);
if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0)
key_id->assign(wvcdm::a2bs_hex(config.key_id()));
else
@@ -3902,11 +3902,12 @@ int main(int argc, char** argv) {
case 'i': {
std::string license_id(optarg);
if (!license_id.compare("gp")) {
g_license_server_id = wvcdm::kGooglePlayServer;
g_server_configuration_id = wvcdm::kGooglePlayServer;
} else if (!license_id.compare("cp")) {
g_license_server_id = wvcdm::kContentProtectionUatLicense;
g_server_configuration_id = wvcdm::kContentProtectionUatServer;
} else if (!license_id.compare("st")) {
g_license_server_id = wvcdm::kContentProtectionStagingLicense;
g_server_configuration_id =
wvcdm::kContentProtectionStagingServer;
} else {
std::cout << "Invalid license server id" << optarg << std::endl;
show_usage = true;
@@ -3940,7 +3941,7 @@ int main(int argc, char** argv) {
return 0;
}
g_config = new wvcdm::ConfigTestEnv(g_license_server_id);
g_config = new wvcdm::ConfigTestEnv(g_server_configuration_id);
g_client_auth.assign(g_config->client_auth());
g_key_system.assign(g_config->key_system());
g_wrong_key_id.assign(g_config->wrong_key_id());

View File

@@ -13,7 +13,7 @@
using ::testing::Eq;
using ::testing::StrEq;
using ::testing::Ge;
using ::testing::Gt;
using ::testing::Test;
using wvcdm::CdmResponseType;
@@ -53,17 +53,13 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineOnlyMetrics) {
ASSERT_TRUE(metrics.ParseFromString(serialized_metrics));
EXPECT_THAT(metrics.metric_size(), Eq(0));
ASSERT_THAT(metrics.metric_sub_group_size(), Eq(1));
ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Ge(6));
ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Gt(0));
EXPECT_THAT(metrics.metric_sub_group(0).metric_sub_group_size(), Eq(0));
EXPECT_THAT(metrics.metric_sub_group(0).metric(0).name(),
StrEq("/drm/widevine/oemcrypto/initialization_mode"));
// Can't check the initialization mode value. Different devices will have
// different values.
EXPECT_THAT(
metrics.metric_sub_group(0).metric(5).name(),
metrics.metric_sub_group(0).metric(0).name(),
StrEq("/drm/widevine/cdm_engine/"
"get_provisioning_request/time/count{error:0}"));
EXPECT_THAT(metrics.metric_sub_group(0).metric(5).value().int_value(), Eq(1));
EXPECT_THAT(metrics.metric_sub_group(0).metric(0).value().int_value(), Eq(1));
}
@@ -87,14 +83,13 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) {
// The outer container will never have metrics.
EXPECT_THAT(metrics.metric_size(), Eq(0));
ASSERT_THAT(metrics.metric_sub_group_size(), Eq(1));
ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Ge(6));
ASSERT_THAT(metrics.metric_sub_group(0).metric_size(), Gt(0));
// Validate engine-level metrics.
EXPECT_THAT(metrics.metric_sub_group(0).metric(0).name(),
StrEq("/drm/widevine/oemcrypto/initialization_mode"));
EXPECT_THAT(metrics.metric_sub_group(0).metric(5).name(),
StrEq("/drm/widevine/cdm_engine/open_session/count{error:7}"));
EXPECT_THAT(metrics.metric_sub_group(0).metric(5).value().int_value(), Eq(1));
// Validate an engine-level metric.
EXPECT_THAT(
metrics.metric_sub_group(0).metric(0).name(),
StrEq("/drm/widevine/cdm_engine/open_session/time/count{error:7}"));
EXPECT_THAT(metrics.metric_sub_group(0).metric(0).value().int_value(), Eq(1));
// Validate a session-level metric.
EXPECT_THAT(metrics.metric_sub_group(0).metric_sub_group_size(), Eq(1));
@@ -106,7 +101,7 @@ TEST_F(WvContentDecryptionModuleMetricsTest, EngineAndSessionMetrics) {
TEST_F(WvContentDecryptionModuleMetricsTest, MultipleEngineMetric) {
CdmSessionId session_id;
wvcdm::CdmKeySystem key_system("com.widevine");
CdmIdentifier identifier = { "foo", "bar", "baz" };
CdmIdentifier identifier = { "foo", "bar" };
// Openning the session will fail with NEEDS_PROVISIONING error. But it will
// still create some session-level stats.
@@ -133,23 +128,17 @@ TEST_F(WvContentDecryptionModuleMetricsTest, MultipleEngineMetric) {
for (int i = 0; i < metrics.metric_sub_group_size(); i++) {
// Validate the engine-level metric.
ASSERT_THAT(metrics.metric_sub_group(i).metric_size(), Ge(6));
EXPECT_THAT(metrics.metric_sub_group(i).metric(0).name(),
StrEq("/drm/widevine/oemcrypto/initialization_mode"));
EXPECT_THAT(metrics.metric_sub_group(i).metric(5).name(),
StrEq("/drm/widevine/cdm_engine/open_session/count{error:7}"));
EXPECT_THAT(metrics.metric_sub_group(i).metric(5).value().int_value(),
Eq(1));
ASSERT_THAT(metrics.metric_sub_group(i).metric_size(), Gt(0));
EXPECT_THAT(
metrics.metric_sub_group(i).metric(0).name(),
StrEq("/drm/widevine/cdm_engine/open_session/time/count{error:7}"));
EXPECT_THAT(metrics.metric_sub_group(i).metric(0).value().int_value(), Eq(1));
// Validate a session-level metric.
EXPECT_THAT(metrics.metric_sub_group(i).metric_sub_group_size(), Eq(1));
EXPECT_THAT(
metrics.metric_sub_group(i).metric_sub_group(0).metric(0).name(),
StrEq("/drm/widevine/cdm_session/session_id"));
}
// Verify that the second metrics app package name is set.
EXPECT_THAT(metrics.metric_sub_group(1).app_package_name(), StrEq("baz"));
}
}