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

@@ -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();
cenc_content_id->add_pssh(init_data);
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:
@@ -187,7 +313,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
break;
default:
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
license_type);
license_type);
return false;
break;
}
@@ -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,14 +747,14 @@ 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:
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d",
license_error.error_code());
license_error.error_code());
return KEY_ERROR;
}
}

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