Allow Apps to Voluntarily Downgrade to L3 Crypto

This merges the following changes from the Widevine CDM repository:

564f4cc  Add CdmClientPropertySet to CDM
  Adds an interface to the CDM that allows it to query its client for
  certain properties.  In this case, this includes the ability to
  specify what security level is desired, as well as support for
  service ceritifcate privacy mode.

9cfbd3e  Force Level 3 fallback
  Adds support for voluntarily invoking L3 crypto to the OEMCrypto
  wrapper.

95d12c1  Add pointer to CdmClientPropertySet class to OpenSession.
  Adds support for storing the property set on a session-by-session
  basis and choosing the appropriate crypto level.

17de442  Add Settable Properties for Clank to Android
  Adds support for setting the aforementioned properties to the
  DrmEngine

bbe704d  Fixes to force fallback to level three security
  Corrections to invoke provisioning, OEMCrypto API with configured
  security level rather than the default. Unit tests were also revised.

Note that some parts of this are also support for the ability to use
a service certificate-based privacy mode. The remaining code for
supporting this mode is still forthcoming.

Bug: 10109249
Change-Id: I2755e4dea1de3e8a56cff237360298f7b7f1bddc
This commit is contained in:
Rahul Frias
2013-08-15 10:59:42 -07:00
parent 0fa3e16999
commit f6c2a60485
45 changed files with 2359 additions and 906 deletions

View File

@@ -92,7 +92,6 @@ LOCAL_C_INCLUDES := \
LOCAL_STATIC_LIBRARIES := \
libcdm \
libcdm_utils \
libwvwrapper \
libwvlevel3 \
libprotobuf-cpp-2.3.0-lite \
libwvdrmcryptoplugin \
@@ -121,7 +120,6 @@ include $(BUILD_SHARED_LIBRARY)
include vendor/widevine/libwvdrmengine/cdm/Android.mk
include vendor/widevine/libwvdrmengine/level3/Android.mk
include vendor/widevine/libwvdrmengine/oemcrypto/Android.mk
include vendor/widevine/libwvdrmengine/mediacrypto/Android.mk
include vendor/widevine/libwvdrmengine/mediadrm/Android.mk

View File

@@ -14,8 +14,7 @@ LOCAL_C_INCLUDES := \
LOCAL_C_INCLUDES += \
external/openssl/include \
external/protobuf/src \
../oemcrypto/include
external/protobuf/src
LOCAL_STATIC_LIBRARIES := libcdm_protos
LOCAL_ADDITIONAL_DEPENDENCIES := $(cdm_proto_gen_headers)
@@ -30,7 +29,9 @@ LOCAL_SRC_FILES := \
$(CORE_SRC_DIR)/crypto_session.cpp \
$(CORE_SRC_DIR)/device_files.cpp \
$(CORE_SRC_DIR)/license.cpp \
$(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \
$(CORE_SRC_DIR)/policy_engine.cpp \
$(CORE_SRC_DIR)/privacy_crypto.cpp \
$(SRC_DIR)/wv_content_decryption_module.cpp
LOCAL_MODULE := libcdm

View File

@@ -0,0 +1,23 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef CDM_BASE_CDM_CLIENT_PROPERTY_SET_H_
#define CDM_BASE_CDM_CLIENT_PROPERTY_SET_H_
#include <string>
#include <vector>
#include <stdint.h>
namespace wvcdm {
class CdmClientPropertySet {
public:
virtual ~CdmClientPropertySet() {}
virtual std::string security_level() const = 0;
virtual bool use_privacy_mode() const = 0;
virtual std::vector<uint8_t> service_certificate() const = 0;
};
} // namespace wvcdm
#endif // CDM_BASE_CDM_CLIENT_PROPERTY_SET_H_

View File

@@ -4,11 +4,13 @@
#define CDM_BASE_CDM_ENGINE_H_
#include "certificate_provisioning.h"
#include "oemcrypto_adapter.h"
#include "timer.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class CdmClientPropertySet;
class CdmSession;
class CryptoEngine;
class WvCdmEventListener;
@@ -22,7 +24,9 @@ class CdmEngine : public TimerHandler {
virtual ~CdmEngine();
// Session related methods
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
virtual CdmResponseType OpenSession(
const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
@@ -116,9 +120,11 @@ class CdmEngine : public TimerHandler {
// instance variables
CdmSessionMap sessions_;
CertificateProvisioning cert_provisioning_;
CdmReleaseKeySetMap release_key_sets_;
CertificateProvisioning cert_provisioning_;
SecurityLevel cert_provisioning_requested_security_level_;
// policy timer
Timer policy_timer_;

View File

@@ -8,23 +8,20 @@
#include "crypto_session.h"
#include "device_files.h"
#include "license.h"
#include "oemcrypto_adapter.h"
#include "policy_engine.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class CdmClientPropertySet;
class WvCdmEventListener;
class CdmSession {
public:
CdmSession()
: session_id_(GenerateSessionId()),
crypto_session_(NULL),
license_received_(false),
reinitialize_session_(false),
license_type_(kLicenseTypeStreaming) {}
~CdmSession() {}
explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set);
~CdmSession();
CdmResponseType Init();
@@ -87,13 +84,14 @@ class CdmSession {
void OnTimerEvent();
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
SecurityLevel GetRequestedSecurityLevel();
private:
// Generate unique ID for each new session.
CdmSessionId GenerateSessionId();
bool GenerateKeySetId(CdmKeySetId* key_set_id);
bool LoadDeviceCertificate(std::string* cert, std::string* wrapped_key);
bool StoreLicense(DeviceFiles::LicenseState state);
// instance variables

View File

@@ -4,6 +4,7 @@
#define CDM_BASE_CERTIFICATE_PROVISIONING_H_
#include "crypto_session.h"
#include "oemcrypto_adapter.h"
#include "wv_cdm_types.h"
namespace wvcdm {
@@ -16,7 +17,8 @@ class CertificateProvisioning {
~CertificateProvisioning() {};
// Provisioning related methods
CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request,
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response);

View File

@@ -9,6 +9,7 @@
#include <map>
#include "lock.h"
#include "oemcrypto_adapter.h"
#include "OEMCryptoCENC.h"
#include "wv_cdm_types.h"
@@ -29,7 +30,8 @@ class CryptoSession {
bool GetSystemId(uint32_t* system_id);
bool GetProvisioningId(std::string* provisioning_id);
CdmResponseType Open();
CdmResponseType Open() { return Open(kLevelDefault); }
CdmResponseType Open(SecurityLevel requested_security_level);
void Close();
bool IsOpen() { return open_; }
@@ -64,7 +66,7 @@ class CryptoSession {
bool SelectKey(const std::string& key_id);
CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
bool GetRandom(uint8_t* random_data, size_t data_length);
bool GetRandom(size_t data_length, uint8_t* random_data);
private:
void Init();
@@ -88,7 +90,7 @@ class CryptoSession {
OEMCryptoBufferType destination_buffer_type_;
bool is_destination_buffer_type_valid_;
CdmSecurityLevel security_level_;
SecurityLevel requested_security_level_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
};

View File

@@ -45,6 +45,7 @@ class DeviceFiles {
CdmKeyResponse* key_renewal_response,
std::string* release_server_url);
virtual bool DeleteLicense(const std::string& key_set_id);
virtual bool DeleteAllFiles();
virtual bool DeleteAllLicenses();
virtual bool LicenseExists(const std::string& key_set_id);

View File

@@ -19,8 +19,11 @@ class PolicyEngine;
class CdmLicense {
public:
CdmLicense();
~CdmLicense();
CdmLicense()
: session_(NULL),
initialized_(false),
service_certificate_response_pending_(false) {};
~CdmLicense() {};
bool Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine);
@@ -28,21 +31,26 @@ class CdmLicense {
bool PrepareKeyRequest(const CdmInitData& pssh_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id,
CdmKeyMessage* signed_request,
std::string* server_url);
bool PrepareKeyUpdateRequest(bool is_renewal,
CdmKeyMessage* signed_request,
bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request,
std::string* server_url);
CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
CdmResponseType HandleKeyUpdateResponse(
bool is_renewal,
const CdmKeyResponse& license_response);
bool is_renewal, const CdmKeyResponse& license_response);
bool RestoreOfflineLicense(CdmKeyMessage& license_request,
CdmKeyResponse& license_response,
CdmKeyResponse& license_renewal_response);
bool HasInitData() { return !init_data_.empty(); }
private:
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
std::string* server_url);
CdmResponseType HandleServiceCertificateResponse(
const CdmKeyResponse& service_certificate_response);
CdmResponseType HandleKeyErrorResponse(
const video_widevine_server::sdk::SignedMessage& signed_message);
@@ -50,6 +58,10 @@ private:
PolicyEngine* policy_engine_;
std::string server_url_;
std::string token_;
std::string service_certificate_;
std::string init_data_;
bool initialized_;
bool service_certificate_response_pending_;
// Used for certificate based licensing
CdmKeyMessage key_request_;

View File

@@ -0,0 +1,39 @@
/*********************************************************************
* oemcrypto_adapter.h
*
* (c) Copyright 2013 Google, Inc.
*
* This interface allows CDM to open a Level 3 session instead of
* letting the wrapper function choose between level 1 or level 3.
*
*********************************************************************/
#ifndef OEMCRYPTO_ADAPTER_H_
#define OEMCRYPTO_ADAPTER_H_
#include "OEMCryptoCENC.h"
namespace wvcdm {
enum SecurityLevel {
kLevelDefault,
kLevel3
};
/* This attempts to open a session at the desired security level.
If one level is not available, the other will be used instead. */
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
SecurityLevel level);
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level);
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level);
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
SecurityLevel level);
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level);
uint32_t OEMCrypto_APIVersion(SecurityLevel level);
const char* OEMCrypto_SecurityLevel(SecurityLevel level);
} // namespace wvcdm
#endif // OEMCRYPTO_ADAPTER_H_

View File

@@ -0,0 +1,77 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// RsaPublicKey based on //depot/google3/video/widevine/common/rsa_key.h by
// tinskip@google.com.
//
// Description:
// Declaration of classes representing AES and RSA public keys used
// for signature verification and encryption.
//
// AES encryption details:
// Algorithm: AES-CBC
//
// RSA signature details:
// Algorithm: RSASSA-PSS
// Hash algorithm: SHA1
// Mask generation function: mgf1SHA1
// Salt length: 20 bytes
// Trailer field: 0xbc
//
// RSA encryption details:
// Algorithm: RSA-OAEP
// Mask generation function: mgf1SHA1
// Label (encoding paramter): empty string
#ifndef CDM_BASE_PRIVACY_CRYPTO_H_
#define CDM_BASE_PRIVACY_CRYPTO_H_
#include <string>
#include "openssl/aes.h"
#include "openssl/rsa.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class AesCbcKey {
public:
AesCbcKey() : initialized_(false) {};
~AesCbcKey() {};
bool Init(const std::string& key);
bool Encrypt(const std::string& in, std::string* out, std::string* iv);
private:
AES_KEY key_;
bool initialized_;
CORE_DISALLOW_COPY_AND_ASSIGN(AesCbcKey);
};
class RsaPublicKey {
public:
RsaPublicKey() : key_(NULL) {}
~RsaPublicKey();
// Initializes an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey
bool Init(const std::string& serialized_key);
// Encrypt a message using RSA-OAEP. Caller retains ownership of all
// parameters. Returns true if successful, false otherwise.
bool Encrypt(const std::string& clear_message,
std::string* encrypted_message);
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
// Returns true if validation succeeds, false otherwise.
bool VerifySignature(const std::string& message,
const std::string& signature);
private:
RSA* key_;
CORE_DISALLOW_COPY_AND_ASSIGN(RsaPublicKey);
};
} // namespace wvcdm
#endif // CDM_BASE_PRIVACY_CRYPTO_H_

View File

@@ -6,11 +6,16 @@
#include <map>
#include <string>
#include "cdm_client_property_set.h"
#include "lock.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
namespace wvcdm {
typedef std::map<CdmSessionId, const CdmClientPropertySet*>
CdmClientPropertySetMap;
// This class saves information about features and properties enabled
// for a given platform. At initialization it initializes properties from
// property_configuration.h. That file specifies features selected for each
@@ -40,6 +45,9 @@ class Properties {
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 bool GetCompanyName(std::string* company_name);
static bool GetModelName(std::string* model_name);
static bool GetArchitectureName(std::string* arch_name);
@@ -49,8 +57,20 @@ class Properties {
static bool GetDeviceFilesBasePath(CdmSecurityLevel security_level,
std::string* base_path);
static bool GetFactoryKeyboxPath(std::string* keybox);
static bool GetOEMCryptoPath(std::string* library_name);
static const std::string GetSecurityLevel(const CdmSessionId& session_id);
static const std::vector<uint8_t> GetServiceCertificate(
const CdmSessionId& session_id);
static bool UsePrivacyMode(const CdmSessionId& session_id);
static bool AddSessionPropertySet(
const CdmSessionId& session_id,
const CdmClientPropertySet* property_set);
static bool RemoveSessionPropertySet(const CdmSessionId& session_id);
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;
}
@@ -72,6 +92,9 @@ class Properties {
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 bool begin_license_usage_when_received_;
static bool require_explicit_renew_request_;
@@ -80,6 +103,8 @@ class Properties {
static bool oem_crypto_use_userspace_buffers_;
static bool use_certificates_as_identification_;
static bool extract_pssh_data_;
static bool decrypt_with_empty_session_support_;
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);
};

View File

@@ -21,7 +21,8 @@ const int kCdmPolicyTimerDurationSeconds = 1;
namespace wvcdm {
CdmEngine::CdmEngine() {
CdmEngine::CdmEngine()
: cert_provisioning_requested_security_level_(kLevelDefault) {
Properties::Init();
}
@@ -36,6 +37,7 @@ CdmEngine::~CdmEngine() {
CdmResponseType CdmEngine::OpenSession(
const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
CdmSessionId* session_id) {
LOGI("CdmEngine::OpenSession");
@@ -49,8 +51,7 @@ CdmResponseType CdmEngine::OpenSession(
return KEY_ERROR;
}
scoped_ptr<CdmSession> new_session(new CdmSession());
scoped_ptr<CdmSession> new_session(new CdmSession(property_set));
if (new_session->session_id().empty()) {
LOGE("CdmEngine::OpenSession: failure to generate session ID");
return UNKNOWN_ERROR;
@@ -58,10 +59,13 @@ CdmResponseType CdmEngine::OpenSession(
CdmResponseType sts = new_session->Init();
if (sts != NO_ERROR) {
LOGE("CdmEngine::OpenSession: bad session init");
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
new_session->GetRequestedSecurityLevel();
}
LOGE("CdmEngine::OpenSession: bad session init: %u", sts);
return sts;
}
*session_id = new_session->session_id();
sessions_[*session_id] = new_session.release();
return NO_ERROR;
@@ -76,7 +80,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
}
CdmSessionId session_id;
CdmResponseType sts = OpenSession(KEY_SYSTEM, &session_id);
CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id);
if (sts != NO_ERROR)
return sts;
@@ -178,6 +182,10 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
server_url);
if (KEY_MESSAGE != sts) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
}
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
"sts = %d", (int)sts);
return sts;
@@ -262,7 +270,13 @@ CdmResponseType CdmEngine::RestoreKey(
return UNKNOWN_ERROR;
}
return iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
CdmResponseType sts =
iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
}
return sts;
}
CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
@@ -425,7 +439,10 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
return UNKNOWN_ERROR;
}
return cert_provisioning_.GetProvisioningRequest(request, default_url);
return cert_provisioning_.GetProvisioningRequest(
cert_provisioning_requested_security_level_,
request,
default_url);
}
/*
@@ -479,9 +496,19 @@ CdmResponseType CdmEngine::Decrypt(
return KEY_ERROR;
}
CdmSessionMap::iterator iter = sessions_.find(session_id);
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->IsKeyValid(*parameters.key_id)) break;
}
} else {
iter = sessions_.find(session_id);
}
if (iter == sessions_.end()) {
LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str());
LOGE("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}

View File

@@ -26,14 +26,33 @@ namespace wvcdm {
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set)
: session_id_(GenerateSessionId()),
crypto_session_(NULL),
license_received_(false),
reinitialize_session_(false),
license_type_(kLicenseTypeStreaming) {
if (cdm_client_property_set) {
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
}
}
CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); }
CdmResponseType CdmSession::Init() {
scoped_ptr<CryptoSession> session(new CryptoSession());
CdmResponseType sts = session->Open();
CdmResponseType sts = session->Open(GetRequestedSecurityLevel());
if (NO_ERROR != sts) return sts;
std::string token;
if (Properties::use_certificates_as_identification()) {
if (!LoadDeviceCertificate(&token, &wrapped_key_)) return NEED_PROVISIONING;
File file;
DeviceFiles handle;
if (!handle.Init(&file, session.get()->GetSecurityLevel()) ||
!handle.RetrieveCertificate(&token, &wrapped_key_)) {
return NEED_PROVISIONING;
}
} else {
if (!session->GetToken(&token)) return UNKNOWN_ERROR;
}
@@ -129,7 +148,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
? UNKNOWN_ERROR
: GenerateRenewalRequest(key_request, server_url);
} else {
if (init_data.empty()) {
if (init_data.empty() && !license_parser_.HasInitData()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR;
}
@@ -148,8 +167,9 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
}
if (!license_parser_.PrepareKeyRequest(
pssh_data, license_type, app_parameters, key_request, server_url)) {
if (!license_parser_.PrepareKeyRequest(pssh_data, license_type,
app_parameters, session_id_,
key_request, server_url)) {
return KEY_ERROR;
}
@@ -310,8 +330,21 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
}
bool CdmSession::IsKeyValid(const KeyId& key_id) {
// TODO(gmorgan): lookup key and determine if valid.
// return (session_keys_.find(key_id) != session_keys_.end());
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
return false;
if (key_id_ != key_id) {
// OEMCrypto does not provide a way to query the existence/validity of a
// key. SelectKey can be used to check whether a key is valid, but there
// is also a side effect - the key is selected for decryption, which might
// be undesirable and it posts restriction on the use of IsKeyValid API.
// TODO(kqyang, gmorgan): consider adding a function in OEMCrypto to check
// if a key is valid.
if (!crypto_session_->SelectKey(key_id)) {
return false;
}
key_id_ = key_id;
}
return true;
}
@@ -337,7 +370,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
return false;
while (key_set_id->empty()) {
if (!crypto_session_->GetRandom(&random_data[0], random_data.size()))
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0]))
return false;
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
@@ -350,16 +383,6 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
return true;
}
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
std::string* wrapped_key) {
File file;
DeviceFiles handle;
if (!handle.Init(&file, crypto_session_->GetSecurityLevel()))
return false;
return handle.RetrieveCertificate(certificate, wrapped_key);
}
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
File file;
DeviceFiles handle;
@@ -404,4 +427,13 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
}
}
SecurityLevel CdmSession::GetRequestedSecurityLevel() {
if (Properties::GetSecurityLevel(session_id_)
.compare(QUERY_VALUE_SECURITY_LEVEL_L3) == 0) {
return kLevel3;
}
return kLevelDefault;
}
} // namespace wvcdm

View File

@@ -53,11 +53,12 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SecurityLevel requested_security_level,
CdmProvisioningRequest* request,
std::string* default_url) {
default_url->assign(kDefaultProvisioningServerUrl);
CdmResponseType sts = crypto_session_.Open();
CdmResponseType sts = crypto_session_.Open(requested_security_level);
if (NO_ERROR != sts) {
LOGE("GetProvisioningRequest: fails to create a crypto session");
return sts;

View File

@@ -10,8 +10,6 @@
#include "crypto_key.h"
#include "log.h"
// TODO(gmorgan,jtinker): decide if OEMCryptoCENC is needed here.
#include "OEMCryptoCENC.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
@@ -37,7 +35,7 @@ int CryptoSession::session_count_ = 0;
CryptoSession::CryptoSession()
: open_(false),
is_destination_buffer_type_valid_(false),
security_level_(kSecurityLevelUninitialized) {
requested_security_level_(kLevelDefault) {
Init();
}
@@ -79,7 +77,7 @@ bool CryptoSession::ValidateKeybox() {
if (!initialized_) {
return false;
}
OEMCryptoResult result = OEMCrypto_IsKeyboxValid();
OEMCryptoResult result = OEMCrypto_IsKeyboxValid(requested_security_level_);
return (OEMCrypto_SUCCESS == result);
}
@@ -95,7 +93,8 @@ bool CryptoSession::GetToken(std::string* token) {
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
OEMCryptoResult sts =
OEMCrypto_GetKeyData(buf, &bufSize, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
@@ -110,16 +109,8 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel() {
return kSecurityLevelUninitialized;
}
switch (security_level_) {
case kSecurityLevelL1:
case kSecurityLevelL2:
case kSecurityLevelL3:
return security_level_;
default:
break;
}
std::string security_level = OEMCrypto_SecurityLevel();
std::string security_level =
OEMCrypto_SecurityLevel(requested_security_level_);
if ((security_level.size() != 2) || (security_level.at(0) != 'L')) {
return kSecurityLevelUnknown;
@@ -127,20 +118,16 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel() {
switch (security_level.at(1)) {
case '1':
security_level_ = kSecurityLevelL1;
break;
return kSecurityLevelL1;
case '2':
security_level_ = kSecurityLevelL2;
break;
return kSecurityLevelL2;
case '3':
security_level_ = kSecurityLevelL3;
break;
return kSecurityLevelL3;
default:
security_level_ = kSecurityLevelUnknown;
break;
return kSecurityLevelUnknown;
}
return security_level_;
return kSecurityLevelUnknown;
}
bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
@@ -159,7 +146,8 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &id_length);
OEMCryptoResult sts =
OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
return false;
@@ -183,7 +171,8 @@ bool CryptoSession::GetSystemId(uint32_t* system_id) {
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
OEMCryptoResult sts =
OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
return false;
@@ -211,7 +200,8 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
if (!initialized_) {
return false;
}
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
OEMCryptoResult sts =
OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
return false;
@@ -221,14 +211,15 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
return true;
}
CdmResponseType CryptoSession::Open() {
CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
LOGV("CryptoSession::Open: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) return false;
if (open_) return true;
if (!initialized_) return UNKNOWN_ERROR;
if (open_) return NO_ERROR;
OEMCrypto_SESSION sid;
OEMCryptoResult sts = OEMCrypto_OpenSession(&sid);
requested_security_level_ = requested_security_level;
OEMCryptoResult sts = OEMCrypto_OpenSession(&sid, requested_security_level);
if (OEMCrypto_SUCCESS == sts) {
oec_session_id_ = static_cast<CryptoSessionId>(sid);
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
@@ -680,7 +671,11 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
return true;
}
bool CryptoSession::GetRandom(uint8_t* random_data, size_t data_length) {
bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) {
if (random_data == NULL) {
LOGE("CryptoSession::GetRandom: random data destination not provided");
return false;
}
OEMCryptoResult sts = OEMCrypto_GetRandom(random_data, data_length);
if (sts != OEMCrypto_SUCCESS) {

View File

@@ -285,7 +285,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path");
LOGW("DeviceFiles::DeleteLicense: Unable to get base path");
return false;
}
path.append(key_set_id);
@@ -296,13 +296,13 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
bool DeviceFiles::DeleteAllLicenses() {
if (!initialized_) {
LOGW("DeviceFiles::DeleteLicense: not initialized");
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path");
LOGW("DeviceFiles::DeleteAllLicenses: Unable to get base path");
return false;
}
path.append(kWildcard);
@@ -311,6 +311,21 @@ bool DeviceFiles::DeleteAllLicenses() {
return file_->Remove(path);
}
bool DeviceFiles::DeleteAllFiles() {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllFiles: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::DeleteAllFiles: Unable to get base path");
return false;
}
return file_->Remove(path);
}
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
if (!initialized_) {
LOGW("DeviceFiles::LicenseExists: not initialized");

View File

@@ -9,6 +9,7 @@
#include "log.h"
#include "policy_engine.h"
#include "properties.h"
#include "privacy_crypto.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
@@ -20,6 +21,41 @@ std::string kDeviceNameKey = "device_name";
std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
const unsigned char kServiceCertificateCAPublicKey[] = {
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xa5, 0x62, 0x07,
0xdf, 0xc8, 0x84, 0x74, 0xe1, 0x2a, 0xb7, 0xbb, 0xc0, 0x78, 0x76, 0xbe,
0x13, 0x3b, 0xe6, 0x2c, 0x09, 0x9d, 0x35, 0x3f, 0xf3, 0x0f, 0xe9, 0x61,
0x96, 0x20, 0x53, 0x6e, 0x78, 0x62, 0xe0, 0x10, 0xd2, 0xca, 0xe4, 0xdd,
0xd5, 0x96, 0xaf, 0x9a, 0xd7, 0x08, 0x47, 0xe4, 0x55, 0x1b, 0x83, 0xbe,
0x10, 0x66, 0x74, 0x08, 0xf2, 0x49, 0x79, 0xea, 0x29, 0x46, 0xc2, 0x65,
0x97, 0xa6, 0xcc, 0x4b, 0xa4, 0x08, 0xc3, 0x04, 0x17, 0x01, 0xb5, 0x11,
0x53, 0xe9, 0x68, 0x34, 0x3c, 0x26, 0x56, 0x44, 0x37, 0x5c, 0xb4, 0x7a,
0x1d, 0x5d, 0x6c, 0x58, 0xc2, 0x82, 0xa0, 0x92, 0xf1, 0x14, 0xf1, 0x22,
0xff, 0x64, 0xde, 0xdf, 0xb3, 0x3d, 0x9d, 0xa5, 0x86, 0xcd, 0xa0, 0x0a,
0x63, 0x08, 0xdd, 0x60, 0x5d, 0xfd, 0xa4, 0x01, 0xe3, 0xb6, 0x0e, 0x85,
0xe4, 0xc3, 0x37, 0x61, 0xd0, 0xe7, 0x12, 0xe9, 0xc4, 0xde, 0xf2, 0x59,
0x11, 0xe3, 0x5b, 0x02, 0x9f, 0x24, 0xb9, 0xb0, 0xbb, 0x31, 0xa0, 0xee,
0x6a, 0x2c, 0xb4, 0x30, 0xff, 0xe0, 0xf0, 0x93, 0xee, 0x3a, 0xae, 0xb2,
0x2e, 0x84, 0xa0, 0x47, 0x42, 0x51, 0xbb, 0xfa, 0xbb, 0x90, 0x97, 0x2c,
0x77, 0x45, 0xee, 0x2c, 0xfb, 0xec, 0x5d, 0xd8, 0xca, 0x49, 0x94, 0x53,
0x5d, 0x37, 0xaf, 0x86, 0x47, 0xda, 0xe2, 0xbd, 0xf0, 0x5f, 0x07, 0x53,
0x8a, 0x10, 0xd0, 0x9a, 0xd0, 0x7f, 0xe9, 0xef, 0xf6, 0xda, 0xea, 0x1e,
0x2e, 0x54, 0xec, 0x44, 0xde, 0x3a, 0xe1, 0xc8, 0xdb, 0x17, 0xe8, 0xc9,
0x3a, 0x81, 0x11, 0x4d, 0xb7, 0x2d, 0x09, 0x83, 0xab, 0x30, 0xb7, 0xf5,
0x1b, 0x03, 0x86, 0x21, 0xa9, 0xf5, 0xca, 0x15, 0x26, 0xaf, 0x39, 0xf3,
0x5d, 0x01, 0x7d, 0xe3, 0x19, 0x54, 0xd1, 0x2e, 0x10, 0x16, 0x9c, 0xee,
0xc3, 0xbd, 0xcc, 0xdb, 0x02, 0x82, 0xd0, 0x60, 0x0b, 0x42, 0x72, 0x85,
0xec, 0xdc, 0x41, 0x7c, 0xf1, 0x34, 0xd8, 0x27, 0x21, 0xf9, 0xa6, 0x82,
0x40, 0xd3, 0xc5, 0xc9, 0xf9, 0x6b, 0xc9, 0x12, 0x64, 0xe4, 0x3a, 0x3b,
0xc9, 0x8f, 0x3c, 0xd0, 0x2c, 0xb8, 0xb8, 0xf3, 0x05, 0x4a, 0xe9, 0x4c,
0x46, 0x2b, 0xb6, 0xe1, 0xed, 0x82, 0xb2, 0xf0, 0xd1, 0x72, 0x71, 0x04,
0x35, 0x19, 0xc1, 0x16, 0x17, 0xd6, 0x75, 0xe0, 0xab, 0xde, 0x8f, 0xe1,
0xc1, 0x49, 0x68, 0x0c, 0xc8, 0xce, 0x6d, 0x87, 0x50, 0x04, 0xb5, 0xd7,
0x24, 0xf4, 0x2e, 0x0c, 0x11, 0x35, 0xb2, 0x67, 0x85, 0x1b, 0x38, 0xff,
0x2f, 0x71, 0xf5, 0x30, 0x18, 0x1e, 0x6f, 0xd7, 0xf0, 0x33, 0x61, 0x53,
0x7e, 0x55, 0x7f, 0x0d, 0x60, 0x83, 0xf3, 0x8a, 0x2b, 0x67, 0xd5, 0xf0,
0x2e, 0x23, 0x23, 0x60, 0x0b, 0x83, 0x9c, 0xc2, 0x87, 0x02, 0x03, 0x01,
0x00, 0x01};
}
namespace wvcdm {
@@ -27,6 +63,8 @@ namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ClientIdentification_NameValue;
using video_widevine_server::sdk::DeviceCertificate;
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;
@@ -35,6 +73,7 @@ using video_widevine_server::sdk::
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_KeyContainer;
using video_widevine_server::sdk::LicenseError;
using video_widevine_server::sdk::SignedDeviceCertificate;
using video_widevine_server::sdk::SignedMessage;
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
@@ -81,36 +120,65 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
return key_array;
}
CdmLicense::CdmLicense() : session_(NULL) {}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine) {
if (token.size() == 0) return false;
if (session == NULL || !session->IsOpen()) return false;
if (token.size() == 0) {
LOGE("CdmLicense::Init: empty token provided");
return false;
}
if (session == NULL || !session->IsOpen()) {
LOGE("CdmLicense::Init: crypto session not provided or not open");
return false;
}
if (policy_engine == NULL) {
LOGE("CdmLicense::Init: no policy engine provided");
return false;
}
token_ = token;
session_ = session;
policy_engine_ = policy_engine;
initialized_ = true;
return true;
}
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id,
CdmKeyMessage* signed_request,
std::string* server_url) {
if (!session_ || token_.empty()) {
if (!initialized_) {
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return false;
}
if (init_data.empty()) {
LOGE("CdmLicense::PrepareKeyRequest : No init data provided;");
if (init_data.empty() && init_data_.empty()) {
LOGE("CdmLicense::PrepareKeyRequest: empty init data provided");
return false;
}
if (session_id.empty()) {
LOGE("CdmLicense::PrepareKeyRequest: empty session id provided");
return false;
}
if (!signed_request) {
LOGE("CdmLicense::PrepareKeyRequest : No signed request provided.");
LOGE("CdmLicense::PrepareKeyRequest: no signed request provided");
return false;
}
if (!server_url) {
LOGE("CdmLicense::PrepareKeyRequest: no server url provided");
return false;
}
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id);
std::vector<uint8_t> cert = Properties::GetServiceCertificate(session_id);
std::string serialized_service_certificate(cert.begin(), cert.end());
if (serialized_service_certificate.empty())
serialized_service_certificate = service_certificate_;
if (privacy_mode_enabled && serialized_service_certificate.empty()) {
init_data_ = init_data;
return PrepareServiceCertificateRequest(signed_request, server_url);
}
// TODO(gmorgan): Request ID owned by session?
std::string request_id;
@@ -170,13 +238,71 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
client_info->set_value(value);
}
if (privacy_mode_enabled) {
EncryptedClientIdentification* encrypted_client_id =
license_request.mutable_encrypted_client_id();
DeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(serialized_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());
return false;
}
encrypted_client_id->set_service_id(service_certificate.service_id());
encrypted_client_id->set_service_certificate_serial_number(
service_certificate.serial_number());
std::string iv(KEY_IV_SIZE, 0);
std::string key(KEY_SIZE, 0);
if (!session_->GetRandom(key.size(), reinterpret_cast<uint8_t*>(&key[0]))) {
return false;
}
if (!session_->GetRandom(iv.size(), reinterpret_cast<uint8_t*>(&iv[0]))) {
return false;
}
std::string id, enc_id, enc_key;
client_id->SerializeToString(&id);
AesCbcKey aes;
if (!aes.Init(key)) return false;
if (!aes.Encrypt(id, &enc_id, &iv)) return false;
RsaPublicKey rsa;
if (!rsa.Init(service_certificate.public_key())) return false;
if (!rsa.Encrypt(key, &enc_key)) return false;
encrypted_client_id->set_encrypted_client_id_iv(iv);
encrypted_client_id->set_encrypted_privacy_key(enc_key);
encrypted_client_id->set_encrypted_client_id(enc_id);
license_request.clear_client_id();
}
// Content Identification may be a cenc_id, a webm_id or a license_id
LicenseRequest_ContentIdentification* content_id =
license_request.mutable_content_id();
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_);
} else {
LOGD("CdmLicense::PrepareKeyRequest: init data not available");
return false;
}
switch (license_type) {
case kLicenseTypeOffline:
@@ -205,7 +331,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
if (!session_->GenerateNonce(&nonce)) {
return false;
}
license_request.set_key_control_nonce(UintToString(nonce));
license_request.set_key_control_nonce(nonce);
LOGD("PrepareKeyRequest: nonce=%u", nonce);
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
@@ -245,8 +371,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
CdmKeyMessage* signed_request,
std::string* server_url) {
if (!session_) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: Invalid crypto session");
if (!initialized_) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized");
return false;
}
if (!signed_request) {
@@ -274,7 +400,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
if (!session_->GenerateNonce(&nonce)) {
return false;
}
license_request.set_key_control_nonce(UintToString(nonce));
license_request.set_key_control_nonce(nonce);
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
@@ -289,7 +415,8 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
return false;
if (license_request_signature.empty()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: empty license request"
LOGE(
"CdmLicense::PrepareKeyUpdateRequest: empty license request"
" signature");
return false;
}
@@ -307,28 +434,46 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
CdmResponseType CdmLicense::HandleKeyResponse(
const CdmKeyResponse& license_response) {
if (!session_) {
if (!initialized_) {
LOGE("CdmLicense::HandleKeyResponse: not initialized");
return KEY_ERROR;
}
if (license_response.empty()) {
LOGE("CdmLicense::HandleKeyResponse : Empty license response.");
LOGE("CdmLicense::HandleKeyResponse: empty license response");
return KEY_ERROR;
}
if (service_certificate_response_pending_)
return CdmLicense::HandleServiceCertificateResponse(license_response);
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) return KEY_ERROR;
if (!signed_response.ParseFromString(license_response)) {
LOGE(
"CdmLicense::HandleKeyResponse: unable to parse signed license"
" response");
return KEY_ERROR;
}
if (signed_response.type() == SignedMessage::ERROR) {
return HandleKeyErrorResponse(signed_response);
}
if (!signed_response.has_signature()) return KEY_ERROR;
if (!signed_response.has_signature()) {
LOGE("CdmLicense::HandleKeyResponse: license response is not signed");
return KEY_ERROR;
}
License license;
if (!license.ParseFromString(signed_response.msg())) return KEY_ERROR;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::HandleKeyResponse: unable to parse license response");
return KEY_ERROR;
}
if (Properties::use_certificates_as_identification()) {
if (!signed_response.has_session_key()) return KEY_ERROR;
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::HandleKeyResponse: no session keys present");
return KEY_ERROR;
}
if (!session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
@@ -349,6 +494,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());
return KEY_ERROR;
}
}
@@ -374,9 +523,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
}
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
bool is_renewal,
const CdmKeyResponse& license_response) {
if (!session_) {
bool is_renewal, const CdmKeyResponse& license_response) {
if (!initialized_) {
LOGE("CdmLicense::HandleKeyUpdateResponse: not initialized");
return KEY_ERROR;
}
if (license_response.empty()) {
@@ -401,7 +550,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
LOGE(
"CdmLicense::HandleKeyUpdateResponse: Unable to parse license"
" from signed message");
return KEY_ERROR;
}
@@ -423,15 +573,12 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
// merge from Eureka)
policy_engine_->UpdateLicense(license);
if (!is_renewal)
return KEY_ADDED;
if (!is_renewal) return KEY_ADDED;
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
if (session_->RefreshKeys(signed_response.msg(),
signed_response.signature(),
key_array.size(),
&key_array[0])) {
if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(),
key_array.size(), &key_array[0])) {
return KEY_ADDED;
} else {
return KEY_ERROR;
@@ -439,13 +586,14 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
}
bool CdmLicense::RestoreOfflineLicense(
CdmKeyMessage& license_request,
CdmKeyResponse& license_response,
CdmKeyMessage& license_request, CdmKeyResponse& license_response,
CdmKeyResponse& license_renewal_response) {
if (license_request.empty() || license_response.empty()) {
LOGE("CdmLicense::RestoreOfflineLicense: key_request or response empty: "
"%u %u", license_request.size(), license_response.size());
LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
"%u %u",
license_request.size(), license_response.size());
return false;
}
@@ -456,36 +604,139 @@ bool CdmLicense::RestoreOfflineLicense(
}
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
LOGE("CdmLicense::RestoreOfflineLicense: license request type: expected = "
LOGE(
"CdmLicense::RestoreOfflineLicense: license request type: expected = "
"%d, actual = %d",
SignedMessage::LICENSE_REQUEST,
signed_request.type());
SignedMessage::LICENSE_REQUEST, signed_request.type());
return false;
}
if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg();
}
else {
if (!session_->GenerateDerivedKeys(signed_request.msg()))
return false;
} else {
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false;
}
CdmResponseType sts = HandleKeyResponse(license_response);
if (sts != KEY_ADDED)
return false;
if (sts != KEY_ADDED) return false;
if (!license_renewal_response.empty()) {
sts = HandleKeyUpdateResponse(true, license_renewal_response);
if (sts != KEY_ADDED)
return false;
if (sts != KEY_ADDED) return false;
}
return true;
}
bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense::PrepareServiceCertificateRequest: not initialized");
return false;
}
if (!signed_request) {
LOGE(
"CdmLicense::PrepareServiceCertificateRequest: no signed request"
" provided");
return false;
}
if (!server_url) {
LOGE(
"CdmLicense::PrepareServiceCertificateRequest: no server url"
" provided");
return false;
}
SignedMessage signed_message;
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
signed_message.SerializeToString(signed_request);
*server_url = server_url_;
service_certificate_response_pending_ = true;
return true;
}
CdmResponseType CdmLicense::HandleServiceCertificateResponse(
const CdmKeyResponse& service_certificate_response) {
if (!initialized_) {
LOGE("CdmLicense::HandleServiceCertificateResponse: not initialized");
return KEY_ERROR;
}
if (service_certificate_response.empty()) {
LOGE(
"CdmLicense::HandleServiceCertificateResponse: empty service "
"certificate response");
return KEY_ERROR;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(service_certificate_response))
return KEY_ERROR;
switch (signed_response.type()) {
case SignedMessage::ERROR:
return HandleKeyErrorResponse(signed_response);
case SignedMessage::SERVICE_CERTIFICATE:
break; // expected message type
default:
LOGE(
"CdmLicense::HandleServiceCertificateResponse: unexpected signed"
"message type: %d",
signed_response.type());
return KEY_ERROR;
}
SignedDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(signed_response.msg())) {
LOGE(
"CdmLicense::HandleServiceCertificateResponse: unable to parse"
"signed device certificate");
return KEY_ERROR;
}
RsaPublicKey root_ca_key;
std::vector<uint8_t> ca_public_key(
&kServiceCertificateCAPublicKey[0],
&kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]);
if (!root_ca_key.Init(b2a_hex(ca_public_key))) {
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");
return KEY_ERROR;
}
DeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(
signed_service_certificate.device_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());
return KEY_ERROR;
}
service_certificate_ = signed_service_certificate.device_certificate();
return NEED_KEY;
}
CdmResponseType CdmLicense::HandleKeyErrorResponse(
const SignedMessage& signed_message) {
@@ -496,9 +747,9 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
}
switch (license_error.error_code()) {
case LicenseError::INVALID_CREDENTIALS:
case LicenseError::INVALID_DEVICE_CERTIFICATE:
return NEED_PROVISIONING;
case LicenseError::REVOKED_CREDENTIALS:
case LicenseError::REVOKED_DEVICE_CERTIFICATE:
return DEVICE_REVOKED;
case LicenseError::SERVICE_UNAVAILABLE:
default:

View File

@@ -8,6 +8,17 @@
// inline to avoid having to hardcode the import path. This is a temporary
// workaround for not getting proto_path to work in Android build envionment.
//
// Origin:
// This file is derived from the authoritative source file at
// https://cs.corp.google.com/#google3/video/widevine/server/sdk/
// license_protocol.proto
//
// Description:
// Definitions of the protocol buffer messages used in the Widevine license
// exchange protocol, which is described in the document
// https://docs.google.com/a/google.com/document/d/
// 1cng6cDnchbDQDymLEd5MxMc_laS3EDv6IsoW3IzpgwQ
syntax = "proto2";
package video_widevine_server.sdk;
@@ -116,7 +127,8 @@ message License {
// |key_control| is documented here:
// https://docs.google.com/a/google.com/document/d/17eDxzzGpPc2qSm7zW68_5ensuxbHErYCvD3IxSKETRo/edit#
// If present, the key control must be communicated to the secure
// environment prior to any usage.
// environment prior to any usage. This message is automatically generated
// by the Widevine License Server SDK.
optional bytes key_control_block = 1;
optional bytes iv = 2;
}
@@ -140,6 +152,16 @@ message License {
}
optional CGMS cgms_flags = 2 [default = CGMS_NONE];
}
message OperatorSessionKeyPermissions {
// Permissions/key usage flags for operator service keys
// (type = OPERATOR_SESSION).
optional bool allow_encrypt = 1 [default = false];
optional bool allow_decrypt = 2 [default = false];
optional bool allow_sign = 3 [default = false];
optional bool allow_signature_verify = 4 [default = false];
}
optional bytes id = 1;
optional bytes iv = 2;
optional bytes key = 3;
@@ -148,6 +170,7 @@ message License {
optional OutputProtection required_protection = 6;
optional OutputProtection requested_protection = 7;
optional KeyControl key_control = 8;
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
}
optional LicenseIdentification id = 1;
@@ -194,20 +217,32 @@ message LicenseRequest {
// The client_id provides information authenticating the calling device. It
// contains the Widevine keybox token that was installed on the device at the
// factory. This field is required for a valid license request.
// factory. This field or encrypted_client_id below is required for a valid
// license request, but both should never be present in the same request.
optional ClientIdentification client_id = 1;
optional ContentIdentification content_id = 2;
optional RequestType type = 3;
optional int64 request_time = 4;
optional bytes key_control_nonce = 5;
// Old-style decimal-encoded string key control nonce.
optional bytes key_control_nonce_deprecated = 5;
optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0];
// New-style uint32 key control nonce, please use instead of
// key_control_nonce_deprecated.
optional uint32 key_control_nonce = 7;
// Encrypted ClientIdentification message, used for privacy purposes.
optional EncryptedClientIdentification encrypted_client_id = 8;
}
message LicenseError {
enum Error {
INVALID_CREDENTIALS = 1;
REVOKED_CREDENTIALS = 2;
// The device credentials are invalid. The device must re-provision.
INVALID_DEVICE_CERTIFICATE = 1;
// The device credentials have been revoked. Re-provisioning is not
// possible.
REVOKED_DEVICE_CERTIFICATE = 2;
// The service is currently unavailable due to the backend being down
// or similar circumstances.
SERVICE_UNAVAILABLE = 3;
}
optional Error error_code = 1;
@@ -218,6 +253,8 @@ message SignedMessage {
LICENSE_REQUEST = 1;
LICENSE = 2;
ERROR = 3;
SERVICE_CERTIFICATE_REQUEST = 4;
SERVICE_CERTIFICATE = 5;
}
optional MessageType type = 1;
@@ -230,7 +267,10 @@ message SignedMessage {
message SessionInit {
optional bytes session_id = 1;
optional bytes purchase_id = 2;
// master_signing_key should be 128 bits in length.
optional bytes master_signing_key = 3;
// signing_key should be 512 bits in length to be split into two
// (server || client) HMAC-SHA256 keys.
optional bytes signing_key = 4;
optional int64 license_start_time = 5;
}
@@ -267,7 +307,7 @@ message ProvisioningResponse {
optional bytes device_rsa_key = 1;
// Initialization vector used to encrypt device_rsa_key. Required.
optional bytes device_rsa_key_iv = 2;
// Serialized DeviceCertificate. Required.
// Serialized SignedDeviceCertificate. Required.
optional bytes device_certificate = 3;
// Nonce value matching nonce in ProvisioningRequest. Required.
optional bytes nonce = 4;
@@ -288,10 +328,13 @@ message SignedProvisioningMessage {
// Copyright 2013 Google Inc. All Rights Reserved.
// Author: tinskip@google.com (Thomas Inskip)
//
// Origin:
// This file is derived from the authoritative source file at
// https://cs.corp.google.com/#google3/video/widevine/server/sdk/
// license_protocol.proto
//
// Description:
// ClientIdentification message used by provisioning and license protocols.
option java_outer_classname = "ClientIdentificationProtos";
// ClientIdentification messages used by provisioning and license protocols.
// ClientIdentification message used to authenticate the client device.
message ClientIdentification {
@@ -312,3 +355,138 @@ message ClientIdentification {
// Optional client information name/value pairs.
repeated NameValue client_info = 3;
}
// EncryptedClientIdentification message used to hold ClientIdentification
// messages encrypted for privacy purposes.
message EncryptedClientIdentification {
// Service ID for which the ClientIdentifcation is encrypted (owner of service
// certificate).
optional string service_id = 1;
// Serial number for the service certificate for which ClientIdentification is
// encrypted.
optional string service_certificate_serial_number = 2;
// Serialized ClientIdentification message, encrypted with the privacy key using
// AES-128-CBC with PKCS#5 padding.
optional bytes encrypted_client_id = 3;
// Initialization vector needed to decrypt encrypted_client_id.
optional bytes encrypted_client_id_iv = 4;
// AES-128 privacy key, encrytped with the service public public key using
// RSA-OAEP.
optional bytes encrypted_privacy_key = 5;
};
// ----------------------------------------------------------------------------
// device_certificate.proto
// ----------------------------------------------------------------------------
// Copyright 2013 Google Inc. All Rights Reserved.
// Author: tinskip@google.com (Thomas Inskip)
//
// Description:
// Device certificate and certificate status list format definitions.
// Certificate definition for user devices, intermediate, service, and root
// certificates.
message DeviceCertificate {
enum CertificateType {
ROOT = 0;
INTERMEDIATE = 1;
USER_DEVICE = 2;
SERVICE = 3;
}
// Type of certificate. Required.
optional CertificateType type = 1;
// 128-bit globally unique serial number of certificate.
// Value is 0 for root certificate. Required.
optional bytes serial_number = 2;
// POSIX time, in seconds, when the certificate was created. Required.
optional uint32 creation_time_seconds = 3;
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
optional bytes public_key = 4;
// Widevine system ID for the device. Required for intermediate and
// user device certificates.
optional uint32 system_id = 5;
// True if the certificate corresponds to a test (non production) device or
// service. Optional.
optional bool test_device = 6 [default = false];
// Service identifier (web origin) for the service which owns the certificate.
// Required for service certificates.
optional string service_id = 7;
}
// DeviceCertificate signed with intermediate or root certificate private key.
message SignedDeviceCertificate {
// Serialized DeviceCertificate. Required.
optional bytes device_certificate = 1;
// Signature of device_certificate. Signed with root or intermediate
// certificate private key using RSASSA-PSS. Required.
optional bytes signature = 2;
// Intermediate signing certificate. Present only for user device
// certificates. All others signed with root certificate private key.
optional SignedDeviceCertificate signer = 3;
}
// Contains device model information for a provisioned device.
message ProvisionedDeviceInfo {
enum WvSecurityLevel {
// Defined in Widevine Security Integration Guide for DASH on Android:
// https://docs.google.com/a/google.com/document/d/1Zum-fcJeoIw6KG1kDP_KepIE5h9gAZg0PaMtemBvk9c/edit#heading=h.1t3h5sf
LEVEL_UNSPECIFIED = 0;
LEVEL_1 = 1;
LEVEL_2 = 2;
LEVEL_3 = 3;
}
// Widevine system ID for the device. Mandatory.
optional uint32 system_id = 1;
// Name of system-on-a-chip. Optional.
optional string soc = 2;
// Name of manufacturer. Optional.
optional string manufacturer = 3;
// Manufacturer's model name. Matches "brand" in device metadata. Optional.
optional string model = 4;
// Type of device (Phone, Tablet, TV, etc).
optional string device_type = 5;
// Device model year. Optional.
optional uint32 model_year = 6;
// Widevine-defined security level. Optional.
optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED];
// True if the certificate corresponds to a test (non production) device.
// Optional.
optional bool test_device = 8 [default = false];
}
// Contains the status of the root or an intermediate DeviceCertificate.
message DeviceCertificateStatus {
enum CertificateStatus {
VALID = 0;
REVOKED = 1;
};
// Serial number of the DeviceCertificate to which this message refers.
// Required.
optional bytes serial_number = 1;
// Status of the certificate. Optional.
optional CertificateStatus status = 2 [default = VALID];
// Device model information about the device to which the certificate
// corresponds. Required.
optional ProvisionedDeviceInfo device_info = 4;
}
// List of DeviceCertificateStatus. Used to propagate certificate revocation and
// update list.
message DeviceCertificateStatusList {
// POSIX time, in seconds, when the list was created. Required.
optional uint32 creation_time_seconds = 1;
// DeviceCertificateStatus for each certifificate.
repeated DeviceCertificateStatus certificate_status = 2;
}
// Signed CertificateStatusList
message SignedCertificateStatusList {
// Serialized DeviceCertificateStatusList. Required.
optional bytes certificate_status_list = 1;
// Signature of certificate_status_list. Signed with root certificate private
// key using RSASSA-PSS. Required.
optional bytes signature = 2;
}

View File

@@ -0,0 +1,665 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* Wrapper of OEMCrypto APIs for platforms that support both Levels 1 and 3.
* This should be used when liboemcrypto.so is dynamically loaded at run
* time and not 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.
*
******************************************************************************/
#include "oemcrypto_adapter.h"
#include <dlfcn.h>
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string>
#include <map>
#include "level3.h"
#include "log.h"
#include "file_store.h"
#include "properties.h"
using namespace wvoec_level3;
namespace wvcdm {
typedef OEMCryptoResult (*L1_Initialize_t)(void);
typedef OEMCryptoResult (*L1_Terminate_t)(void);
typedef OEMCryptoResult (*L1_OpenSession_t)(OEMCrypto_SESSION* session);
typedef OEMCryptoResult (*L1_CloseSession_t)(OEMCrypto_SESSION session);
typedef OEMCryptoResult (*L1_GenerateDerivedKeys_t)(
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
uint32_t enc_key_context_length);
typedef OEMCryptoResult (*L1_GenerateNonce_t)(OEMCrypto_SESSION session,
uint32_t* nonce);
typedef OEMCryptoResult (*L1_GenerateSignature_t)(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
typedef OEMCryptoResult (*L1_LoadKeys_t)(
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);
typedef OEMCryptoResult (*L1_RefreshKeys_t)(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array);
typedef OEMCryptoResult (*L1_SelectKey_t)(const OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length);
typedef OEMCryptoResult (*L1_DecryptCTR_t)(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv, size_t offset,
const OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags);
typedef OEMCryptoResult (*L1_InstallKeybox_t)(const uint8_t* keybox,
size_t keyBoxLength);
typedef OEMCryptoResult (*L1_IsKeyboxValid_t)(void);
typedef OEMCryptoResult (*L1_GetDeviceID_t)(uint8_t* deviceID,
size_t* idLength);
typedef OEMCryptoResult (*L1_GetKeyData_t)(uint8_t* keyData,
size_t* keyDataLength);
typedef OEMCryptoResult (*L1_GetRandom_t)(uint8_t* randomData,
size_t dataLength);
typedef OEMCryptoResult (*L1_WrapKeybox_t)(const uint8_t* keybox,
size_t keyBoxLength,
uint8_t* wrappedKeybox,
size_t* wrappedKeyBoxLength,
const uint8_t* transportKey,
size_t transportKeyLength);
typedef OEMCryptoResult (*L1_RewrapDeviceRSAKey_t)(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, const uint32_t* nonce,
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length);
typedef OEMCryptoResult (*L1_LoadDeviceRSAKey_t)(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length);
typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)(
OEMCrypto_SESSION session, const uint8_t* enc_session_key,
size_t enc_session_key_length, const uint8_t* mac_key_context,
size_t mac_key_context_length, const uint8_t* enc_key_context,
size_t enc_key_context_length);
typedef OEMCryptoResult (*L1_Generic_Encrypt_t)(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer);
typedef OEMCryptoResult (*L1_Generic_Decrypt_t)(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer);
typedef OEMCryptoResult (*L1_Generic_Sign_t)(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length);
typedef OEMCryptoResult (*L1_Generic_Verify_t)(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
typedef uint32_t (*L1_APIVersion_t)();
typedef const char* (*L1_SecurityLevel_t)();
struct FunctionPointers {
L1_Initialize_t Initialize;
L1_Terminate_t Terminate;
L1_OpenSession_t OpenSession;
L1_CloseSession_t CloseSession;
L1_GenerateDerivedKeys_t GenerateDerivedKeys;
L1_GenerateNonce_t GenerateNonce;
L1_GenerateSignature_t GenerateSignature;
L1_LoadKeys_t LoadKeys;
L1_RefreshKeys_t RefreshKeys;
L1_SelectKey_t SelectKey;
L1_DecryptCTR_t DecryptCTR;
L1_InstallKeybox_t InstallKeybox;
L1_IsKeyboxValid_t IsKeyboxValid;
L1_GetDeviceID_t GetDeviceID;
L1_GetKeyData_t GetKeyData;
L1_GetRandom_t GetRandom;
L1_WrapKeybox_t WrapKeybox;
L1_RewrapDeviceRSAKey_t RewrapDeviceRSAKey;
L1_LoadDeviceRSAKey_t LoadDeviceRSAKey;
L1_GenerateRSASignature_t GenerateRSASignature;
L1_DeriveKeysFromSessionKey_t DeriveKeysFromSessionKey;
L1_APIVersion_t APIVersion;
L1_SecurityLevel_t SecurityLevel;
L1_Generic_Encrypt_t Generic_Encrypt;
L1_Generic_Decrypt_t Generic_Decrypt;
L1_Generic_Sign_t Generic_Sign;
L1_Generic_Verify_t Generic_Verify;
};
struct LevelSession {
FunctionPointers* fcn;
OEMCrypto_SESSION session;
LevelSession() : fcn(0), session(0) {};
};
#define QUOTE_DEFINE(A) #A
#define QUOTE(A) QUOTE_DEFINE(A)
#define LOOKUP(Name, Function) \
level1_.Name = \
(L1_##Name##_t)dlsym(level1_library_, QUOTE(Function)); \
if (!level1_.Name) { \
LOGW("Could not load L1 %s. Falling Back to L3.", \
QUOTE(OEMCrypto_##Name)); \
return false; \
}
class Adapter {
public:
typedef std::map<OEMCrypto_SESSION, LevelSession>::iterator map_iterator;
Adapter() : level1_valid_(false), level1_library_(NULL) {}
~Adapter() {
for (map_iterator i = session_map_.begin(); i != session_map_.end(); i++) {
if (i->second.fcn) i->second.fcn->CloseSession(i->second.session);
}
session_map_.clear();
}
OEMCryptoResult Initialize() {
LoadLevel3();
OEMCryptoResult result = Level3_Initialize();
std::string library_name;
if (!wvcdm::Properties::GetOEMCryptoPath(&library_name)) {
LOGW("L1 library not specified. Falling Back to L3");
return result;
}
level1_library_ = dlopen(library_name.c_str(), RTLD_NOW);
if (level1_library_ == NULL) {
LOGW("Could not load %s. Falling Back to L3. %s", library_name.c_str(),
dlerror());
return result;
}
if (LoadLevel1()) {
LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1.");
} else {
dlclose(level1_library_);
level1_library_ = NULL;
level1_valid_ = false;
}
return result;
}
bool LoadLevel1() {
level1_valid_ = true;
LOOKUP(Initialize, OEMCrypto_Initialize);
LOOKUP(Terminate, OEMCrypto_Terminate);
LOOKUP(OpenSession, OEMCrypto_OpenSession);
LOOKUP(CloseSession, OEMCrypto_CloseSession);
LOOKUP(GenerateDerivedKeys, OEMCrypto_GenerateDerivedKeys);
LOOKUP(GenerateNonce, OEMCrypto_GenerateNonce);
LOOKUP(GenerateSignature, OEMCrypto_GenerateSignature);
LOOKUP(LoadKeys, OEMCrypto_LoadKeys);
LOOKUP(RefreshKeys, OEMCrypto_RefreshKeys);
LOOKUP(SelectKey, OEMCrypto_SelectKey);
LOOKUP(DecryptCTR, OEMCrypto_DecryptCTR);
LOOKUP(InstallKeybox, OEMCrypto_InstallKeybox);
LOOKUP(IsKeyboxValid, OEMCrypto_IsKeyboxValid);
LOOKUP(GetDeviceID, OEMCrypto_GetDeviceID);
LOOKUP(GetKeyData, OEMCrypto_GetKeyData);
LOOKUP(GetRandom, OEMCrypto_GetRandom);
LOOKUP(WrapKeybox, OEMCrypto_WrapKeybox);
LOOKUP(RewrapDeviceRSAKey, OEMCrypto_RewrapDeviceRSAKey);
LOOKUP(LoadDeviceRSAKey, OEMCrypto_LoadDeviceRSAKey);
LOOKUP(GenerateRSASignature, OEMCrypto_GenerateRSASignature);
LOOKUP(DeriveKeysFromSessionKey, OEMCrypto_DeriveKeysFromSessionKey);
LOOKUP(APIVersion, OEMCrypto_APIVersion);
LOOKUP(SecurityLevel, OEMCrypto_SecurityLevel);
LOOKUP(Generic_Decrypt, OEMCrypto_Generic_Decrypt);
LOOKUP(Generic_Encrypt, OEMCrypto_Generic_Encrypt);
LOOKUP(Generic_Sign, OEMCrypto_Generic_Sign);
LOOKUP(Generic_Verify, OEMCrypto_Generic_Verify);
if (!level1_valid_) {
return false;
}
OEMCryptoResult st = level1_.Initialize();
if (st != OEMCrypto_SUCCESS) {
LOGW("Could not initialize L1. Falling Back to L3.");
return false;
}
uint32_t level1_version = level1_.APIVersion();
if (level1_version != oec_latest_version) {
LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.",
level1_version, oec_latest_version);
return false;
}
if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) {
return true;
}
wvcdm::File file;
std::string filename;
if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) {
LOGW("Bad Level 1 Keybox. Falling Back to L3.");
return false;
}
ssize_t size = file.FileSize(filename);
if (size <= 0 || !file.Open(filename, file.kBinary | file.kReadOnly)) {
LOGW("Could not open %s. Falling Back to L3.", filename.c_str());
return false;
}
uint8_t keybox[size];
ssize_t size_read = file.Read(reinterpret_cast<char*>(keybox), size);
if (level1_.InstallKeybox(keybox, size) != OEMCrypto_SUCCESS) {
LOGE("Could NOT install keybox from %s. Falling Back to L3.",
filename.c_str());
false;
}
LOGI("Installed keybox from %s", filename.c_str());
return true;
}
void LoadLevel3() {
level3_.Initialize = Level3_Initialize;
level3_.Terminate = Level3_Terminate;
level3_.OpenSession = Level3_OpenSession;
level3_.CloseSession = Level3_CloseSession;
level3_.GenerateDerivedKeys = Level3_GenerateDerivedKeys;
level3_.GenerateNonce = Level3_GenerateNonce;
level3_.GenerateSignature = Level3_GenerateSignature;
level3_.LoadKeys = Level3_LoadKeys;
level3_.RefreshKeys = Level3_RefreshKeys;
level3_.SelectKey = Level3_SelectKey;
level3_.DecryptCTR = Level3_DecryptCTR;
level3_.InstallKeybox = Level3_InstallKeybox;
level3_.IsKeyboxValid = Level3_IsKeyboxValid;
level3_.GetDeviceID = Level3_GetDeviceID;
level3_.GetKeyData = Level3_GetKeyData;
level3_.GetRandom = Level3_GetRandom;
level3_.WrapKeybox = Level3_WrapKeybox;
level3_.RewrapDeviceRSAKey = Level3_RewrapDeviceRSAKey;
level3_.LoadDeviceRSAKey = Level3_LoadDeviceRSAKey;
level3_.GenerateRSASignature = Level3_GenerateRSASignature;
level3_.DeriveKeysFromSessionKey = Level3_DeriveKeysFromSessionKey;
level3_.APIVersion = Level3_APIVersion;
level3_.SecurityLevel = Level3_SecurityLevel;
level3_.Generic_Decrypt = Level3_Generic_Decrypt;
level3_.Generic_Encrypt = Level3_Generic_Encrypt;
level3_.Generic_Sign = Level3_Generic_Sign;
level3_.Generic_Verify = Level3_Generic_Verify;
}
OEMCryptoResult Terminate() {
OEMCryptoResult result = Level3_Terminate();
if (level1_valid_) {
result = level1_.Terminate();
dlclose(level1_library_);
level1_library_ = NULL;
}
return result;
}
const FunctionPointers* get(SecurityLevel level) {
if (level1_valid_ && level == kLevelDefault) return &level1_;
return &level3_;
}
LevelSession get(OEMCrypto_SESSION session) {
map_iterator pair = session_map_.find(session);
if (pair == session_map_.end()) {
return LevelSession();
}
return pair->second;
}
OEMCryptoResult OpenSession(OEMCrypto_SESSION* session, SecurityLevel level) {
LevelSession new_session;
OEMCryptoResult result;
if (level == kLevelDefault && level1_valid_) {
new_session.fcn = &level1_;
result = level1_.OpenSession(&new_session.session);
*session = new_session.session;
} else {
new_session.fcn = &level3_;
result = level3_.OpenSession(&new_session.session);
*session = new_session.session + kLevel3Offset;
}
if (result == OEMCrypto_SUCCESS) {
// Make sure session is not already in my list of sessions.
while (session_map_.find(*session) != session_map_.end()) {
(*session)++;
}
session_map_[*session] = new_session;
}
return result;
}
OEMCryptoResult CloseSession(OEMCrypto_SESSION session) {
map_iterator pair = session_map_.find(session);
if (pair == session_map_.end()) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
OEMCryptoResult result =
pair->second.fcn->CloseSession(pair->second.session);
session_map_.erase(pair);
return result;
}
private:
bool level1_valid_;
void* level1_library_;
struct FunctionPointers level1_;
struct FunctionPointers level3_;
std::map<OEMCrypto_SESSION, LevelSession> session_map_;
// This is just for debugging the map between session ids.
// If we add this to the level 3 session id, then the external session
// id will match the internal session id in the last two digits.
static const OEMCrypto_SESSION kLevel3Offset = 25600;
};
static Adapter* kAdapter = 0;
extern "C" OEMCryptoResult OEMCrypto_Initialize(void) {
if (kAdapter) {
delete kAdapter;
}
kAdapter = new Adapter();
return kAdapter->Initialize();
}
extern "C" OEMCryptoResult OEMCrypto_Terminate(void) {
OEMCryptoResult result = OEMCrypto_SUCCESS;
if (kAdapter) {
result = kAdapter->Terminate();
delete kAdapter;
}
kAdapter = NULL;
return result;
}
extern "C" OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
return OEMCrypto_OpenSession(session, kLevelDefault);
}
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
SecurityLevel level) {
if (!kAdapter) return OEMCrypto_ERROR_OPEN_SESSION_FAILED;
return kAdapter->OpenSession(session, level);
}
extern "C" OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
if (!kAdapter) return OEMCrypto_ERROR_CLOSE_SESSION_FAILED;
return kAdapter->CloseSession(session);
}
extern "C" OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
uint32_t* nonce) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->GenerateNonce(pair.session, nonce);
}
extern "C" OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
uint32_t enc_key_context_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->GenerateDerivedKeys(pair.session, mac_key_context,
mac_key_context_length, enc_key_context,
enc_key_context_length);
}
extern "C" OEMCryptoResult OEMCrypto_GenerateSignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->GenerateSignature(pair.session, message, message_length,
signature, signature_length);
}
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) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->LoadKeys(pair.session, message, message_length, signature,
signature_length, enc_mac_key_iv, enc_mac_key,
num_keys, key_array);
}
extern "C" OEMCryptoResult OEMCrypto_RefreshKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->RefreshKeys(pair.session, message, message_length, signature,
signature_length, num_keys, key_array);
}
extern "C" OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->SelectKey(pair.session, key_id, key_id_length);
}
extern "C" OEMCryptoResult OEMCrypto_DecryptCTR(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv, size_t offset,
const OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->DecryptCTR(pair.session, data_addr, data_length,
is_encrypted, iv, offset, out_buffer,
subsample_flags);
}
extern "C" OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength) {
return OEMCrypto_InstallKeybox(keybox, keyBoxLength, kLevelDefault);
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = kAdapter->get(level);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return fcn->InstallKeybox(keybox, keyBoxLength);
}
extern "C" OEMCryptoResult OEMCrypto_IsKeyboxValid() {
return OEMCrypto_IsKeyboxValid(kLevelDefault);
}
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = kAdapter->get(level);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return fcn->IsKeyboxValid();
}
extern "C" OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
size_t* idLength) {
return OEMCrypto_GetDeviceID(deviceID, idLength, kLevelDefault);
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = kAdapter->get(level);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return fcn->GetDeviceID(deviceID, idLength);
}
extern "C" OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
size_t* keyDataLength) {
return OEMCrypto_GetKeyData(keyData, keyDataLength, kLevelDefault);
}
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
SecurityLevel level) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = kAdapter->get(level);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return fcn->GetKeyData(keyData, keyDataLength);
}
extern "C" OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
size_t dataLength) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = kAdapter->get(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return fcn->GetRandom(randomData, dataLength);
}
extern "C" OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox,
size_t keyBoxLength,
uint8_t* wrappedKeybox,
size_t* wrappedKeyBoxLength,
const uint8_t* transportKey,
size_t transportKeyLength) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = kAdapter->get(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return fcn->WrapKeybox(keybox, keyBoxLength, wrappedKeybox,
wrappedKeyBoxLength, transportKey, transportKeyLength);
}
extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, const uint32_t* nonce,
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->RewrapDeviceRSAKey(
pair.session, message, message_length, signature, signature_length, nonce,
enc_rsa_key, enc_rsa_key_length, enc_rsa_key_iv, wrapped_rsa_key,
wrapped_rsa_key_length);
}
extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn
->LoadDeviceRSAKey(pair.session, wrapped_rsa_key, wrapped_rsa_key_length);
}
extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->GenerateRSASignature(pair.session, message, message_length,
signature, signature_length);
}
extern "C" OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
OEMCrypto_SESSION session, const uint8_t* enc_session_key,
size_t enc_session_key_length, const uint8_t* mac_key_context,
size_t mac_key_context_length, const uint8_t* enc_key_context,
size_t enc_key_context_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->DeriveKeysFromSessionKey(
pair.session, enc_session_key, enc_session_key_length, mac_key_context,
mac_key_context_length, enc_key_context, enc_key_context_length);
}
extern "C" uint32_t OEMCrypto_APIVersion() {
return OEMCrypto_APIVersion(kLevelDefault);
}
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
if (!kAdapter) return 0;
const FunctionPointers* fcn = kAdapter->get(level);
if (!fcn) return 0;
return fcn->APIVersion();
}
extern "C" const char* OEMCrypto_SecurityLevel() {
return OEMCrypto_SecurityLevel(kLevelDefault);
}
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
if (!kAdapter) return "";
const FunctionPointers* fcn = kAdapter->get(level);
if (!fcn) return "";
return fcn->SecurityLevel();
}
extern "C" OEMCryptoResult OEMCrypto_Generic_Encrypt(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->Generic_Encrypt(pair.session, in_buffer, buffer_length, iv,
algorithm, out_buffer);
}
extern "C" OEMCryptoResult OEMCrypto_Generic_Decrypt(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->Generic_Decrypt(pair.session, in_buffer, buffer_length, iv,
algorithm, out_buffer);
}
extern "C" OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->Generic_Sign(pair.session, in_buffer, buffer_length,
algorithm, signature, signature_length);
}
extern "C" OEMCryptoResult OEMCrypto_Generic_Verify(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
OEMCrypto_Algorithm algorithm, const uint8_t* signature,
size_t signature_length) {
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
return pair.fcn->Generic_Verify(pair.session, in_buffer, buffer_length,
algorithm, signature, signature_length);
}
}; // namespace wvcdm

View File

@@ -0,0 +1,51 @@
/*******************************************************************************
*
* 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.
*
******************************************************************************/
#include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h"
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();
}
}; // namespace wvcdm

View File

@@ -0,0 +1,201 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Original code at //depot/google3/video/widevine/common/rsa_key.cc by
// tinskip@google.com. Modified for core CDM usage.
//
// Description:
// Definition of classes representing RSA public keys used
// for signature verification and encryption and decryption.
//
#include "privacy_crypto.h"
#include "log.h"
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/evp.h"
#include "openssl/pem.h"
#include "openssl/rsa.h"
#include "openssl/sha.h"
namespace {
const int kPssSaltLength = 20;
const int kRsaPkcs1OaepPaddingLength = 41;
const int kBitsInAByte = 8;
} // namespace
namespace wvcdm {
bool AesCbcKey::Init(const std::string& key) {
if (key.empty()) {
LOGE("AesCbcKey::Init: no key provided");
return false;
}
if (key.size() != AES_BLOCK_SIZE) {
LOGE("AesCbcKey::Init: unexpected key size: %d", key.size());
return false;
}
if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(&key[0]),
key.size() * kBitsInAByte, &key_) != 0) {
LOGE("AesCbcKey::Init: AES CBC key setup failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
}
initialized_ = true;
return true;
}
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
std::string* iv) {
if (in.empty()) {
LOGE("AesCbcKey::Encrypt: no cleartext provided");
return false;
}
if (in.size() % AES_BLOCK_SIZE) {
LOGE("AesCbcKey::Encrypt: cleartext not a block multiple: %d", in.size());
return false;
}
if (iv == NULL) {
LOGE("AesCbcKey::Encrypt: initialization vector destination not provided");
return false;
}
if (iv->size() != AES_BLOCK_SIZE) {
LOGE("AesCbcKey::Encrypt: invalid iv size: %d", iv->size());
return false;
}
if (out == NULL) {
LOGE("AesCbcKey::Encrypt: crypttext destination not provided");
return false;
}
if (!initialized_) {
LOGE("AesCbcKey::Encrypt: AES key not initialized");
return false;
}
out->resize(in.size());
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(&in[0]),
reinterpret_cast<uint8_t*>(&out[0]), in.size(), &key_,
reinterpret_cast<uint8_t*>(&iv[0]), AES_ENCRYPT);
return true;
}
RsaPublicKey::~RsaPublicKey() {
if (key_ != NULL) {
RSA_free(key_);
}
}
bool RsaPublicKey::Init(const std::string& serialized_key) {
if (serialized_key.empty()) {
LOGE("RsaPublicKey::Init: no serialized key provided");
return false;
}
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
serialized_key.size());
if (bio == NULL) {
LOGE("RsaPublicKey::Init: BIO_new_mem_buf returned NULL");
return false;
}
key_ = d2i_RSAPublicKey_bio(bio, NULL);
BIO_free(bio);
if (key_ == NULL) {
LOGE("RsaPublicKey::Init: RSA key deserialization failure");
return false;
}
return true;
}
bool RsaPublicKey::Encrypt(const std::string& clear_message,
std::string* encrypted_message) {
if (clear_message.empty()) {
LOGE("RsaPublicKey::Encrypt: message to be encrypted is empty");
return false;
}
if (encrypted_message == NULL) {
LOGE("RsaPublicKey::Encrypt: no encrypt message buffer provided");
return false;
}
if (key_ == NULL) {
LOGE("RsaPublicKey::Encrypt: RSA key not initialized");
return false;
}
int rsa_size = RSA_size(key_);
if (static_cast<int>(clear_message.size()) >
rsa_size - kRsaPkcs1OaepPaddingLength) {
LOGE("RsaPublicKey::Encrypt: message too large to be encrypted (actual %d",
" max allowed %d)", clear_message.size(),
rsa_size - kRsaPkcs1OaepPaddingLength);
return false;
}
encrypted_message->assign(rsa_size, 0);
if (RSA_public_encrypt(
clear_message.size(),
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(clear_message.data())),
reinterpret_cast<unsigned char*>(&(*encrypted_message)[0]), key_,
RSA_PKCS1_OAEP_PADDING) != rsa_size) {
LOGE("RsaPublicKey::Encrypt: encrypt failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
bool RsaPublicKey::VerifySignature(const std::string& message,
const std::string& signature) {
if (key_ == NULL) {
LOGE("RsaPublicKey::VerifySignature: RSA key not initialized");
return false;
}
if (message.empty()) {
LOGE("RsaPublicKey::VerifySignature: signed message is empty");
return false;
}
int rsa_size = RSA_size(key_);
if (static_cast<int>(signature.size()) != rsa_size) {
LOGE(
"RsaPublicKey::VerifySignature: message signature is of the wrong "
"size (expected %d, actual %d)",
rsa_size, signature.size());
return false;
}
// Decrypt the signature.
std::string padded_digest(signature.size(), 0);
if (RSA_public_decrypt(
signature.size(),
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(signature.data())),
reinterpret_cast<unsigned char*>(&padded_digest[0]), key_,
RSA_NO_PADDING) != rsa_size) {
LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
}
// Hash the message using SHA1.
std::string message_digest(SHA_DIGEST_LENGTH, 0);
SHA1(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
reinterpret_cast<unsigned char*>(&message_digest[0]));
// Verify PSS padding.
if (RSA_verify_PKCS1_PSS(
key_, reinterpret_cast<const unsigned char*>(message_digest.data()),
EVP_sha1(),
reinterpret_cast<const unsigned char*>(padded_digest.data()),
kPssSaltLength) == 0) {
LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
} // namespace wvcdm

View File

@@ -1,11 +1,10 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "properties.h"
#include "log.h"
#include "properties_configuration.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
bool Properties::begin_license_usage_when_received_;
bool Properties::require_explicit_renew_request_;
bool Properties::oem_crypto_use_secure_buffers_;
@@ -13,6 +12,8 @@ bool Properties::oem_crypto_use_fifo_;
bool Properties::oem_crypto_use_userspace_buffers_;
bool Properties::use_certificates_as_identification_;
bool Properties::extract_pssh_data_;
bool Properties::decrypt_with_empty_session_support_;
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
void Properties::Init() {
begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived;
@@ -23,6 +24,73 @@ void Properties::Init() {
use_certificates_as_identification_ =
kPropertyUseCertificatesAsIdentification;
extract_pssh_data_ = kExtractPsshData;
decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport;
session_property_set_.reset(new CdmClientPropertySetMap());
}
bool Properties::AddSessionPropertySet(
const CdmSessionId& session_id,
const CdmClientPropertySet* property_set) {
if (NULL == session_property_set_.get()) {
return false;
}
std::pair<CdmClientPropertySetMap::iterator, bool> result =
session_property_set_->insert(
std::pair<const CdmSessionId,
const CdmClientPropertySet*>(session_id, property_set));
return result.second;
}
bool Properties::RemoveSessionPropertySet(const CdmSessionId& session_id) {
if (NULL == session_property_set_.get()) {
return false;
}
return (1 == session_property_set_->erase(session_id));
}
const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
const CdmSessionId& session_id) {
if (NULL != session_property_set_.get()) {
CdmClientPropertySetMap::const_iterator it =
session_property_set_->find(session_id);
if (it != session_property_set_->end()) {
return it->second;
}
}
return NULL;
}
const std::string Properties::GetSecurityLevel(const CdmSessionId& session_id) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
LOGE("Properties::GetSecurityLevel: cannot find property set for %s",
session_id.c_str());
return "";
}
return property_set->security_level();
}
const std::vector<uint8_t> Properties::GetServiceCertificate(
const CdmSessionId& session_id) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
LOGE("Properties::GetServiceCertificate: cannot find property set for %s",
session_id.c_str());
return std::vector<uint8_t>();
}
return property_set->service_certificate();
}
bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
LOGE("Properties::UsePrivacyMode: cannot find property set for %s",
session_id.c_str());
return false;
}
return property_set->use_privacy_mode();
}
} // namespace wvcdm

View File

@@ -88,7 +88,7 @@ class WvCdmEngineTest : public testing::Test {
public:
virtual void SetUp() {
cdm_engine_.reset(new CdmEngine());
cdm_engine_->OpenSession(g_key_system, &session_id_);
cdm_engine_->OpenSession(g_key_system, NULL, &session_id_);
}
virtual void TearDown() {

View File

@@ -78,9 +78,11 @@ TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;
CdmSessionId session_id;
license_.PrepareKeyRequest(a2bs_hex(kInitData),
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
@@ -90,10 +92,12 @@ TEST_F(LicenseTest, DISABLED_PrepareKeyRequest) {
TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
std::string signed_request;
CdmAppParameterMap app_parameters;
CdmSessionId session_id;
std::string server_url;
license_.PrepareKeyRequest(a2bs_hex(kInitData),
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
@@ -104,10 +108,12 @@ TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
std::string signed_request;
CdmAppParameterMap app_parameters;
CdmSessionId session_id;
std::string server_url;
license_.PrepareKeyRequest(a2bs_hex(kInitData),
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));

View File

@@ -41,7 +41,7 @@ void UrlRequest::AppendChunkToUpload(const std::string& data) {
// buffer to store length of chunk
memset(buffer_, 0, kHttpBufferSize);
snprintf(buffer_, kHttpBufferSize, "%x\r\n", data.size());
snprintf(buffer_, kHttpBufferSize, "%zx\r\n", data.size());
request_.append(buffer_); // appends size of chunk
LOGD("...\r\n%s", request_.c_str());
request_.append(data);
@@ -61,7 +61,7 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
// processes chunked encoding
size_t chunk_size = 0;
size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
sscanf(&http_response[chunk_size_pos], "%zx", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
@@ -85,7 +85,7 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
// Search for next chunk
chunk_size_pos = chunk_pos + chunk_size + kCrLf.size();
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
sscanf(&http_response[chunk_size_pos], "%zx", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);

View File

@@ -31,6 +31,10 @@ const bool kPropertyUseCertificatesAsIdentification = true;
// once all platforms support it (b/9465346)
const bool kExtractPsshData = true;
// If true, session_id parameter to CdmEngine::Decrypt can be empty; the
// function will try to find out the session_id from the key_id.
const bool kDecryptWithEmptySessionSupport = false;
} // namespace wvcdm
#endif // CDM_BASE_WV_PROPERTIES_CONFIGURATION_H_

View File

@@ -9,6 +9,7 @@
namespace wvcdm {
class CdmClientPropertySet;
class CdmEngine;
class WvCdmEventListener;
@@ -18,7 +19,9 @@ class WvContentDecryptionModule {
virtual ~WvContentDecryptionModule();
// Session related methods
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
virtual CdmResponseType OpenSession(
const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);

View File

@@ -12,9 +12,9 @@
namespace {
const char kBasePathPrefix[] = "/data/mediadrm/IDM";
const char kL1Dir[] = "/L1";
const char kL2Dir[] = "/L2";
const char kL3Dir[] = "/L3";
const char kL1Dir[] = "/L1/";
const char kL2Dir[] = "/L2/";
const char kL3Dir[] = "/L3/";
const char kFactoryKeyboxPath[] = "/factory/wv.keys";
bool GetAndroidProperty(const char* key, std::string* value) {
@@ -118,4 +118,13 @@ bool Properties::GetFactoryKeyboxPath(std::string* keybox) {
return true;
}
bool Properties::GetOEMCryptoPath(std::string* library_name) {
if (!library_name) {
LOGW("Properties::GetOEMCryptoPath: Invalid parameter");
return false;
}
*library_name = "liboemcrypto.so";
return true;
}
} // namespace wvcdm

View File

@@ -17,8 +17,10 @@ WvContentDecryptionModule::WvContentDecryptionModule()
WvContentDecryptionModule::~WvContentDecryptionModule() {}
CdmResponseType WvContentDecryptionModule::OpenSession(
const CdmKeySystem& key_system, CdmSessionId* session_id) {
return cdm_engine_->OpenSession(key_system, session_id);
const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
CdmSessionId* session_id) {
return cdm_engine_->OpenSession(key_system, property_set, session_id);
}
CdmResponseType WvContentDecryptionModule::CloseSession(

View File

@@ -6,9 +6,13 @@
#include "config_test_env.h"
#include "gtest/gtest.h"
#include "device_files.h"
#include "file_store.h"
#include "license_request.h"
#include "log.h"
#include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h"
#include "properties.h"
#include "string_conversions.h"
#include "url_request.h"
#include "wv_cdm_constants.h"
@@ -186,6 +190,38 @@ SubSampleInfo partial_offset_single_encrypted_sub_sample = {
} // namespace
namespace wvcdm {
class TestWvCdmClientPropertySet : public CdmClientPropertySet {
public:
TestWvCdmClientPropertySet()
: service_certificate_(std::vector<uint8_t>()),
use_privacy_mode_(false) {}
virtual ~TestWvCdmClientPropertySet() {}
virtual std::string security_level() const { return security_level_; }
virtual std::vector<uint8_t> service_certificate() const {
return service_certificate_;
}
virtual bool use_privacy_mode() const { return use_privacy_mode_; }
void set_security_level(const std::string& security_level) {
if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)) {
security_level_ = security_level;
}
}
void set_service_certificate(
const std::vector<uint8_t>& service_certificate) {
service_certificate_ = service_certificate;
}
void set_use_privacy_mode(bool use_privacy_mode) {
use_privacy_mode_ = use_privacy_mode;
}
private:
std::string security_level_;
std::vector<uint8_t> service_certificate_;
bool use_privacy_mode_;
};
class TestWvCdmEventListener : public WvCdmEventListener {
public:
@@ -337,7 +373,7 @@ class WvCdmDecryptionTest
public ::testing::WithParamInterface<SubSampleInfo*> {};
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
std::string provisioning_server_url;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
@@ -352,7 +388,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
}
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
std::string provisioning_server_url;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(
@@ -376,15 +412,108 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
TestWvCdmClientPropertySet property_set_L1;
TestWvCdmClientPropertySet property_set_L3;
TestWvCdmClientPropertySet property_set_Ln;
CdmSessionId session_id_L1;
CdmSessionId session_id_L3;
CdmSessionId session_id_Ln;
property_set_L1.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L1);
property_set_L1.set_use_privacy_mode(true);
decryptor_.OpenSession(g_key_system, &property_set_L1, &session_id_L1);
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
property_set_L3.set_use_privacy_mode(false);
decryptor_.OpenSession(g_key_system, &property_set_L3, &session_id_L3);
property_set_Ln.set_security_level("");
decryptor_.OpenSession(g_key_system, &property_set_Ln, &session_id_Ln);
std::string security_level = Properties::GetSecurityLevel(session_id_L1);
EXPECT_TRUE(!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3));
EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1));
EXPECT_EQ(Properties::GetSecurityLevel(session_id_L3),
QUERY_VALUE_SECURITY_LEVEL_L3);
EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3));
security_level = Properties::GetSecurityLevel(session_id_Ln);
EXPECT_TRUE(security_level.empty() ||
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3));
decryptor_.CloseSession(session_id_L1);
decryptor_.CloseSession(session_id_L3);
decryptor_.CloseSession(session_id_Ln);
}
TEST_F(WvCdmRequestLicenseTest, ForceL3Test) {
TestWvCdmClientPropertySet property_set;
property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
File file;
DeviceFiles handle;
EXPECT_TRUE(handle.Init(&file, kSecurityLevelL3));
EXPECT_TRUE(handle.DeleteAllFiles());
EXPECT_EQ(NEED_PROVISIONING,
decryptor_.OpenSession(g_key_system, &property_set, &session_id_));
std::string provisioning_server_url;
EXPECT_EQ(NO_ERROR,
decryptor_.GetProvisioningRequest(&key_msg_,
&provisioning_server_url));
EXPECT_EQ(provisioning_server_url, config_.provisioning_server_url());
std::string response =
GetCertRequestResponse(config_.provisioning_test_server_url(), 200);
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(NO_ERROR, decryptor_.HandleProvisioningResponse(response));
EXPECT_EQ(NO_ERROR, decryptor_.OpenSession(g_key_system, &property_set,
&session_id_));
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, UsePrivacyModeTest) {
TestWvCdmClientPropertySet property_set;
property_set.set_use_privacy_mode(true);
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
if (property_set.service_certificate().empty()) {
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
std::string resp = GetKeyRequestResponse(g_license_server,
g_client_auth, 200);
EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_),
wvcdm::NEED_KEY);
std::vector<uint8_t> service_certificate(key_msg_.begin(), key_msg_.end());
property_set.set_service_certificate(service_certificate);
}
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
// property has service certificate set from previous request
EXPECT_FALSE(property_set.service_certificate().empty());
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
property_set.set_use_privacy_mode(false);
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
GetKeyRequestResponse(g_license_server, g_client_auth, 200);
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message, kLicenseTypeStreaming);
@@ -393,21 +522,21 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
}
TEST_F(WvCdmRequestLicenseTest, AddSteamingKeyTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -416,13 +545,13 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
decryptor_.CloseSession(session_id_);
session_id_.clear();
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -432,7 +561,7 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
session_id_.clear();
key_set_id_.clear();
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
decryptor_.CloseSession(session_id_);
@@ -444,7 +573,7 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
}
TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -454,7 +583,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
session_id_.clear();
key_set_id_.clear();
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
CdmSessionId restore_session_id = session_id_;
TestWvCdmEventListener listener;
EXPECT_TRUE(decryptor_.AttachEventListener(restore_session_id, &listener));
@@ -474,7 +603,7 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
}
TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -484,7 +613,7 @@ TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
}
TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -494,7 +623,7 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
}
TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -564,7 +693,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) {
}
TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -585,7 +714,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) {
TEST_P(WvCdmDecryptionTest, DecryptionTest) {
SubSampleInfo* data = GetParam();
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
if (data->retrieve_key) {
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -616,7 +745,7 @@ INSTANTIATE_TEST_CASE_P(
&partial_single_encrypted_sub_sample));
TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
@@ -669,7 +798,7 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_OfflineLicenseDecryptionTest) {
}
TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) {
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
CdmKeySetId key_set_id = key_set_id_;
@@ -677,7 +806,7 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_RestoreOfflineLicenseDecryptionTest) {
decryptor_.CloseSession(session_id_);
session_id_.clear();
decryptor_.OpenSession(g_key_system, &session_id_);
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
/*
// key 1, encrypted, 256b

View File

@@ -38,7 +38,6 @@ LOCAL_STATIC_LIBRARIES := \
libgmock \
libgtest \
libgtest_main \
libwvwrapper \
libwvlevel3 \
libcdm_utils \
libprotobuf-cpp-2.3.0-lite

View File

@@ -17,7 +17,8 @@ enum {
kErrorCDMGeneric = ERROR_DRM_VENDOR_MIN + 1,
kErrorUnsupportedCrypto = ERROR_DRM_VENDOR_MIN + 2,
kErrorExpectedUnencrypted = ERROR_DRM_VENDOR_MIN + 3,
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 3,
kErrorSessionIsOpen = ERROR_DRM_VENDOR_MIN + 4,
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 4,
// Used by crypto test mode
kErrorTestMode = ERROR_DRM_VENDOR_MAX,

View File

@@ -23,7 +23,6 @@ LOCAL_STATIC_LIBRARIES := \
libgmock \
libgmock_main \
libgtest \
libwvwrapper \
libwvlevel3 \
libprotobuf-cpp-2.3.0-lite \
libwvdrmcryptoplugin \

View File

@@ -8,6 +8,7 @@
#include <stdint.h>
#include <map>
#include "cdm_client_property_set.h"
#include "media/drm/DrmAPI.h"
#include "media/stagefright/foundation/ABase.h"
#include "media/stagefright/foundation/AString.h"
@@ -157,6 +158,45 @@ class WVDrmPlugin : public android::DrmPlugin,
OEMCrypto_Algorithm mMacAlgorithm;
};
class WVClientPropertySet : public wvcdm::CdmClientPropertySet {
public:
WVClientPropertySet()
: mUsePrivacyMode(false) {}
virtual ~WVClientPropertySet() {}
void set_security_level(const std::string& securityLevel) {
mSecurityLevel = securityLevel;
}
virtual std::string security_level() const {
return mSecurityLevel;
}
void set_use_privacy_mode(bool usePrivacyMode) {
mUsePrivacyMode = usePrivacyMode;
}
virtual bool use_privacy_mode() const {
return mUsePrivacyMode;
}
void set_service_certificate(const std::vector<uint8_t>& serviceCertificate) {
mServiceCertificate = serviceCertificate;
}
virtual std::vector<uint8_t> service_certificate() const {
return mServiceCertificate;
}
private:
DISALLOW_EVIL_CONSTRUCTORS(WVClientPropertySet);
std::string mSecurityLevel;
bool mUsePrivacyMode;
std::vector<uint8_t> mServiceCertificate;
} mPropertySet;
WvContentDecryptionModule* mCDM;
WVGenericCryptoInterface* mCrypto;
map<CdmSessionId, CryptoSession> mCryptoSessions;

View File

@@ -25,6 +25,10 @@ using namespace android;
using namespace std;
using namespace wvcdm;
static const char* const kResetSecurityLevel = "";
static const char* const kEnable = "enable";
static const char* const kDisable = "disable";
WVDrmPlugin::WVDrmPlugin(WvContentDecryptionModule* cdm,
WVGenericCryptoInterface* crypto)
: mCDM(cdm), mCrypto(crypto) {}
@@ -51,10 +55,11 @@ WVDrmPlugin::~WVDrmPlugin() {
status_t WVDrmPlugin::openSession(Vector<uint8_t>& sessionId) {
CdmSessionId cdmSessionId;
CdmResponseType res = mCDM->OpenSession("com.widevine", &cdmSessionId);
CdmResponseType res = mCDM->OpenSession("com.widevine", &mPropertySet,
&cdmSessionId);
if (!isCdmResponseTypeSuccess(res)) {
return mapAndNotifyOfCdmResponseType(sessionId, res);
return mapCdmResponseType(res);
}
bool success = false;
@@ -347,6 +352,11 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
} else if (name == "algorithms") {
value = "AES/CBC/NoPadding,HmacSHA256";
} else if (name == "securityLevel") {
string requestedLevel = mPropertySet.security_level();
if (requestedLevel.length() > 0) {
value = requestedLevel.c_str();
} else {
CdmQueryMap status;
CdmResponseType res = mCDM->QueryStatus(&status);
if (!isCdmResponseTypeSuccess(res)) {
@@ -357,6 +367,7 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
return kErrorCDMGeneric;
}
value = status[QUERY_KEY_SECURITY_LEVEL].c_str();
}
} else if (name == "systemId") {
CdmQueryMap status;
CdmResponseType res = mCDM->QueryStatus(&status);
@@ -368,8 +379,14 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
return kErrorCDMGeneric;
}
value = status[QUERY_KEY_SYSTEM_ID].c_str();
} else if (name == "privacyMode") {
if (mPropertySet.use_privacy_mode()) {
value = kEnable;
} else {
ALOGE("App requested unknown property %s", name.string());
value = kDisable;
}
} else {
ALOGE("App requested unknown string property %s", name.string());
return android::ERROR_DRM_CANNOT_HANDLE;
}
@@ -414,8 +431,12 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name,
value.clear();
value.appendArray(reinterpret_cast<const uint8_t*>(uniqueId.data()),
uniqueId.size());
} else if (name == "serviceCertificate") {
vector<uint8_t> cert = mPropertySet.service_certificate();
value.clear();
value.appendArray(&cert[0], cert.size());
} else {
ALOGE("App requested unknown property %s", name.string());
ALOGE("App requested unknown byte array property %s", name.string());
return android::ERROR_DRM_CANNOT_HANDLE;
}
@@ -424,14 +445,50 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name,
status_t WVDrmPlugin::setPropertyString(const String8& name,
const String8& value) {
if (name == "securityLevel") {
if (mCryptoSessions.size() == 0) {
if (value == QUERY_VALUE_SECURITY_LEVEL_L3.c_str()) {
mPropertySet.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
} else if (value == kResetSecurityLevel) {
mPropertySet.set_security_level("");
} else {
ALOGE("App requested invalid security level %s", value.string());
return android::BAD_VALUE;
}
} else {
ALOGE("App tried to change security level while sessions are open.");
return kErrorSessionIsOpen;
}
} else if (name == "privacyMode") {
if (value == kEnable) {
mPropertySet.set_use_privacy_mode(true);
} else if (value == kDisable) {
mPropertySet.set_use_privacy_mode(false);
} else {
ALOGE("App requested unknown privacy mode %s", value.string());
return android::BAD_VALUE;
}
} else {
ALOGE("App set unknown string property %s", name.string());
return android::ERROR_DRM_CANNOT_HANDLE;
}
return android::OK;
}
status_t WVDrmPlugin::setPropertyByteArray(const String8& name,
const Vector<uint8_t>& value) {
if (name == "serviceCertificate") {
vector<uint8_t> cert(value.begin(), value.end());
mPropertySet.set_service_certificate(cert);
} else {
ALOGE("App set unknown byte array property %s", name.string());
return android::ERROR_DRM_CANNOT_HANDLE;
}
return android::OK;
}
status_t WVDrmPlugin::setCipherAlgorithm(const Vector<uint8_t>& sessionId,
const String8& algorithm) {
CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end());

View File

@@ -23,7 +23,6 @@ LOCAL_STATIC_LIBRARIES := \
libgmock \
libgmock_main \
libgtest \
libwvwrapper \
libwvlevel3 \
libprotobuf-cpp-2.3.0-lite \
libwvdrmdrmplugin \

View File

@@ -6,6 +6,7 @@
#include <string.h>
#include <string>
#include "cdm_client_property_set.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "media/stagefright/foundation/ABase.h"
@@ -23,7 +24,8 @@ using namespace wvdrm;
class MockCDM : public WvContentDecryptionModule {
public:
MOCK_METHOD2(OpenSession, CdmResponseType(const CdmKeySystem&,
MOCK_METHOD3(OpenSession, CdmResponseType(const CdmKeySystem&,
const CdmClientPropertySet*,
CdmSessionId*));
MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&));
@@ -133,8 +135,8 @@ TEST_F(WVDrmPluginTest, OpensSessions) {
StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(&cdm, &crypto);
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
.WillOnce(DoAll(SetArgPointee<1>(cdmSessionId),
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.WillOnce(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
// Provide expected behavior when plugin requests session control info
@@ -633,9 +635,9 @@ TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) {
bool match;
// Provide expected behavior to support session creation
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.Times(AtLeast(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
@@ -719,9 +721,9 @@ TEST_F(WVDrmPluginTest, CallsGenericEncrypt) {
}
// Provide expected behavior to support session creation
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.Times(AtLeast(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
@@ -786,9 +788,9 @@ TEST_F(WVDrmPluginTest, CallsGenericDecrypt) {
}
// Provide expected behavior to support session creation
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.Times(AtLeast(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
@@ -855,9 +857,9 @@ TEST_F(WVDrmPluginTest, CallsGenericSign) {
}
// Provide expected behavior to support session creation
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.Times(AtLeast(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
@@ -934,9 +936,9 @@ TEST_F(WVDrmPluginTest, CallsGenericVerify) {
}
// Provide expected behavior to support session creation
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.Times(AtLeast(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
@@ -977,9 +979,9 @@ TEST_F(WVDrmPluginTest, RegistersForEvents) {
.Times(1);
// Provide expected behavior to support session creation
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.Times(AtLeast(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
@@ -1014,10 +1016,10 @@ TEST_F(WVDrmPluginTest, UnregistersForAllEventsOnDestruction) {
CdmSessionId cdmSessionId1(sessionIdRaw1, sessionIdRaw1 + kSessionIdSize);
CdmSessionId cdmSessionId2(sessionIdRaw2, sessionIdRaw2 + kSessionIdSize);
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
.WillOnce(DoAll(SetArgPointee<1>(cdmSessionId1),
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.WillOnce(DoAll(SetArgPointee<2>(cdmSessionId1),
Return(wvcdm::NO_ERROR)))
.WillOnce(DoAll(SetArgPointee<1>(cdmSessionId2),
.WillOnce(DoAll(SetArgPointee<2>(cdmSessionId2),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId1, _))
@@ -1071,9 +1073,9 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) {
}
// Provide expected behavior to support session creation
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _))
EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _, _))
.Times(AtLeast(1))
.WillRepeatedly(DoAll(SetArgPointee<1>(cdmSessionId),
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
Return(wvcdm::NO_ERROR)));
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
@@ -1099,3 +1101,217 @@ TEST_F(WVDrmPluginTest, MarshalsEvents) {
plugin.onEvent(cdmSessionId, LICENSE_EXPIRED_EVENT);
plugin.onEvent(cdmSessionId, LICENSE_RENEWAL_NEEDED_EVENT);
}
TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) {
StrictMock<MockCDM> cdm;
StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(&cdm, &crypto);
const CdmClientPropertySet* propertySet = NULL;
// Provide expected mock behavior
{
// Provide expected behavior in response to OpenSession and store the
// property set
EXPECT_CALL(cdm, OpenSession(_, _, _))
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
SaveArg<1>(&propertySet),
Return(wvcdm::NO_ERROR)));
// Provide expected behavior when plugin requests session control info
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
// Let gMock know these calls will happen but we aren't interested in them.
EXPECT_CALL(cdm, AttachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
}
plugin.openSession(sessionId);
ASSERT_THAT(propertySet, NotNull());
EXPECT_STREQ("", propertySet->security_level().c_str());
EXPECT_FALSE(propertySet->use_privacy_mode());
EXPECT_EQ(0u, propertySet->service_certificate().size());
}
TEST_F(WVDrmPluginTest, CanSetSecurityLevel) {
StrictMock<MockCDM> cdm;
StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(&cdm, &crypto);
const CdmClientPropertySet* propertySet = NULL;
// Provide expected mock behavior
{
// Provide expected behavior in response to OpenSession and store the
// property set
EXPECT_CALL(cdm, OpenSession(_, _, _))
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
SaveArg<1>(&propertySet),
Return(wvcdm::NO_ERROR)));
// Provide expected behavior when plugin requests session control info
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
// Let gMock know these calls will happen but we aren't interested in them.
EXPECT_CALL(cdm, AttachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
}
status_t res;
// Test forcing L3
res = plugin.setPropertyString(String8("securityLevel"), String8("L3"));
ASSERT_EQ(OK, res);
plugin.openSession(sessionId);
ASSERT_THAT(propertySet, NotNull());
EXPECT_STREQ("L3", propertySet->security_level().c_str());
plugin.closeSession(sessionId);
// Test forcing L1 (Should Fail)
res = plugin.setPropertyString(String8("securityLevel"), String8("L1"));
ASSERT_NE(OK, res);
// Test un-forcing a level
res = plugin.setPropertyString(String8("securityLevel"), String8(""));
ASSERT_EQ(OK, res);
plugin.openSession(sessionId);
ASSERT_THAT(propertySet, NotNull());
EXPECT_STREQ("", propertySet->security_level().c_str());
plugin.closeSession(sessionId);
// Test nonsense (Should Fail)
res = plugin.setPropertyString(String8("securityLevel"), String8("nonsense"));
ASSERT_NE(OK, res);
// Test attempting to force a level with a session open (Should Fail)
plugin.openSession(sessionId);
res = plugin.setPropertyString(String8("securityLevel"), String8("L3"));
ASSERT_NE(OK, res);
}
TEST_F(WVDrmPluginTest, CanSetPrivacyMode) {
StrictMock<MockCDM> cdm;
StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(&cdm, &crypto);
const CdmClientPropertySet* propertySet = NULL;
// Provide expected mock behavior
{
// Provide expected behavior in response to OpenSession and store the
// property set
EXPECT_CALL(cdm, OpenSession(_, _, _))
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
SaveArg<1>(&propertySet),
Return(wvcdm::NO_ERROR)));
// Provide expected behavior when plugin requests session control info
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
// Let gMock know these calls will happen but we aren't interested in them.
EXPECT_CALL(cdm, AttachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
}
plugin.openSession(sessionId);
ASSERT_THAT(propertySet, NotNull());
status_t res;
// Test turning on privacy mode
res = plugin.setPropertyString(String8("privacyMode"), String8("enable"));
ASSERT_EQ(OK, res);
EXPECT_TRUE(propertySet->use_privacy_mode());
// Test turning off privacy mode
res = plugin.setPropertyString(String8("privacyMode"), String8("disable"));
ASSERT_EQ(OK, res);
EXPECT_FALSE(propertySet->use_privacy_mode());
// Test nonsense (Should Fail)
res = plugin.setPropertyString(String8("privacyMode"), String8("nonsense"));
ASSERT_NE(OK, res);
}
TEST_F(WVDrmPluginTest, CanSetServiceCertificate) {
StrictMock<MockCDM> cdm;
StrictMock<MockCrypto> crypto;
WVDrmPlugin plugin(&cdm, &crypto);
const CdmClientPropertySet* propertySet = NULL;
static const size_t kPrivacyCertSize = 256;
uint8_t privacyCertRaw[kPrivacyCertSize];
FILE* fp = fopen("/dev/urandom", "r");
fread(privacyCertRaw, sizeof(uint8_t), kPrivacyCertSize, fp);
fclose(fp);
Vector<uint8_t> privacyCert;
privacyCert.appendArray(privacyCertRaw, kPrivacyCertSize);
Vector<uint8_t> emptyVector;
// Provide expected mock behavior
{
// Provide expected behavior in response to OpenSession and store the
// property set
EXPECT_CALL(cdm, OpenSession(_, _, _))
.WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId),
SaveArg<1>(&propertySet),
Return(wvcdm::NO_ERROR)));
// Provide expected behavior when plugin requests session control info
EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _))
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
// Let gMock know these calls will happen but we aren't interested in them.
EXPECT_CALL(cdm, AttachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, DetachEventListener(_, _))
.Times(AtLeast(0));
EXPECT_CALL(cdm, CloseSession(_))
.Times(AtLeast(0));
}
plugin.openSession(sessionId);
ASSERT_THAT(propertySet, NotNull());
status_t res;
// Test setting a certificate
res = plugin.setPropertyByteArray(String8("serviceCertificate"), privacyCert);
ASSERT_EQ(OK, res);
EXPECT_THAT(propertySet->service_certificate(),
ElementsAreArray(privacyCertRaw, kPrivacyCertSize));
// Test clearing a certificate
res = plugin.setPropertyByteArray(String8("serviceCertificate"), emptyVector);
ASSERT_EQ(OK, res);
EXPECT_EQ(0u, propertySet->service_certificate().size());
}

View File

@@ -1,34 +0,0 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := \
vendor/widevine/libwvdrmengine/cdm/core/include \
bionic \
external/openssh \
external/openssl/include \
external/openssl/include/openssl \
external/stlport/stlport \
vendor/widevine/libwvdrmengine/oemcrypto/include \
LOCAL_SHARED_LIBRARIES := \
libcrypto \
libcutils \
libdl \
liblog \
libstlport \
libutils \
libz \
LOCAL_STATIC_LIBRARIES := \
libwvlevel3 \
LOCAL_MODULE := libwvwrapper
# TODO(fredgc): remove mock reference when library is complete.
REL_MOCK_SOURCE := ../oemcrypto/mock/src
LOCAL_SRC_FILES := \
src/wrapper.cpp \
include $(BUILD_STATIC_LIBRARY)

View File

@@ -130,6 +130,8 @@ OEMCryptoResult Level3_DeriveKeysFromSessionKey(OEMCrypto_SESSION session,
size_t mac_key_context_length,
const uint8_t *enc_key_context,
size_t enc_key_context_length);
uint32_t Level3_APIVersion();
const char* Level3_SecurityLevel();
OEMCryptoResult Level3_Generic_Encrypt(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,

View File

@@ -1,647 +0,0 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* mock implementation of OEMCrypto APIs
*
******************************************************************************/
#include "OEMCryptoCENC.h"
#include <dlfcn.h>
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <string>
#include "level3.h"
#include "log.h"
#include "file_store.h"
#include "properties.h"
using namespace wvoec_level3;
namespace {
typedef OEMCryptoResult (*L1_Initialize_t)(void);
typedef OEMCryptoResult (*L1_Terminate_t)(void);
typedef OEMCryptoResult (*L1_OpenSession_t)(OEMCrypto_SESSION *session);
typedef OEMCryptoResult (*L1_CloseSession_t)(OEMCrypto_SESSION session);
typedef OEMCryptoResult (*L1_GenerateDerivedKeys_t)(OEMCrypto_SESSION session,
const uint8_t *mac_key_context,
uint32_t mac_key_context_length,
const uint8_t *enc_key_context,
uint32_t enc_key_context_length);
typedef OEMCryptoResult (*L1_GenerateNonce_t)(OEMCrypto_SESSION session,
uint32_t* nonce);
typedef OEMCryptoResult (*L1_GenerateSignature_t)(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
typedef OEMCryptoResult (*L1_LoadKeys_t)(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);
typedef OEMCryptoResult (*L1_RefreshKeys_t)(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array);
typedef OEMCryptoResult (*L1_SelectKey_t)(const OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length);
typedef OEMCryptoResult (*L1_DecryptCTR_t)(OEMCrypto_SESSION session,
const uint8_t *data_addr,
size_t data_length,
bool is_encrypted,
const uint8_t *iv,
size_t offset,
const OEMCrypto_DestBufferDesc* out_buffer,
uint8_t subsample_flags);
typedef OEMCryptoResult (*L1_InstallKeybox_t)(const uint8_t *keybox,
size_t keyBoxLength);
typedef OEMCryptoResult (*L1_IsKeyboxValid_t)(void);
typedef OEMCryptoResult (*L1_GetDeviceID_t)(uint8_t* deviceID,
size_t *idLength);
typedef OEMCryptoResult (*L1_GetKeyData_t)(uint8_t* keyData,
size_t *keyDataLength);
typedef OEMCryptoResult (*L1_GetRandom_t)(uint8_t* randomData,
size_t dataLength);
typedef OEMCryptoResult (*L1_WrapKeybox_t)(const uint8_t *keybox,
size_t keyBoxLength,
uint8_t *wrappedKeybox,
size_t *wrappedKeyBoxLength,
const uint8_t *transportKey,
size_t transportKeyLength);
typedef OEMCryptoResult (*L1_RewrapDeviceRSAKey_t)(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
const uint32_t *nonce,
const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key,
size_t *wrapped_rsa_key_length);
typedef OEMCryptoResult (*L1_LoadDeviceRSAKey_t)(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length);
typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t *signature_length);
typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)(OEMCrypto_SESSION session,
const uint8_t* enc_session_key,
size_t enc_session_key_length,
const uint8_t *mac_key_context,
size_t mac_key_context_length,
const uint8_t *enc_key_context,
size_t enc_key_context_length);
typedef OEMCryptoResult (*L1_Generic_Encrypt_t)(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
typedef OEMCryptoResult (*L1_Generic_Decrypt_t)(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer);
typedef OEMCryptoResult (*L1_Generic_Sign_t)(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length);
typedef OEMCryptoResult (*L1_Generic_Verify_t)(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length);
typedef uint8_t (*L1_APIVersion_t)();
typedef const char* (*L1_SecurityLevel_t)();
struct FunctionPointers {
void* library;
L1_Initialize_t OEMCrypto_Initialize;
L1_Terminate_t OEMCrypto_Terminate;
L1_OpenSession_t OEMCrypto_OpenSession;
L1_CloseSession_t OEMCrypto_CloseSession;
L1_GenerateDerivedKeys_t OEMCrypto_GenerateDerivedKeys;
L1_GenerateNonce_t OEMCrypto_GenerateNonce;
L1_GenerateSignature_t OEMCrypto_GenerateSignature;
L1_LoadKeys_t OEMCrypto_LoadKeys;
L1_RefreshKeys_t OEMCrypto_RefreshKeys;
L1_SelectKey_t OEMCrypto_SelectKey;
L1_DecryptCTR_t OEMCrypto_DecryptCTR;
L1_InstallKeybox_t OEMCrypto_InstallKeybox;
L1_IsKeyboxValid_t OEMCrypto_IsKeyboxValid;
L1_GetDeviceID_t OEMCrypto_GetDeviceID;
L1_GetKeyData_t OEMCrypto_GetKeyData;
L1_GetRandom_t OEMCrypto_GetRandom;
L1_WrapKeybox_t OEMCrypto_WrapKeybox;
L1_RewrapDeviceRSAKey_t OEMCrypto_RewrapDeviceRSAKey;
L1_LoadDeviceRSAKey_t OEMCrypto_LoadDeviceRSAKey;
L1_GenerateRSASignature_t OEMCrypto_GenerateRSASignature;
L1_DeriveKeysFromSessionKey_t OEMCrypto_DeriveKeysFromSessionKey;
L1_APIVersion_t OEMCrypto_APIVersion;
L1_SecurityLevel_t OEMCrypto_SecurityLevel;
L1_Generic_Encrypt_t OEMCrypto_Generic_Encrypt;
L1_Generic_Decrypt_t OEMCrypto_Generic_Decrypt;
L1_Generic_Sign_t OEMCrypto_Generic_Sign;
L1_Generic_Verify_t OEMCrypto_Generic_Verify;
};
static struct FunctionPointers level1;
#define QUOTE_DEFINE(A) #A
#define QUOTE(A) QUOTE_DEFINE(A)
#define LOOKUP(Type, Name) \
level1.Name = (Type)dlsym(level1.library, QUOTE(Name)); \
if (!level1.Name) { \
dll_valid = false; \
}
extern "C"
OEMCryptoResult OEMCrypto_Initialize(void) {
// LOGD("First, I will try to load Level 1");
level1.library = dlopen("liboemcrypto.so", RTLD_NOW);
if (level1.library == NULL) {
LOGW("Could not load liboemcrypto.so. Falling Back to L3. %s", dlerror());
return Level3_Initialize();
}
bool dll_valid = true;
LOOKUP(L1_Initialize_t, OEMCrypto_Initialize);
LOOKUP(L1_Terminate_t, OEMCrypto_Terminate);
LOOKUP(L1_OpenSession_t, OEMCrypto_OpenSession);
LOOKUP(L1_CloseSession_t, OEMCrypto_CloseSession);
LOOKUP(L1_GenerateDerivedKeys_t, OEMCrypto_GenerateDerivedKeys);
LOOKUP(L1_GenerateNonce_t, OEMCrypto_GenerateNonce);
LOOKUP(L1_GenerateSignature_t, OEMCrypto_GenerateSignature);
LOOKUP(L1_LoadKeys_t, OEMCrypto_LoadKeys);
LOOKUP(L1_RefreshKeys_t, OEMCrypto_RefreshKeys);
LOOKUP(L1_SelectKey_t, OEMCrypto_SelectKey);
LOOKUP(L1_DecryptCTR_t, OEMCrypto_DecryptCTR);
LOOKUP(L1_InstallKeybox_t, OEMCrypto_InstallKeybox);
LOOKUP(L1_IsKeyboxValid_t, OEMCrypto_IsKeyboxValid);
LOOKUP(L1_GetDeviceID_t, OEMCrypto_GetDeviceID);
LOOKUP(L1_GetKeyData_t, OEMCrypto_GetKeyData);
LOOKUP(L1_GetRandom_t, OEMCrypto_GetRandom);
LOOKUP(L1_WrapKeybox_t, OEMCrypto_WrapKeybox);
LOOKUP(L1_RewrapDeviceRSAKey_t, OEMCrypto_RewrapDeviceRSAKey);
LOOKUP(L1_LoadDeviceRSAKey_t, OEMCrypto_LoadDeviceRSAKey);
LOOKUP(L1_GenerateRSASignature_t, OEMCrypto_GenerateRSASignature);
LOOKUP(L1_DeriveKeysFromSessionKey_t, OEMCrypto_DeriveKeysFromSessionKey);
LOOKUP(L1_APIVersion_t, OEMCrypto_APIVersion);
LOOKUP(L1_SecurityLevel_t, OEMCrypto_SecurityLevel);
LOOKUP(L1_Generic_Decrypt_t, OEMCrypto_Generic_Decrypt);
LOOKUP(L1_Generic_Encrypt_t, OEMCrypto_Generic_Encrypt);
LOOKUP(L1_Generic_Sign_t, OEMCrypto_Generic_Sign);
LOOKUP(L1_Generic_Verify_t, OEMCrypto_Generic_Verify);
if (!dll_valid) {
dlclose(level1.library);
level1.library = NULL;
LOGW("Could not load functions from liboemcrypto.so. Falling Back to L3.");
return Level3_Initialize();
}
OEMCryptoResult st = level1.OEMCrypto_Initialize();
if (st != OEMCrypto_SUCCESS) {
LOGW("Could not initialize liboemcrypto.so. Falling Back to L3.");
dlclose(level1.library);
level1.library = NULL;
return Level3_Initialize();
}
if (level1.OEMCrypto_APIVersion) {
uint32_t level1_version = level1.OEMCrypto_APIVersion();
if (level1_version != oec_latest_version) {
LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.",
level1_version, oec_latest_version);
dlclose(level1.library);
level1.library = NULL;
return Level3_Initialize();
}
}
if (OEMCrypto_SUCCESS != OEMCrypto_IsKeyboxValid()) {
wvcdm::File file;
std::string filename;
if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) {
LOGW("Bad Level 1 Keybox. Falling Back to L3.");
dlclose(level1.library);
level1.library = NULL;
return Level3_Initialize();
}
ssize_t size = file.FileSize(filename);
if( size <= 0 || !file.Open(filename, file.kBinary | file.kReadOnly) ) {
LOGW("Could not open %s. Falling Back to L3.", filename.c_str());
dlclose(level1.library);
level1.library = NULL;
return Level3_Initialize();
}
uint8_t keybox[size];
ssize_t size_read = file.Read(reinterpret_cast<char *>(keybox), size);
if (level1.OEMCrypto_InstallKeybox(keybox, size) != OEMCrypto_SUCCESS) {
LOGE("Could NOT install keybox in /factory/wv.keys. Falling Back to L3.");
dlclose(level1.library);
level1.library = NULL;
return Level3_Initialize();
}
LOGI("Installed keybox from %s", filename.c_str());
}
LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1.");
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_Terminate(void) {
if (level1.library) {
OEMCryptoResult st = level1.OEMCrypto_Terminate();
dlclose(level1.library);
level1.library = NULL;
return st;
}
return Level3_Terminate();
}
extern "C"
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
if (level1.library) {
return level1.OEMCrypto_OpenSession(session);
}
return Level3_OpenSession(session);
}
extern "C"
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
if (level1.library) {
return level1.OEMCrypto_CloseSession(session);
}
return Level3_CloseSession(session);
}
extern "C"
OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
uint32_t* nonce) {
if (level1.library) {
return level1.OEMCrypto_GenerateNonce(session, nonce);
}
return Level3_GenerateNonce(session, nonce);
}
extern "C"
OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
const uint8_t* mac_key_context,
uint32_t mac_key_context_length,
const uint8_t* enc_key_context,
uint32_t enc_key_context_length) {
if (level1.library) {
return level1.OEMCrypto_GenerateDerivedKeys(session, mac_key_context,
mac_key_context_length,
enc_key_context,
enc_key_context_length);
}
return Level3_GenerateDerivedKeys(session, mac_key_context,
mac_key_context_length,
enc_key_context,
enc_key_context_length);
}
extern "C"
OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) {
if (level1.library) {
return level1.OEMCrypto_GenerateSignature(session, message, message_length,
signature, signature_length);
}
return Level3_GenerateSignature(session, message, message_length,
signature, signature_length);
}
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) {
if (level1.library) {
return level1.OEMCrypto_LoadKeys(session, message, message_length, signature,
signature_length, enc_mac_key_iv, enc_mac_key,
num_keys, key_array);
}
return Level3_LoadKeys(session, message, message_length, signature,
signature_length, enc_mac_key_iv, enc_mac_key,
num_keys, key_array);
}
extern "C"
OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array) {
if (level1.library) {
return level1.OEMCrypto_RefreshKeys(session, message, message_length, signature,
signature_length, num_keys, key_array);
}
return Level3_RefreshKeys(session, message, message_length, signature,
signature_length, num_keys, key_array);
}
extern "C"
OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length) {
if (level1.library) {
return level1.OEMCrypto_SelectKey(session, key_id, key_id_length);
}
return Level3_SelectKey(session, key_id, key_id_length);
}
extern "C"
OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
const uint8_t* data_addr,
size_t data_length,
bool is_encrypted,
const uint8_t* iv,
size_t offset,
const OEMCrypto_DestBufferDesc* out_buffer,
uint8_t subsample_flags) {
if (level1.library) {
return level1.OEMCrypto_DecryptCTR(session, data_addr, data_length,
is_encrypted, iv, offset, out_buffer,
subsample_flags);
}
return Level3_DecryptCTR(session, data_addr, data_length,
is_encrypted, iv, offset, out_buffer, subsample_flags);
}
extern "C"
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength) {
if (level1.library) {
return level1.OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
return Level3_InstallKeybox(keybox, keyBoxLength);
}
extern "C"
OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {
if (level1.library) {
return level1.OEMCrypto_IsKeyboxValid();
}
return Level3_IsKeyboxValid();
}
extern "C"
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
size_t* idLength) {
if (level1.library) {
return level1.OEMCrypto_GetDeviceID(deviceID, idLength);
}
return Level3_GetDeviceID(deviceID, idLength);
}
extern "C"
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
size_t* keyDataLength) {
if (level1.library) {
return level1.OEMCrypto_GetKeyData(keyData, keyDataLength);
}
return Level3_GetKeyData(keyData, keyDataLength);
}
extern "C"
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {
if (level1.library) {
return level1.OEMCrypto_GetRandom(randomData, dataLength);
}
return Level3_GetRandom(randomData, dataLength);
}
extern "C"
OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox,
size_t keyBoxLength,
uint8_t* wrappedKeybox,
size_t* wrappedKeyBoxLength,
const uint8_t* transportKey,
size_t transportKeyLength) {
if (level1.library) {
return level1.OEMCrypto_WrapKeybox(keybox, keyBoxLength, wrappedKeybox,
wrappedKeyBoxLength, transportKey,
transportKeyLength);
}
return Level3_WrapKeybox(keybox, keyBoxLength, wrappedKeybox,
wrappedKeyBoxLength, transportKey,
transportKeyLength);
}
extern "C"
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
const uint32_t* nonce,
const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
if (level1.library) {
if (!level1.OEMCrypto_RewrapDeviceRSAKey) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_RewrapDeviceRSAKey(session, message, message_length,
signature, signature_length, nonce,
enc_rsa_key, enc_rsa_key_length,
enc_rsa_key_iv, wrapped_rsa_key,
wrapped_rsa_key_length);
}
return Level3_RewrapDeviceRSAKey(session, message, message_length,
signature, signature_length, nonce,
enc_rsa_key, enc_rsa_key_length,
enc_rsa_key_iv, wrapped_rsa_key,
wrapped_rsa_key_length);
}
extern "C"
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length) {
if (level1.library) {
if (!level1.OEMCrypto_LoadDeviceRSAKey) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_LoadDeviceRSAKey(session, wrapped_rsa_key,
wrapped_rsa_key_length);
}
return Level3_LoadDeviceRSAKey(session, wrapped_rsa_key,
wrapped_rsa_key_length);
}
extern "C"
OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) {
if (level1.library) {
if (!level1.OEMCrypto_GenerateRSASignature) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_GenerateRSASignature(session, message, message_length,
signature, signature_length);
}
return Level3_GenerateRSASignature(session, message, message_length,
signature, signature_length);
}
extern "C"
OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session,
const uint8_t* enc_session_key,
size_t enc_session_key_length,
const uint8_t* mac_key_context,
size_t mac_key_context_length,
const uint8_t* enc_key_context,
size_t enc_key_context_length) {
if (level1.library) {
if (!level1.OEMCrypto_DeriveKeysFromSessionKey) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_DeriveKeysFromSessionKey(session, enc_session_key,
enc_session_key_length,
mac_key_context,
mac_key_context_length,
enc_key_context,
enc_key_context_length);
}
return Level3_DeriveKeysFromSessionKey(session, enc_session_key,
enc_session_key_length,
mac_key_context,
mac_key_context_length,
enc_key_context,
enc_key_context_length);
}
extern "C"
uint32_t OEMCrypto_APIVersion() {
if (level1.library) {
if (!level1.OEMCrypto_APIVersion) {
return 5;
}
return level1.OEMCrypto_APIVersion();
}
return oec_latest_version;
}
extern "C"
const char* OEMCrypto_SecurityLevel() {
if (level1.library) {
if (!level1.OEMCrypto_SecurityLevel) {
return "Unknown";
}
return level1.OEMCrypto_SecurityLevel();
}
return "L3";
}
extern "C"
OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
if (level1.library) {
if (!level1.OEMCrypto_Generic_Encrypt) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_Generic_Encrypt(session, in_buffer, buffer_length,
iv, algorithm, out_buffer);
}
return Level3_Generic_Encrypt(session, in_buffer, buffer_length,
iv, algorithm, out_buffer);
}
extern "C"
OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
uint8_t* out_buffer) {
if (level1.library) {
if (!level1.OEMCrypto_Generic_Decrypt) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_Generic_Decrypt(session, in_buffer, buffer_length,
iv, algorithm, out_buffer);
}
return Level3_Generic_Decrypt(session, in_buffer, buffer_length,
iv, algorithm, out_buffer);
}
extern "C"
OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length) {
if (level1.library) {
if (!level1.OEMCrypto_Generic_Sign) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_Generic_Sign(session, in_buffer, buffer_length,
algorithm, signature,
signature_length);
}
return Level3_Generic_Sign(session, in_buffer, buffer_length,
algorithm, signature,
signature_length);
}
extern "C"
OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length) {
if (level1.library) {
if (!level1.OEMCrypto_Generic_Verify) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return level1.OEMCrypto_Generic_Verify(session, in_buffer, buffer_length,
algorithm, signature,
signature_length);
}
return Level3_Generic_Verify(session, in_buffer, buffer_length,
algorithm, signature,
signature_length);
}
}; // namespace wvoec_mock

View File

@@ -22,9 +22,9 @@ LOCAL_C_INCLUDES += \
vendor/widevine/libwvdrmengine/third_party/stringencoders/src \
LOCAL_STATIC_LIBRARIES := \
libcdm \
libgtest \
libgtest_main \
libwvwrapper \
libwvlevel3 \
libcdm_utils \