Migration from jb-mr2 to master for Widevine CDM
Android development of the widevine CDM has been done on the jb-mr2 branch of the cdm code base. This CL contains a merge of that jb-mr2 work to CDM master, and also reflects the evolution of the common Modular DRM code base since jb-mr2 branched. Change-Id: I1d7e1a12d092c00044a4298261146cb97808d4ef
This commit is contained in:
@@ -7,42 +7,22 @@
|
||||
|
||||
#include "buffer_reader.h"
|
||||
#include "cdm_session.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "device_files.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
#ifndef CDM_POLICY_TIMER_DURATION_SECONDS
|
||||
#define CDM_POLICY_TIMER_DURATION_SECONDS 1
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
const std::string kDefaultProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
const int kCdmPolicyTimerDurationSeconds = 1;
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::ProvisioningRequest;
|
||||
using video_widevine_server::sdk::ProvisioningResponse;
|
||||
using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
|
||||
typedef std::map<CdmSessionId, CdmSession*>::const_iterator CdmSessionIter;
|
||||
typedef std::map<CdmKeySetId, CdmSessionId>::iterator CdmReleaseKeySetIter;
|
||||
|
||||
CdmEngine::CdmEngine() : provisioning_session_(NULL) {
|
||||
CdmEngine::CdmEngine() {
|
||||
Properties::Init();
|
||||
Clock clock;
|
||||
srand(static_cast<int>(clock.GetCurrentTime() & 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
@@ -69,28 +49,21 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmSession* new_session = new CdmSession();
|
||||
if (!new_session) {
|
||||
LOGE("CdmEngine::OpenSession: session creation failed");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession());
|
||||
|
||||
CdmSessionId new_session_id = new_session->session_id();
|
||||
|
||||
if (new_session_id.empty()) {
|
||||
if (new_session->session_id().empty()) {
|
||||
LOGE("CdmEngine::OpenSession: failure to generate session ID");
|
||||
delete(new_session);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = new_session->Init();
|
||||
if (sts != NO_ERROR) {
|
||||
delete(new_session);
|
||||
LOGE("CdmEngine::OpenSession: bad session init");
|
||||
return sts;
|
||||
}
|
||||
|
||||
sessions_[new_session_id] = new_session;
|
||||
*session_id = new_session_id;
|
||||
*session_id = new_session->session_id();
|
||||
sessions_[*session_id] = new_session.release();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -98,7 +71,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::OpenKeySetSession");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::OpenKeySetSession: invalid key set id");
|
||||
LOGE("CdmEngine::OpenKeySetSession: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -115,24 +88,22 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CloseSession");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
sessions_.erase(session_id);
|
||||
if (sessions_.size() == 0)
|
||||
DisablePolicyTimer();
|
||||
iter->second->DestroySession();
|
||||
delete iter->second;
|
||||
sessions_.erase(session_id);
|
||||
DisablePolicyTimer();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::CloseKeySetSession");
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
@@ -169,7 +140,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
key_set_id.c_str());
|
||||
@@ -179,7 +150,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
id = iter->second;
|
||||
}
|
||||
|
||||
CdmSessionIter iter = sessions_.find(id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
|
||||
id.c_str());
|
||||
@@ -222,28 +193,33 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
CdmResponseType CdmEngine::AddKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId& key_set_id) {
|
||||
CdmKeySetId* key_set_id) {
|
||||
LOGI("CdmEngine::AddKey");
|
||||
|
||||
CdmSessionId id = session_id;
|
||||
bool license_type_release = session_id.empty();
|
||||
|
||||
if (license_type_release) {
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::AddKey: invalid key set id");
|
||||
if (!key_set_id) {
|
||||
LOGE("CdmEngine::AddKey: no key set id provided");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetIter iter = release_key_sets_.find(key_set_id);
|
||||
if (key_set_id->empty()) {
|
||||
LOGE("CdmEngine::AddKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id.c_str());
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
}
|
||||
|
||||
CdmSessionIter iter = sessions_.find(id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
|
||||
@@ -255,7 +231,7 @@ CdmResponseType CdmEngine::AddKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->AddKey(key_data, &key_set_id);
|
||||
CdmResponseType sts = iter->second->AddKey(key_data, key_set_id);
|
||||
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
|
||||
@@ -279,7 +255,7 @@ CdmResponseType CdmEngine::RestoreKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
@@ -298,13 +274,15 @@ CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
|
||||
// active sessions. Sessions are currently not being destroyed here. We can
|
||||
// add this logic once the semantics of canceling the key is worked out.
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
// TODO(edwinwong, rfrias): unload keys here
|
||||
delete iter->second;
|
||||
sessions_.erase(session_id);
|
||||
DisablePolicyTimer();
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -315,7 +293,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
std::string* server_url) {
|
||||
LOGI("CdmEngine::GenerateRenewalRequest");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
@@ -345,7 +323,7 @@ CdmResponseType CdmEngine::RenewKey(
|
||||
const CdmKeyResponse& key_data) {
|
||||
LOGI("CdmEngine::RenewKey");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
@@ -367,22 +345,19 @@ CdmResponseType CdmEngine::RenewKey(
|
||||
|
||||
CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryStatus");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (!crypto_engine) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
switch (crypto_engine->GetSecurityLevel()) {
|
||||
case CryptoEngine::kSecurityLevelL1:
|
||||
CryptoSession crypto_session;
|
||||
switch (crypto_session.GetSecurityLevel()) {
|
||||
case CryptoSession::kSecurityLevelL1:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
break;
|
||||
case CryptoEngine::kSecurityLevelL2:
|
||||
case CryptoSession::kSecurityLevelL2:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2;
|
||||
break;
|
||||
case CryptoEngine::kSecurityLevelL3:
|
||||
case CryptoSession::kSecurityLevelL3:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
|
||||
break;
|
||||
case CryptoEngine::kSecurityLevelUnknown:
|
||||
case CryptoSession::kSecurityLevelUninitialized:
|
||||
case CryptoSession::kSecurityLevelUnknown:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown;
|
||||
break;
|
||||
default:
|
||||
@@ -390,13 +365,13 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
}
|
||||
|
||||
std::string deviceId;
|
||||
bool success = crypto_engine->GetDeviceUniqueId(&deviceId);
|
||||
bool success = crypto_session.GetDeviceUniqueId(&deviceId);
|
||||
if (success) {
|
||||
(*key_info)[QUERY_KEY_DEVICE_ID] = deviceId;
|
||||
}
|
||||
|
||||
uint32_t system_id;
|
||||
success = crypto_engine->GetSystemId(&system_id);
|
||||
success = crypto_session.GetSystemId(&system_id);
|
||||
if (success) {
|
||||
std::ostringstream system_id_stream;
|
||||
system_id_stream << system_id;
|
||||
@@ -404,7 +379,7 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
}
|
||||
|
||||
std::string provisioning_id;
|
||||
success = crypto_engine->GetProvisioningId(&provisioning_id);
|
||||
success = crypto_session.GetProvisioningId(&provisioning_id);
|
||||
if (success) {
|
||||
(*key_info)[QUERY_KEY_PROVISIONING_ID] = provisioning_id;
|
||||
}
|
||||
@@ -416,7 +391,7 @@ CdmResponseType CdmEngine::QueryKeyStatus(
|
||||
const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyStatus");
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
@@ -428,7 +403,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
|
||||
const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyControlInfo");
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
@@ -436,63 +411,6 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
|
||||
return iter->second->QueryKeyControlInfo(key_info);
|
||||
}
|
||||
|
||||
/*
|
||||
* The certificate provisioning process creates a cdm and a crypto session.
|
||||
* The lives of these sessions are short and therefore, not added to the
|
||||
* CdmSessionMap. We need to explicitly delete these objects when error occurs
|
||||
* or when we are done with provisioning.
|
||||
*/
|
||||
void CdmEngine::CleanupProvisioningSession() {
|
||||
if (provisioning_session_) {
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (crypto_engine) {
|
||||
CdmSessionId cdm_session_id = provisioning_session_->session_id();
|
||||
CryptoSession* crypto_session =
|
||||
crypto_engine->FindSession(cdm_session_id);
|
||||
if (crypto_session) {
|
||||
LOGV("delete crypto session for id=%s", cdm_session_id.c_str());
|
||||
delete crypto_session;
|
||||
} else {
|
||||
LOGE("CleanupProvisioningSession: cannot find crypto_session");
|
||||
}
|
||||
}
|
||||
delete provisioning_session_;
|
||||
provisioning_session_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function converts SignedProvisioningRequest into base64 string.
|
||||
* It then wraps it in JSON format expected by the Apiary frontend.
|
||||
* Apiary requires the base64 encoding to replace '+' with minus '-',
|
||||
* and '/' with underscore '_'; opposite to stubby's.
|
||||
*
|
||||
* Returns the JSON formated string in *request. The JSON string will be
|
||||
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
|
||||
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
|
||||
*
|
||||
* The JSON formated request takes the following format:
|
||||
*
|
||||
* base64 encoded message
|
||||
*/
|
||||
void CdmEngine::ComposeJsonRequestAsQueryString(
|
||||
const std::string& message,
|
||||
CdmProvisioningRequest* request) {
|
||||
|
||||
// performs base64 encoding for message
|
||||
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
||||
std::string message_b64 = Base64SafeEncode(message_vector);
|
||||
|
||||
// removes trailing '=' padding characters;
|
||||
// the encoded string can have at most 2 '=' padding chars, so start
|
||||
// searching at the end minus four characters
|
||||
size_t found_pos = message_b64.find("=", message_b64.size() - 4);
|
||||
if (std::string::npos != found_pos) {
|
||||
message_b64.resize(found_pos);
|
||||
}
|
||||
request->assign(message_b64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Composes a device provisioning request and output the request in JSON format
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
@@ -504,143 +422,10 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!request || !default_url) {
|
||||
LOGE("GetProvisioningRequest: invalid input parameters");
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
default_url->assign(kDefaultProvisioningServerUrl);
|
||||
|
||||
if (provisioning_session_) {
|
||||
CleanupProvisioningSession();
|
||||
}
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// This function can be called before a cdm session is created.
|
||||
// First creates a cdm session, then creates a crypto session.
|
||||
//
|
||||
CdmSession* cdm_session = new CdmSession();
|
||||
if (!cdm_session) {
|
||||
LOGE("GetProvisioningRequest: fails to create a cdm session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (cdm_session->session_id().empty()) {
|
||||
LOGE("GetProvisioningRequest: fails to generate session ID");
|
||||
delete cdm_session;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (!crypto_engine) {
|
||||
LOGE("GetProvisioningRequest: fails to create a crypto engine");
|
||||
delete cdm_session;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionId cdm_session_id = cdm_session->session_id();
|
||||
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
|
||||
if (!crypto_session) {
|
||||
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
||||
delete cdm_session;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// TODO(edwinwong): replace this cdm session pointer with crypto session
|
||||
// pointer if feasible
|
||||
provisioning_session_ = cdm_session;
|
||||
LOGV("provisioning session id=%s", cdm_session_id.c_str());
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// Prepares device provisioning request.
|
||||
//
|
||||
ProvisioningRequest provisioning_request;
|
||||
ClientIdentification* client_id = provisioning_request.mutable_client_id();
|
||||
client_id->set_type(ClientIdentification::KEYBOX);
|
||||
std::string token;
|
||||
if (!crypto_engine->GetToken(&token)) {
|
||||
LOGE("GetProvisioningRequest: fails to get token");
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
client_id->set_token(token);
|
||||
|
||||
uint32_t nonce;
|
||||
if (!crypto_session->GenerateNonce(&nonce)) {
|
||||
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// The provisioning server does not convert the nonce to uint32_t, it just
|
||||
// passes the binary data to the response message.
|
||||
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
|
||||
provisioning_request.set_nonce(the_nonce);
|
||||
|
||||
std::string serialized_message;
|
||||
provisioning_request.SerializeToString(&serialized_message);
|
||||
|
||||
// Derives signing and encryption keys and constructs signature.
|
||||
std::string request_signature;
|
||||
if (!crypto_session->PrepareRequest(serialized_message,
|
||||
&request_signature, true)) {
|
||||
request->clear();
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (request_signature.empty()) {
|
||||
request->clear();
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
SignedProvisioningMessage signed_provisioning_msg;
|
||||
signed_provisioning_msg.set_message(serialized_message);
|
||||
signed_provisioning_msg.set_signature(request_signature);
|
||||
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
|
||||
// converts request into JSON string
|
||||
ComposeJsonRequestAsQueryString(serialized_request, request);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses the input json_str and locates substring using start_substr and
|
||||
* end_stubstr. The found base64 substring is then decoded and returns
|
||||
* in *result.
|
||||
*
|
||||
* Returns true for success and false if fails.
|
||||
*/
|
||||
bool CdmEngine::ParseJsonResponse(
|
||||
const CdmProvisioningResponse& json_str,
|
||||
const std::string& start_substr,
|
||||
const std::string& end_substr,
|
||||
std::string* result) {
|
||||
std::string b64_string;
|
||||
size_t start = json_str.find(start_substr);
|
||||
if (start == json_str.npos) {
|
||||
LOGE("ParseJsonResponse: cannot find start substring");
|
||||
return false;
|
||||
} else {
|
||||
size_t end = json_str.find(end_substr, start + start_substr.length());
|
||||
if (end == json_str.npos) {
|
||||
LOGE("ParseJsonResponse cannot locate end substring");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t b64_string_size = end - start - start_substr.length();
|
||||
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
|
||||
}
|
||||
|
||||
// Decodes base64 substring and returns it in *result
|
||||
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
|
||||
result->assign(result_vector.begin(), result_vector.end());
|
||||
|
||||
return true;
|
||||
return cert_provisioning_.GetProvisioningRequest(request, default_url);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -653,103 +438,10 @@ bool CdmEngine::ParseJsonResponse(
|
||||
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response) {
|
||||
if (response.empty()) {
|
||||
LOGE("Empty provisioning response.");
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Extracts signed response from JSON string, decodes base64 signed response
|
||||
const std::string kMessageStart = "\"signedResponse\": \"";
|
||||
const std::string kMessageEnd = "\"";
|
||||
std::string serialized_signed_response;
|
||||
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
|
||||
&serialized_signed_response)) {
|
||||
LOGE("Fails to extract signed serialized response from JSON response");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// Creates a crypto session using provisioning_session_.
|
||||
//
|
||||
if (!provisioning_session_) {
|
||||
LOGE("HandleProvisioningResponse: invalid provisioning session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (!crypto_engine) {
|
||||
LOGE("HandleProvisioningResponse: fails to create a crypto engine");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionId cdm_session_id = provisioning_session_->session_id();
|
||||
CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id);
|
||||
if (!crypto_session) {
|
||||
LOGE("HandleProvisioningResponse: fails to find %s",
|
||||
cdm_session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Authenticates provisioning response using D1s (server key derived from
|
||||
// the provisioing request's input). Validate provisioning response and
|
||||
// stores private device RSA key and certificate.
|
||||
SignedProvisioningMessage signed_response;
|
||||
if (!signed_response.ParseFromString(serialized_signed_response)) {
|
||||
LOGE("Fails to parse signed serialized response");
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!signed_response.has_signature() || !signed_response.has_message()) {
|
||||
LOGE("Invalid response - signature or message not found");
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
ProvisioningResponse provisioning_response;
|
||||
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
LOGE("Fails to parse signed message");
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!provisioning_response.has_device_rsa_key()) {
|
||||
LOGE("Invalid response - key not found");
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
const std::string& nonce = provisioning_response.nonce();
|
||||
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
||||
const std::string& signature = signed_response.signature();
|
||||
|
||||
std::string wrapped_rsa_key;
|
||||
if (!crypto_session->RewrapDeviceRSAKey(signed_message,
|
||||
signature,
|
||||
nonce,
|
||||
enc_rsa_key,
|
||||
enc_rsa_key.size(),
|
||||
rsa_key_iv,
|
||||
&wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
CleanupProvisioningSession();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
const std::string& device_certificate = provisioning_response.device_certificate();
|
||||
DeviceFiles::StoreCertificate(device_certificate, wrapped_rsa_key);
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// Deletes cdm and crypto sessions created for provisioning.
|
||||
//
|
||||
CleanupProvisioningSession();
|
||||
return NO_ERROR;
|
||||
return cert_provisioning_.HandleProvisioningResponse(response);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetSecureStops(
|
||||
@@ -776,7 +468,7 @@ CdmResponseType CdmEngine::Decrypt(
|
||||
void* decrypt_buffer,
|
||||
size_t decrypt_buffer_offset,
|
||||
bool is_video) {
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
@@ -789,7 +481,7 @@ CdmResponseType CdmEngine::Decrypt(
|
||||
}
|
||||
|
||||
bool CdmEngine::IsKeyValid(const KeyId& key_id) {
|
||||
for (CdmSessionIter iter = sessions_.begin();
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
if (iter->second->IsKeyValid(key_id)) {
|
||||
return true;
|
||||
@@ -802,7 +494,7 @@ bool CdmEngine::AttachEventListener(
|
||||
const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
}
|
||||
@@ -814,7 +506,7 @@ bool CdmEngine::DetachEventListener(
|
||||
const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
}
|
||||
@@ -923,28 +615,25 @@ bool CdmEngine::ExtractWidevinePssh(
|
||||
}
|
||||
|
||||
void CdmEngine::EnablePolicyTimer() {
|
||||
|
||||
if (!policy_timer_.IsRunning())
|
||||
policy_timer_.Start(this, CDM_POLICY_TIMER_DURATION_SECONDS);
|
||||
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
|
||||
}
|
||||
|
||||
void CdmEngine::DisablePolicyTimer() {
|
||||
|
||||
if (policy_timer_.IsRunning())
|
||||
if (sessions_.size() == 0 && policy_timer_.IsRunning())
|
||||
policy_timer_.Stop();
|
||||
}
|
||||
|
||||
void CdmEngine::OnTimerEvent() {
|
||||
|
||||
for (CdmSessionIter iter = sessions_.begin();
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnTimerEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
|
||||
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
|
||||
for (CdmSessionIter iter = sessions_.begin();
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
|
||||
@@ -7,87 +7,67 @@
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "cdm_engine.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
#include "log.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
namespace {
|
||||
const size_t kKeySetIdLength = 14;
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
|
||||
|
||||
CdmResponseType CdmSession::Init() {
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (!crypto_engine) {
|
||||
LOGE("CdmSession::Init failed to get CryptoEngine instance.");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
crypto_session_ = crypto_engine->CreateSession(session_id_);
|
||||
if (!crypto_session_) {
|
||||
LOGE("CdmSession::Init crypto session creation failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
scoped_ptr<CryptoSession> session(new CryptoSession());
|
||||
if (!session->Open()) return UNKNOWN_ERROR;
|
||||
|
||||
std::string token;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!LoadDeviceCertificate(&token, &wrapped_key_)) {
|
||||
LOGE("CdmSession::Init provisioning needed");
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!crypto_engine->GetToken(&token)) {
|
||||
LOGE("CdmSession::Init token retrieval failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (!LoadDeviceCertificate(&token, &wrapped_key_)) return NEED_PROVISIONING;
|
||||
} else {
|
||||
if (!session->GetToken(&token)) return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_parser_.Init(token, crypto_session_, &policy_engine_))
|
||||
return NO_ERROR;
|
||||
else
|
||||
if (!license_parser_.Init(token, session.get(), &policy_engine_))
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::ReInit() {
|
||||
DestroySession();
|
||||
return Init();
|
||||
}
|
||||
|
||||
bool CdmSession::DestroySession() {
|
||||
if (crypto_session_) {
|
||||
delete crypto_session_;
|
||||
crypto_session_ = NULL;
|
||||
}
|
||||
return true;
|
||||
crypto_session_.reset(session.release());
|
||||
license_received_ = false;
|
||||
reinitialize_session_ = false;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
const CdmKeySetId& key_set_id,
|
||||
const CdmLicenseType license_type) {
|
||||
const CdmKeySetId& key_set_id, const CdmLicenseType license_type) {
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
File file;
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(&file)) return UNKNOWN_ERROR;
|
||||
|
||||
DeviceFiles::LicenseState license_state;
|
||||
|
||||
if (!DeviceFiles::RetrieveLicense(key_set_id, &license_state,
|
||||
&offline_pssh_data_,
|
||||
&offline_key_request_,
|
||||
&offline_key_response_,
|
||||
&offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_,
|
||||
&offline_release_server_url_)) {
|
||||
if (!handle.RetrieveLicense(key_set_id, &license_state, &offline_pssh_data_,
|
||||
&offline_key_request_, &offline_key_response_,
|
||||
&offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_,
|
||||
&offline_release_server_url_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (license_state != DeviceFiles::kLicenseStateActive) {
|
||||
if (license_state != DeviceFiles::kLicenseStateActive) {
|
||||
LOGE("CdmSession::Init invalid offline license state = %s", license_state);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -117,21 +97,18 @@ bool CdmSession::VerifySession(const CdmKeySystem& key_system,
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request,
|
||||
const CdmInitData& init_data, const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
|
||||
if (reinitialize_session_) {
|
||||
CdmResponseType sts = ReInit();
|
||||
CdmResponseType sts = Init();
|
||||
if (sts != NO_ERROR) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Reinitialization failed");
|
||||
return sts;
|
||||
}
|
||||
reinitialize_session_ = false;
|
||||
}
|
||||
|
||||
if (!crypto_session_) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -145,17 +122,23 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
|
||||
if (license_type_ == kLicenseTypeRelease) {
|
||||
return GenerateReleaseRequest(key_request, server_url);
|
||||
}
|
||||
else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request() ?
|
||||
UNKNOWN_ERROR : GenerateRenewalRequest(key_request, server_url);
|
||||
}
|
||||
else {
|
||||
CdmInitData pssh_data;
|
||||
if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) {
|
||||
} else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request()
|
||||
? UNKNOWN_ERROR
|
||||
: GenerateRenewalRequest(key_request, server_url);
|
||||
} else {
|
||||
if (init_data.empty()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
CdmInitData pssh_data = init_data;
|
||||
if (Properties::extract_pssh_data()) {
|
||||
if (!CdmEngine::ExtractWidevinePssh(init_data, &pssh_data)) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
|
||||
reinitialize_session_ = true;
|
||||
@@ -163,11 +146,8 @@ 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, key_request, server_url)) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -182,10 +162,9 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
}
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
CdmResponseType CdmSession::AddKey(
|
||||
const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (!crypto_session_) {
|
||||
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::AddKey: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -197,25 +176,32 @@ CdmResponseType CdmSession::AddKey(
|
||||
|
||||
if (license_type_ == kLicenseTypeRelease) {
|
||||
return ReleaseKey(key_response);
|
||||
}
|
||||
else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request() ?
|
||||
UNKNOWN_ERROR : RenewKey(key_response);
|
||||
}
|
||||
else {
|
||||
} else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request()
|
||||
? UNKNOWN_ERROR
|
||||
: RenewKey(key_response);
|
||||
} else {
|
||||
CdmResponseType sts = license_parser_.HandleKeyResponse(key_response);
|
||||
|
||||
if (sts != KEY_ADDED)
|
||||
return sts;
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
|
||||
license_received_ = true;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_response_ = key_response;
|
||||
key_set_id_ = GenerateKeySetId(offline_pssh_data_);
|
||||
if (!StoreLicense(true)) {
|
||||
if (!GenerateKeySetId(&key_set_id_)) {
|
||||
LOGE("CdmSession::AddKey: Unable to generate key set Id");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
|
||||
LOGE("CdmSession::AddKey: Unable to store license");
|
||||
ReInit();
|
||||
CdmResponseType sts = Init();
|
||||
if (sts != NO_ERROR) {
|
||||
LOGW("CdmSession::AddKey: Reinitialization failed");
|
||||
return sts;
|
||||
}
|
||||
|
||||
key_set_id_.clear();
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -231,7 +217,7 @@ CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
if ((!crypto_session_) || (!crypto_session_->IsOpen()))
|
||||
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
std::stringstream ss;
|
||||
@@ -248,17 +234,12 @@ CdmResponseType CdmSession::CancelKeyRequest() {
|
||||
}
|
||||
|
||||
// Decrypt() - Accept encrypted buffer and return decrypted data.
|
||||
CdmResponseType CdmSession::Decrypt(bool is_encrypted,
|
||||
bool is_secure,
|
||||
const KeyId& key_id,
|
||||
const uint8_t* encrypt_buffer,
|
||||
size_t encrypt_length,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t block_offset,
|
||||
void* decrypt_buffer,
|
||||
size_t decrypt_buffer_offset,
|
||||
bool is_video) {
|
||||
if (!crypto_session_ || !crypto_session_->IsOpen())
|
||||
CdmResponseType CdmSession::Decrypt(
|
||||
bool is_encrypted, bool is_secure, const KeyId& key_id,
|
||||
const uint8_t* encrypt_buffer, size_t encrypt_length,
|
||||
const std::vector<uint8_t>& iv, size_t block_offset, void* decrypt_buffer,
|
||||
size_t decrypt_buffer_offset, bool is_video) {
|
||||
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
// Check if key needs to be selected
|
||||
@@ -266,17 +247,15 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted,
|
||||
if (key_id_.compare(key_id) != 0) {
|
||||
if (crypto_session_->SelectKey(key_id)) {
|
||||
key_id_ = key_id;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crypto_session_->Decrypt(is_encrypted, is_secure, encrypt_buffer,
|
||||
encrypt_length, iv, block_offset,
|
||||
decrypt_buffer, decrypt_buffer_offset,
|
||||
is_video);
|
||||
return crypto_session_->Decrypt(
|
||||
is_encrypted, is_secure, encrypt_buffer, encrypt_length, iv, block_offset,
|
||||
decrypt_buffer, decrypt_buffer_offset, is_video);
|
||||
}
|
||||
|
||||
// License renewal
|
||||
@@ -295,37 +274,34 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(true,
|
||||
key_response);
|
||||
if (sts != KEY_ADDED)
|
||||
return sts;
|
||||
CdmResponseType sts =
|
||||
license_parser_.HandleKeyUpdateResponse(true, key_response);
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
|
||||
if (license_type_ == kLicenseTypeOffline) {
|
||||
offline_key_renewal_response_ = key_response;
|
||||
if (!StoreLicense(true))
|
||||
return UNKNOWN_ERROR;
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) return UNKNOWN_ERROR;
|
||||
}
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (license_parser_.PrepareKeyUpdateRequest(false, key_request,
|
||||
server_url)) {
|
||||
if (license_parser_.PrepareKeyUpdateRequest(false, key_request, server_url)) {
|
||||
// Mark license as being released
|
||||
if (!StoreLicense(false))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
return KEY_MESSAGE;
|
||||
if (StoreLicense(DeviceFiles::kLicenseStateReleasing)) return KEY_MESSAGE;
|
||||
}
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// ReleaseKey() - Accept release response and release license.
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false,
|
||||
key_response);
|
||||
DeviceFiles::DeleteLicense(key_set_id_);
|
||||
CdmResponseType sts =
|
||||
license_parser_.HandleKeyUpdateResponse(false, key_response);
|
||||
File file;
|
||||
DeviceFiles handle;
|
||||
if (handle.Init(&file)) handle.DeleteLicense(key_set_id_);
|
||||
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -342,51 +318,51 @@ CdmSessionId CdmSession::GenerateSessionId() {
|
||||
return SESSION_ID_PREFIX + IntToString(++session_num);
|
||||
}
|
||||
|
||||
bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
||||
if (!key_set_id) {
|
||||
LOGW("CdmSession::GenerateKeySetId: key set id destination not provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
CdmSessionId CdmSession::GenerateKeySetId(CdmInitData& pssh_data) {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
std::string key_set_id;
|
||||
std::vector<uint8_t> random_data(
|
||||
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
|
||||
|
||||
while (key_set_id.empty()) {
|
||||
int random = rand();
|
||||
File file;
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(&file)) return false;
|
||||
|
||||
std::vector<uint8_t> hash(SHA256_DIGEST_LENGTH, 0);
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
SHA256_Update(&sha256, pssh_data.data(), pssh_data.size());
|
||||
SHA256_Update(&sha256, ¤t_time, sizeof(int64_t));
|
||||
SHA256_Update(&sha256, &random, sizeof(random));
|
||||
SHA256_Final(&hash[0], &sha256);
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
|
||||
hash[i%(SHA256_DIGEST_LENGTH/4)] ^= hash[i];
|
||||
}
|
||||
hash.resize(SHA256_DIGEST_LENGTH/4);
|
||||
key_set_id = KEY_SET_ID_PREFIX + b2a_hex(hash);
|
||||
while (key_set_id->empty()) {
|
||||
if (!crypto_session_->GetRandom(&random_data[0], random_data.size()))
|
||||
return false;
|
||||
|
||||
if (DeviceFiles::LicenseExists(key_set_id)) { // key set collision
|
||||
key_set_id.clear();
|
||||
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
|
||||
|
||||
// key set collision
|
||||
if (handle.LicenseExists(*key_set_id)) {
|
||||
key_set_id->clear();
|
||||
}
|
||||
}
|
||||
return key_set_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdmSession::LoadDeviceCertificate(std::string* certificate,
|
||||
std::string* wrapped_key) {
|
||||
return DeviceFiles::RetrieveCertificate(certificate,
|
||||
wrapped_key);
|
||||
File file;
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(&file)) return false;
|
||||
|
||||
return handle.RetrieveCertificate(certificate, wrapped_key);
|
||||
}
|
||||
|
||||
bool CdmSession::StoreLicense(bool active) {
|
||||
DeviceFiles::LicenseState state = DeviceFiles::kLicenseStateReleasing;
|
||||
if (active)
|
||||
state = DeviceFiles::kLicenseStateActive;
|
||||
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
File file;
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(&file)) return false;
|
||||
|
||||
return DeviceFiles::StoreLicense(key_set_id_, state, offline_pssh_data_,
|
||||
offline_key_request_, offline_key_response_,
|
||||
offline_key_renewal_request_,
|
||||
offline_key_renewal_response_,
|
||||
offline_release_server_url_);
|
||||
return handle.StoreLicense(
|
||||
key_set_id_, state, offline_pssh_data_, offline_key_request_,
|
||||
offline_key_response_, offline_key_renewal_request_,
|
||||
offline_key_renewal_response_, offline_release_server_url_);
|
||||
}
|
||||
|
||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||
@@ -407,22 +383,16 @@ void CdmSession::OnTimerEvent() {
|
||||
if (event_occurred) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
CdmSessionId id = (*iter)->session_id();
|
||||
if (id.empty() || (id.compare(session_id_) == 0)) {
|
||||
(*iter)->onEvent(session_id_, event);
|
||||
}
|
||||
(*iter)->onEvent(session_id_, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CdmSession::OnKeyReleaseEvent(CdmKeySetId key_set_id) {
|
||||
if (key_set_id_.compare(key_set_id) == 0) {
|
||||
void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
if (key_set_id_ == key_set_id) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
CdmSessionId id = (*iter)->session_id();
|
||||
if (id.empty() || (id.compare(session_id_) == 0)) {
|
||||
(*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT);
|
||||
}
|
||||
iter != listeners_.end(); ++iter) {
|
||||
(*iter)->onEvent(session_id_, LICENSE_EXPIRED_EVENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
229
libwvdrmengine/cdm/core/src/certificate_provisioning.cpp
Normal file
229
libwvdrmengine/cdm/core/src/certificate_provisioning.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "certificate_provisioning.h"
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace {
|
||||
const std::string kDefaultProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::ProvisioningRequest;
|
||||
using video_widevine_server::sdk::ProvisioningResponse;
|
||||
using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
|
||||
/*
|
||||
* This function converts SignedProvisioningRequest into base64 string.
|
||||
* It then wraps it in JSON format expected by the Apiary frontend.
|
||||
* Apiary requires the base64 encoding to replace '+' with minus '-',
|
||||
* and '/' with underscore '_'; opposite to stubby's.
|
||||
*
|
||||
* Returns the JSON formated string in *request. The JSON string will be
|
||||
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
|
||||
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
|
||||
*
|
||||
* The JSON formated request takes the following format:
|
||||
*
|
||||
* base64 encoded message
|
||||
*/
|
||||
void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
const std::string& message,
|
||||
CdmProvisioningRequest* request) {
|
||||
|
||||
// Performs base64 encoding for message
|
||||
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
||||
std::string message_b64 = Base64SafeEncodeNoPad(message_vector);
|
||||
request->assign(message_b64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Composes a device provisioning request and output the request in JSON format
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
* in *default_url.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
default_url->assign(kDefaultProvisioningServerUrl);
|
||||
|
||||
if (!crypto_session_.Open()) {
|
||||
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// Prepares device provisioning request.
|
||||
ProvisioningRequest provisioning_request;
|
||||
ClientIdentification* client_id = provisioning_request.mutable_client_id();
|
||||
client_id->set_type(ClientIdentification::KEYBOX);
|
||||
std::string token;
|
||||
if (!crypto_session_.GetToken(&token)) {
|
||||
LOGE("GetProvisioningRequest: fails to get token");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
client_id->set_token(token);
|
||||
|
||||
uint32_t nonce;
|
||||
if (!crypto_session_.GenerateNonce(&nonce)) {
|
||||
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// The provisioning server does not convert the nonce to uint32_t, it just
|
||||
// passes the binary data to the response message.
|
||||
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
|
||||
provisioning_request.set_nonce(the_nonce);
|
||||
|
||||
std::string serialized_message;
|
||||
provisioning_request.SerializeToString(&serialized_message);
|
||||
|
||||
// Derives signing and encryption keys and constructs signature.
|
||||
std::string request_signature;
|
||||
if (!crypto_session_.PrepareRequest(serialized_message, true,
|
||||
&request_signature)) {
|
||||
LOGE("GetProvisioningRequest: fails to prepare request");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (request_signature.empty()) {
|
||||
LOGE("GetProvisioningRequest: request signature is empty");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
SignedProvisioningMessage signed_provisioning_msg;
|
||||
signed_provisioning_msg.set_message(serialized_message);
|
||||
signed_provisioning_msg.set_signature(request_signature);
|
||||
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
|
||||
// Converts request into JSON string
|
||||
ComposeJsonRequestAsQueryString(serialized_request, request);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses the input json_str and locates substring using start_substr and
|
||||
* end_stubstr. The found base64 substring is then decoded and returns
|
||||
* in *result.
|
||||
*
|
||||
* Returns true for success and false if fails.
|
||||
*/
|
||||
bool CertificateProvisioning::ParseJsonResponse(
|
||||
const CdmProvisioningResponse& json_str,
|
||||
const std::string& start_substr,
|
||||
const std::string& end_substr,
|
||||
std::string* result) {
|
||||
std::string b64_string;
|
||||
size_t start = json_str.find(start_substr);
|
||||
if (start == json_str.npos) {
|
||||
LOGE("ParseJsonResponse: cannot find start substring");
|
||||
return false;
|
||||
}
|
||||
size_t end = json_str.find(end_substr, start + start_substr.length());
|
||||
if (end == json_str.npos) {
|
||||
LOGE("ParseJsonResponse cannot locate end substring");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t b64_string_size = end - start - start_substr.length();
|
||||
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
|
||||
|
||||
// Decodes base64 substring and returns it in *result
|
||||
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
|
||||
result->assign(result_vector.begin(), result_vector.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The response message consists of a device certificate and the device RSA key.
|
||||
* The device RSA key is stored in the T.E.E. The device certificate is stored
|
||||
* in the device.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response) {
|
||||
|
||||
// Extracts signed response from JSON string, decodes base64 signed response
|
||||
const std::string kMessageStart = "\"signedResponse\": \"";
|
||||
const std::string kMessageEnd = "\"";
|
||||
std::string serialized_signed_response;
|
||||
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
|
||||
&serialized_signed_response)) {
|
||||
LOGE("Fails to extract signed serialized response from JSON response");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// Authenticates provisioning response using D1s (server key derived from
|
||||
// the provisioing request's input). Validate provisioning response and
|
||||
// stores private device RSA key and certificate.
|
||||
SignedProvisioningMessage signed_response;
|
||||
if (!signed_response.ParseFromString(serialized_signed_response)) {
|
||||
LOGE("HandleProvisioningResponse: fails to parse signed response");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!signed_response.has_signature() || !signed_response.has_message()) {
|
||||
LOGE("HandleProvisioningResponse: signature or message not found");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
ProvisioningResponse provisioning_response;
|
||||
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
LOGE("HandleProvisioningResponse: Fails to parse signed message");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!provisioning_response.has_device_rsa_key()) {
|
||||
LOGE("HandleProvisioningResponse: key not found");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
const std::string& nonce = provisioning_response.nonce();
|
||||
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
||||
const std::string& signature = signed_response.signature();
|
||||
std::string wrapped_rsa_key;
|
||||
if (!crypto_session_.RewrapDeviceRSAKey(signed_message,
|
||||
signature,
|
||||
nonce,
|
||||
enc_rsa_key,
|
||||
rsa_key_iv,
|
||||
&wrapped_rsa_key)){
|
||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
crypto_session_.Close();
|
||||
|
||||
const std::string& device_certificate =
|
||||
provisioning_response.device_certificate();
|
||||
|
||||
File file;
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(&file)) {
|
||||
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -1,286 +0,0 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Crypto - wrapper classes for OEMCrypto interface
|
||||
//
|
||||
|
||||
#include "crypto_engine.h"
|
||||
|
||||
#include <arpa/inet.h> // TODO(fredgc): Add ntoh to wv_cdm_utilities.h
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "log.h"
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
CryptoEngine* CryptoEngine::crypto_engine_ = NULL;
|
||||
Lock CryptoEngine::crypto_engine_lock_;
|
||||
|
||||
// wrapper classes for OEMCrypto interface
|
||||
// CryptoEngine -- top-level interface
|
||||
// CryptoSession -- session-specific interface
|
||||
// CryptoKey -- key interface
|
||||
|
||||
// CryptoEngine methods
|
||||
|
||||
CryptoEngine::CryptoEngine() : initialized_(false) {}
|
||||
|
||||
CryptoEngine::~CryptoEngine() {
|
||||
if (initialized_) {
|
||||
Terminate();
|
||||
}
|
||||
|
||||
CryptoSessionMap::iterator i(sessions_.begin());
|
||||
for (; i != sessions_.end(); ++i)
|
||||
delete i->second;
|
||||
sessions_.clear();
|
||||
}
|
||||
|
||||
// get the instance of OEMCrypto Client
|
||||
CryptoEngine* CryptoEngine::GetInstance() {
|
||||
if (NULL == crypto_engine_) {
|
||||
crypto_engine_ = CreateSingleton();
|
||||
}
|
||||
return crypto_engine_;
|
||||
}
|
||||
|
||||
CryptoEngine* CryptoEngine::CreateSingleton() {
|
||||
AutoLock auto_lock(crypto_engine_lock_);
|
||||
if (NULL == crypto_engine_) {
|
||||
crypto_engine_ = new CryptoEngine;
|
||||
}
|
||||
return crypto_engine_;
|
||||
}
|
||||
|
||||
void CryptoEngine::DeleteInstance() {
|
||||
if (NULL != crypto_engine_) {
|
||||
delete crypto_engine_;
|
||||
LOGV("CryptoEngine::DeleteInstance");
|
||||
crypto_engine_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CryptoEngine::Init() {
|
||||
LOGV("CryptoEngine::Init: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
OEMCryptoResult result = OEMCrypto_Initialize();
|
||||
initialized_ = (OEMCrypto_SUCCESS == result);
|
||||
}
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
bool CryptoEngine::Terminate() {
|
||||
DestroySessions();
|
||||
LOGV("CryptoEngine::Terminate: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
OEMCryptoResult result = OEMCrypto_Terminate();
|
||||
if (OEMCrypto_SUCCESS == result) {
|
||||
initialized_ = false;
|
||||
}
|
||||
return !initialized_;
|
||||
}
|
||||
|
||||
bool CryptoEngine::ValidateKeybox() {
|
||||
LOGV("CryptoEngine::ValidateKeybox: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
OEMCryptoResult result = OEMCrypto_IsKeyboxValid();
|
||||
return (OEMCrypto_SUCCESS == result);
|
||||
}
|
||||
|
||||
CryptoSession* CryptoEngine::CreateSession(const CdmSessionId& session_id) {
|
||||
LOGV("CryptoEngine::CreateSession: SLock");
|
||||
AutoLock auto_lock(sessions_lock_);
|
||||
if (0 == sessions_.size()) {
|
||||
if (!Init()) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CryptoSessionMap::iterator it = sessions_.find(session_id);
|
||||
if (it != sessions_.end()) {
|
||||
LOGE("CryptoEngine::CreateSession : Duplicate session ID.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CryptoSession* new_session = new CryptoSession(session_id);
|
||||
if (!new_session) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!new_session->Open()) {
|
||||
delete new_session;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sessions_[session_id] = new_session;
|
||||
return new_session;
|
||||
}
|
||||
|
||||
CryptoSession* CryptoEngine::FindSessionInternal(
|
||||
const CdmSessionId& session_id) {
|
||||
// must hold sessions_lock_
|
||||
CryptoSessionMap::iterator it = sessions_.find(session_id);
|
||||
if (it != sessions_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CryptoSession* CryptoEngine::FindSession(const CdmSessionId& session_id) {
|
||||
LOGV("CryptoEngine::FindSession: SLock");
|
||||
AutoLock auto_lock(sessions_lock_);
|
||||
return FindSessionInternal(session_id);
|
||||
}
|
||||
|
||||
bool CryptoEngine::DestroySession(const CdmSessionId& session_id) {
|
||||
LOGV("CryptoEngine::DestroySession: SLock");
|
||||
AutoLock auto_lock(sessions_lock_);
|
||||
if (0 == sessions_.size()) {
|
||||
return false;
|
||||
}
|
||||
CryptoSession* session = FindSessionInternal(session_id);
|
||||
if (session) {
|
||||
delete session;
|
||||
sessions_.erase(session_id);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CryptoEngine::DestroySessions() {
|
||||
for (CryptoSessionMap::iterator it = sessions_.begin();
|
||||
it != sessions_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
sessions_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoEngine::GetToken(std::string* token) {
|
||||
LOGV("CryptoEngine::GetToken: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!token) {
|
||||
LOGE("CryptoEngine::GetToken : No token passed to method.");
|
||||
return false;
|
||||
}
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t bufSize = sizeof(buf);
|
||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
token->assign((const char*)buf, bufSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
CryptoEngine::SecurityLevel CryptoEngine::GetSecurityLevel() {
|
||||
if (!Init())
|
||||
return kSecurityLevelUnknown;
|
||||
|
||||
LOGV("CryptoEngine::GetSecurityLevel: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
std::string security_level = OEMCrypto_SecurityLevel();
|
||||
|
||||
if ((security_level.size() != 2) ||
|
||||
(security_level.at(0) != 'L')) {
|
||||
return kSecurityLevelUnknown;
|
||||
}
|
||||
|
||||
switch (security_level.at(1)) {
|
||||
case '1': return kSecurityLevelL1;
|
||||
case '2': return kSecurityLevelL2;
|
||||
case '3': return kSecurityLevelL3;
|
||||
default : return kSecurityLevelUnknown;
|
||||
}
|
||||
|
||||
return kSecurityLevelUnknown;
|
||||
}
|
||||
|
||||
bool CryptoEngine::GetDeviceUniqueId(std::string* deviceId) {
|
||||
if (!Init())
|
||||
return false;
|
||||
|
||||
if (!deviceId) {
|
||||
LOGE("CryptoEngine::GetDeviceUniqueId : No buffer passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("CryptoEngine::GetDeviceUniqueId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
std::vector<uint8_t> id;
|
||||
size_t idLength = 32;
|
||||
|
||||
id.resize(idLength);
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &idLength);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*deviceId = reinterpret_cast<const char*>(&id[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoEngine::GetSystemId(uint32_t* systemId) {
|
||||
if (!Init())
|
||||
return false;
|
||||
|
||||
if (!systemId) {
|
||||
LOGE("CryptoEngine::GetSystemId : No buffer passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("CryptoEngine::GetSystemId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t bufSize = sizeof(buf);
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decode 32-bit int encoded as network-byte-order byte array starting at
|
||||
// index 4.
|
||||
uint32_t* id = reinterpret_cast<uint32_t*>(&buf[4]);
|
||||
|
||||
*systemId = ntohl(*id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoEngine::GetProvisioningId(std::string* provisioningId) {
|
||||
if (!Init())
|
||||
return false;
|
||||
|
||||
if (!provisioningId) {
|
||||
LOGE("CryptoEngine::GetProvisioningId : No buffer passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("CryptoEngine::GetProvisioningId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t bufSize = sizeof(buf);
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
provisioningId->assign(reinterpret_cast<char*>(&buf[8]), 16);
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
#include "crypto_session.h"
|
||||
|
||||
#include <arpa/inet.h> // TODO(fredgc): Add ntoh to wv_cdm_utilities.h
|
||||
#include <iostream>
|
||||
|
||||
#include "crypto_engine.h"
|
||||
#include "crypto_key.h"
|
||||
#include "log.h"
|
||||
// TODO(gmorgan,jtinker): decide if OEMCryptoCENC is needed here.
|
||||
#include "OEMCryptoCENC.h"
|
||||
@@ -21,95 +22,226 @@ std::string EncodeUint32(unsigned int u) {
|
||||
std::string s;
|
||||
s.append(1, (u >> 24) & 0xFF);
|
||||
s.append(1, (u >> 16) & 0xFF);
|
||||
s.append(1, (u >> 8) & 0xFF);
|
||||
s.append(1, (u >> 0) & 0xFF);
|
||||
s.append(1, (u >> 8) & 0xFF);
|
||||
s.append(1, (u >> 0) & 0xFF);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// wrapper classes for OEMCrypto interface
|
||||
// CryptoEngine -- top-level interface
|
||||
// CryptoSession -- session-specific interface
|
||||
// CryptoKey -- key interface
|
||||
Lock CryptoSession::crypto_lock_;
|
||||
bool CryptoSession::initialized_ = false;
|
||||
int CryptoSession::session_count_ = 0;
|
||||
|
||||
// CryptoSession methods
|
||||
|
||||
CryptoSession::CryptoSession() :
|
||||
valid_(false),
|
||||
open_(false),
|
||||
is_destination_buffer_type_valid_(false) {}
|
||||
|
||||
CryptoSession::CryptoSession(const std::string& sname) :
|
||||
valid_(true),
|
||||
open_(false),
|
||||
cdm_session_id_(sname),
|
||||
is_destination_buffer_type_valid_(false) {}
|
||||
CryptoSession::CryptoSession()
|
||||
: open_(false), is_destination_buffer_type_valid_(false) {
|
||||
Init();
|
||||
}
|
||||
|
||||
CryptoSession::~CryptoSession() {
|
||||
if (open_) {
|
||||
Close();
|
||||
}
|
||||
LOGV("CryptoSession::dtor: SLock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
crypto_engine->sessions_.erase(cdm_session_id_);
|
||||
if (0 == crypto_engine->sessions_.size()) {
|
||||
crypto_engine->DeleteInstance();
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void CryptoSession::Init() {
|
||||
LOGV("CryptoSession::Init");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
session_count_ += 1;
|
||||
if (initialized_) return;
|
||||
OEMCryptoResult sts = OEMCrypto_Initialize();
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("OEMCrypto_Initialize failed: %d", sts);
|
||||
return;
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void CryptoSession::Terminate() {
|
||||
LOGV("CryptoSession::Terminate");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
session_count_ -= 1;
|
||||
if (session_count_ > 0 || !initialized_) return;
|
||||
OEMCryptoResult sts = OEMCrypto_Terminate();
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("OEMCrypto_Terminate failed: %d", sts);
|
||||
}
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
bool CryptoSession::ValidateKeybox() {
|
||||
LOGV("CryptoSession::ValidateKeybox: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_IsKeyboxValid();
|
||||
return (OEMCrypto_SUCCESS == result);
|
||||
}
|
||||
|
||||
bool CryptoSession::GetToken(std::string* token) {
|
||||
if (!token) {
|
||||
LOGE("CryptoSession::GetToken : No token passed to method.");
|
||||
return false;
|
||||
}
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t bufSize = sizeof(buf);
|
||||
LOGV("CryptoSession::GetToken: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &bufSize);
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
token->assign((const char*)buf, (size_t)bufSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
CryptoSession::SecurityLevel CryptoSession::GetSecurityLevel() {
|
||||
LOGV("CryptoSession::GetSecurityLevel: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return kSecurityLevelUninitialized;
|
||||
}
|
||||
std::string security_level = OEMCrypto_SecurityLevel();
|
||||
|
||||
if ((security_level.size() != 2) || (security_level.at(0) != 'L')) {
|
||||
return kSecurityLevelUnknown;
|
||||
}
|
||||
|
||||
CryptoKeyMap::iterator i(keys_.begin());
|
||||
for (; i != keys_.end(); ++i)
|
||||
delete i->second;
|
||||
keys_.clear();
|
||||
switch (security_level.at(1)) {
|
||||
case '1':
|
||||
return kSecurityLevelL1;
|
||||
case '2':
|
||||
return kSecurityLevelL2;
|
||||
case '3':
|
||||
return kSecurityLevelL3;
|
||||
default:
|
||||
return kSecurityLevelUnknown;
|
||||
}
|
||||
|
||||
return kSecurityLevelUnknown;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
||||
if (!device_id) {
|
||||
LOGE("CryptoSession::GetDeviceUniqueId : No buffer passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> id;
|
||||
size_t id_length = 32;
|
||||
|
||||
id.resize(id_length);
|
||||
|
||||
LOGV("CryptoSession::GetDeviceUniqueId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &id_length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*device_id = reinterpret_cast<const char*>(&id[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetSystemId(uint32_t* system_id) {
|
||||
if (!system_id) {
|
||||
LOGE("CryptoSession::GetSystemId : No buffer passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t buf_size = sizeof(buf);
|
||||
|
||||
LOGV("CryptoSession::GetSystemId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decode 32-bit int encoded as network-byte-order byte array starting at
|
||||
// index 4.
|
||||
uint32_t* id = reinterpret_cast<uint32_t*>(&buf[4]);
|
||||
|
||||
*system_id = ntohl(*id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
||||
if (!provisioning_id) {
|
||||
LOGE("CryptoSession::GetProvisioningId : No buffer passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t buf_size = sizeof(buf);
|
||||
|
||||
LOGV("CryptoSession::GetProvisioningId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buf_size);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
provisioning_id->assign(reinterpret_cast<char*>(&buf[8]), 16);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::Open() {
|
||||
LOGV("CryptoSession::Open: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) return false;
|
||||
if (open_) return true;
|
||||
|
||||
OEMCrypto_SESSION sid;
|
||||
OEMCryptoResult sts;
|
||||
if (open_)
|
||||
return false;
|
||||
sts = OEMCrypto_OpenSession(&sid);
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
open_ = false;
|
||||
} else {
|
||||
if (OEMCrypto_SUCCESS == OEMCrypto_OpenSession(&sid)) {
|
||||
oec_session_id_ = static_cast<CryptoSessionId>(sid);
|
||||
LOGV("OpenSession: id= %ld", (uint32_t) oec_session_id_);
|
||||
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
|
||||
open_ = true;
|
||||
}
|
||||
return open_;
|
||||
}
|
||||
|
||||
void CryptoSession::Close() {
|
||||
LOGV("CryptoSession::Close: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
LOGV("CloseSession: id=%ld open=%s", (uint32_t) oec_session_id_, open_? "true" : "false") ;
|
||||
if (open_) {
|
||||
OEMCryptoResult sts = OEMCrypto_CloseSession(oec_session_id_);
|
||||
if (OEMCrypto_SUCCESS == sts) {
|
||||
open_ = false;
|
||||
}
|
||||
LOGV("CloseSession: id=%ld open=%s", (uint32_t)oec_session_id_,
|
||||
open_ ? "true" : "false");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!open_) return;
|
||||
if (OEMCrypto_SUCCESS == OEMCrypto_CloseSession(oec_session_id_)) {
|
||||
open_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CryptoSession::GenerateRequestId(std::string& req_id_str) {
|
||||
LOGV("CryptoSession::GenerateRequestId: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
// TODO(gmorgan): Get unique ID from OEMCrypto
|
||||
req_id_str.assign("987654321");
|
||||
}
|
||||
|
||||
bool CryptoSession::PrepareRequest(const std::string& message,
|
||||
std::string* signature,
|
||||
bool is_provisioning) {
|
||||
bool is_provisioning,
|
||||
std::string* signature) {
|
||||
LOGV("CryptoSession::PrepareRequest: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
if (!signature) {
|
||||
LOGE("CryptoSession::PrepareRequest : No output destination provided.");
|
||||
@@ -117,15 +249,11 @@ bool CryptoSession::PrepareRequest(const std::string& message,
|
||||
}
|
||||
|
||||
if (!Properties::use_certificates_as_identification() || is_provisioning) {
|
||||
if (!GenerateDerivedKeys(message))
|
||||
return false;
|
||||
if (!GenerateDerivedKeys(message)) return false;
|
||||
|
||||
if (!GenerateSignature(message, signature, false))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (!GenerateSignature(message, signature, true))
|
||||
return false;
|
||||
if (!GenerateSignature(message, false, signature)) return false;
|
||||
} else {
|
||||
if (!GenerateSignature(message, true, signature)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -134,15 +262,15 @@ bool CryptoSession::PrepareRequest(const std::string& message,
|
||||
bool CryptoSession::PrepareRenewalRequest(const std::string& message,
|
||||
std::string* signature) {
|
||||
LOGV("CryptoSession::PrepareRenewalRequest: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
if (!signature) {
|
||||
LOGE("CryptoSession::PrepareRenewalRequest : No output destination provided.");
|
||||
LOGE("CryptoSession::PrepareRenewalRequest : No output destination "
|
||||
"provided.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GenerateSignature(message, signature, false)) {
|
||||
if (!GenerateSignature(message, false, signature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,13 +290,14 @@ void CryptoSession::GenerateMacContext(const std::string& input_context,
|
||||
deriv_context->assign(kSigningKeyLabel);
|
||||
deriv_context->append(1, '\0');
|
||||
deriv_context->append(input_context);
|
||||
deriv_context->append(EncodeUint32(kSigningKeySizeBits*2));
|
||||
deriv_context->append(EncodeUint32(kSigningKeySizeBits * 2));
|
||||
}
|
||||
|
||||
void CryptoSession::GenerateEncryptContext(const std::string& input_context,
|
||||
std::string* deriv_context) {
|
||||
if (!deriv_context) {
|
||||
LOGE("CryptoSession::GenerateEncryptContext : No output destination provided.");
|
||||
LOGE("CryptoSession::GenerateEncryptContext : No output destination "
|
||||
"provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,12 +322,10 @@ size_t CryptoSession::GetOffset(std::string message, std::string field) {
|
||||
bool CryptoSession::LoadKeys(const std::string& message,
|
||||
const std::string& signature,
|
||||
const std::string& mac_key_iv,
|
||||
const std::string& mac_key,
|
||||
int num_keys,
|
||||
const std::string& mac_key, int num_keys,
|
||||
const CryptoKey* key_array) {
|
||||
LOGV("CryptoSession::LoadKeys: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
const uint8_t* enc_mac_key = NULL;
|
||||
@@ -208,7 +335,7 @@ bool CryptoSession::LoadKeys(const std::string& message,
|
||||
enc_mac_key_iv = msg + GetOffset(message, mac_key_iv);
|
||||
}
|
||||
std::vector<OEMCrypto_KeyObject> load_key_array(num_keys);
|
||||
for (int i=0; i<num_keys; ++i) {
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
const CryptoKey* ki = &key_array[i];
|
||||
OEMCrypto_KeyObject* ko = &load_key_array[i];
|
||||
ko->key_id = msg + GetOffset(message, ki->key_id());
|
||||
@@ -219,30 +346,28 @@ bool CryptoSession::LoadKeys(const std::string& message,
|
||||
if (ki->HasKeyControl()) {
|
||||
ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv());
|
||||
ko->key_control = msg + GetOffset(message, ki->key_control());
|
||||
}
|
||||
else {
|
||||
LOGE("For key %d: XXX key has no control block. size=%d", i, ki->key_control().size());
|
||||
} else {
|
||||
LOGE("For key %d: XXX key has no control block. size=%d", i,
|
||||
ki->key_control().size());
|
||||
ko->key_control_iv = NULL;
|
||||
ko->key_control = NULL;
|
||||
}
|
||||
}
|
||||
LOGV("LoadKeys: id=%ld", (uint32_t) oec_session_id_);
|
||||
return (OEMCrypto_SUCCESS == OEMCrypto_LoadKeys(
|
||||
oec_session_id_, msg, message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()),
|
||||
signature.size(), enc_mac_key_iv, enc_mac_key,
|
||||
num_keys, &load_key_array[0]));
|
||||
LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_);
|
||||
return (OEMCrypto_SUCCESS ==
|
||||
OEMCrypto_LoadKeys(oec_session_id_, msg, message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()),
|
||||
signature.size(), enc_mac_key_iv, enc_mac_key,
|
||||
num_keys, &load_key_array[0]));
|
||||
}
|
||||
|
||||
bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
|
||||
LOGV("CryptoSession::LoadKeys: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t) oec_session_id_);
|
||||
LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_);
|
||||
OEMCryptoResult sts = OEMCrypto_LoadDeviceRSAKey(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<const uint8_t*>(wrapped_key.data()),
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(wrapped_key.data()),
|
||||
wrapped_key.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
@@ -254,16 +379,14 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
|
||||
}
|
||||
|
||||
bool CryptoSession::RefreshKeys(const std::string& message,
|
||||
const std::string& signature,
|
||||
int num_keys,
|
||||
const std::string& signature, int num_keys,
|
||||
const CryptoKey* key_array) {
|
||||
LOGV("CryptoSession::RefreshKeys: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
const uint8_t* msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
std::vector<OEMCrypto_KeyRefreshObject> load_key_array(num_keys);
|
||||
for (int i=0; i<num_keys; ++i) {
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
const CryptoKey* ki = &key_array[i];
|
||||
OEMCrypto_KeyRefreshObject* ko = &load_key_array[i];
|
||||
if (ki->key_id().empty()) {
|
||||
@@ -278,30 +401,28 @@ bool CryptoSession::RefreshKeys(const std::string& message,
|
||||
ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv());
|
||||
}
|
||||
ko->key_control = msg + GetOffset(message, ki->key_control());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ko->key_control_iv = NULL;
|
||||
ko->key_control = NULL;
|
||||
}
|
||||
}
|
||||
LOGV("RefreshKeys: id=%ld", static_cast<uint32_t>(oec_session_id_));
|
||||
return (OEMCrypto_SUCCESS == OEMCrypto_RefreshKeys(
|
||||
oec_session_id_, msg, message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()),
|
||||
signature.size(),
|
||||
num_keys, &load_key_array[0]));
|
||||
return (
|
||||
OEMCrypto_SUCCESS ==
|
||||
OEMCrypto_RefreshKeys(oec_session_id_, msg, message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()),
|
||||
signature.size(), num_keys, &load_key_array[0]));
|
||||
}
|
||||
|
||||
bool CryptoSession::SelectKey(const std::string& key_id) {
|
||||
LOGV("CryptoSession::SelectKey: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
const uint8_t* key_id_string =
|
||||
reinterpret_cast<const uint8_t*>(key_id.data());
|
||||
|
||||
LOGV("SelectKey: id=%ld", static_cast<uint32_t>(oec_session_id_));
|
||||
OEMCryptoResult sts = OEMCrypto_SelectKey(oec_session_id_, key_id_string,
|
||||
key_id.size());
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_SelectKey(oec_session_id_, key_id_string, key_id.size());
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
@@ -314,7 +435,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message) {
|
||||
GenerateMacContext(message, &mac_deriv_message);
|
||||
GenerateEncryptContext(message, &enc_deriv_message);
|
||||
|
||||
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t) oec_session_id_);
|
||||
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
|
||||
OEMCryptoResult sts = OEMCrypto_GenerateDerivedKeys(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
|
||||
@@ -337,10 +458,9 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
|
||||
GenerateMacContext(message, &mac_deriv_message);
|
||||
GenerateEncryptContext(message, &enc_deriv_message);
|
||||
|
||||
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t) oec_session_id_);
|
||||
LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_);
|
||||
OEMCryptoResult sts = OEMCrypto_DeriveKeysFromSessionKey(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<const uint8_t*>(session_key.data()),
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(session_key.data()),
|
||||
session_key.size(),
|
||||
reinterpret_cast<const uint8_t*>(mac_deriv_message.data()),
|
||||
mac_deriv_message.size(),
|
||||
@@ -355,51 +475,43 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
std::string* signature,
|
||||
bool use_rsa) {
|
||||
LOGV("GenerateSignature: id=%ld", (uint32_t) oec_session_id_);
|
||||
if (!signature)
|
||||
return false;
|
||||
bool CryptoSession::GenerateSignature(const std::string& message, bool use_rsa,
|
||||
std::string* signature) {
|
||||
LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) return false;
|
||||
|
||||
size_t length = 0;
|
||||
OEMCryptoResult sts;
|
||||
OEMCryptoResult sts = OEMCrypto_SUCCESS;
|
||||
if (use_rsa) {
|
||||
sts = OEMCrypto_GenerateRSASignature(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(),
|
||||
NULL,
|
||||
&length);
|
||||
}
|
||||
else {
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(), NULL, &length);
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
length = kSignatureSize;
|
||||
// TODO(gmorgan,kqyang): Use OEMCrypto_GenerateSignature to determine
|
||||
// length after marvell fixes their implementation.
|
||||
/*
|
||||
sts = OEMCrypto_GenerateSignature(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(),
|
||||
NULL,
|
||||
&length);
|
||||
}
|
||||
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(), NULL, &length);
|
||||
*/
|
||||
}
|
||||
|
||||
signature->resize(length);
|
||||
|
||||
if (use_rsa) {
|
||||
sts = OEMCrypto_GenerateRSASignature(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<const uint8_t*>(message.data()),
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(),
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
|
||||
&length);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sts = OEMCrypto_GenerateSignature(
|
||||
oec_session_id_,
|
||||
reinterpret_cast<const uint8_t*>(message.data()),
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(),
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
|
||||
&length);
|
||||
@@ -410,29 +522,24 @@ bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(fredgc): remove in K, when L1 library reports correct length.
|
||||
// TODO(fredgc): b/8878371
|
||||
// remove in K, when L1 library reports correct length.
|
||||
signature->resize(length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::Decrypt(bool is_encrypted,
|
||||
bool is_secure,
|
||||
const uint8_t* encrypt_buffer,
|
||||
size_t encrypt_length,
|
||||
const std::vector<uint8_t>& iv,
|
||||
size_t block_offset,
|
||||
void* decrypt_buffer,
|
||||
size_t decrypt_buffer_offset,
|
||||
bool is_video) {
|
||||
CdmResponseType CryptoSession::Decrypt(
|
||||
bool is_encrypted, bool is_secure, const uint8_t* encrypt_buffer,
|
||||
size_t encrypt_length, const std::vector<uint8_t>& iv, size_t block_offset,
|
||||
void* decrypt_buffer, size_t decrypt_buffer_offset, bool is_video) {
|
||||
if (!is_destination_buffer_type_valid_) {
|
||||
if (!SetDestinationBufferType())
|
||||
return UNKNOWN_ERROR;
|
||||
if (!SetDestinationBufferType()) return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor;
|
||||
buffer_descriptor.type =
|
||||
is_secure ? destination_buffer_type_: OEMCrypto_BufferType_Clear;
|
||||
is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear;
|
||||
|
||||
switch (buffer_descriptor.type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
@@ -452,13 +559,8 @@ CdmResponseType CryptoSession::Decrypt(bool is_encrypted,
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCTR(
|
||||
oec_session_id_,
|
||||
encrypt_buffer,
|
||||
encrypt_length,
|
||||
is_encrypted,
|
||||
&iv[0],
|
||||
block_offset,
|
||||
&buffer_descriptor,
|
||||
oec_session_id_, encrypt_buffer, encrypt_length, is_encrypted, &iv[0],
|
||||
block_offset, &buffer_descriptor,
|
||||
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
@@ -474,29 +576,23 @@ bool CryptoSession::GenerateNonce(uint32_t* nonce) {
|
||||
}
|
||||
|
||||
LOGV("CryptoSession::GenerateNonce: Lock");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
return(OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce));
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
return (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce));
|
||||
}
|
||||
|
||||
bool CryptoSession::SetDestinationBufferType() {
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
|
||||
if (Properties::oem_crypto_use_secure_buffers()) {
|
||||
if (crypto_engine->GetSecurityLevel() == CryptoEngine::kSecurityLevelL1) {
|
||||
if (GetSecurityLevel() == CryptoSession::kSecurityLevelL1) {
|
||||
destination_buffer_type_ = OEMCrypto_BufferType_Secure;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
destination_buffer_type_ = OEMCrypto_BufferType_Clear;
|
||||
}
|
||||
}
|
||||
else if (Properties::oem_crypto_use_fifo()) {
|
||||
} else if (Properties::oem_crypto_use_fifo()) {
|
||||
destination_buffer_type_ = OEMCrypto_BufferType_Direct;
|
||||
}
|
||||
else if (Properties::oem_crypto_use_userspace_buffers()) {
|
||||
} else if (Properties::oem_crypto_use_userspace_buffers()) {
|
||||
destination_buffer_type_ = OEMCrypto_BufferType_Clear;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -508,14 +604,10 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
const std::string& signature,
|
||||
const std::string& nonce,
|
||||
const std::string& enc_rsa_key,
|
||||
size_t enc_rsa_key_length,
|
||||
const std::string& rsa_key_iv,
|
||||
std::string* wrapped_rsa_key) {
|
||||
LOGV("CryptoSession::RewrapDeviceRSAKey: Lock+++");
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
AutoLock auto_lock(crypto_engine->crypto_lock_);
|
||||
|
||||
LOGV("crypto session id=%ld", static_cast<uint32_t>(oec_session_id_));
|
||||
LOGD("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
|
||||
static_cast<uint32_t>(oec_session_id_));
|
||||
|
||||
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
const uint8_t* msg_rsa_key = NULL;
|
||||
@@ -524,21 +616,17 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
if (enc_rsa_key.size() >= MAC_KEY_SIZE && rsa_key_iv.size() >= KEY_IV_SIZE) {
|
||||
msg_rsa_key = signed_msg + GetOffset(message, enc_rsa_key);
|
||||
msg_rsa_key_iv = signed_msg + GetOffset(message, rsa_key_iv);
|
||||
msg_nonce = reinterpret_cast<const uint32_t*>(
|
||||
signed_msg + GetOffset(message, nonce));
|
||||
msg_nonce = reinterpret_cast<const uint32_t*>(signed_msg +
|
||||
GetOffset(message, nonce));
|
||||
}
|
||||
|
||||
// Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key
|
||||
// and 0 as wrapped_rsa_key_length.
|
||||
size_t wrapped_rsa_key_length = 0;
|
||||
OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey(
|
||||
oec_session_id_,
|
||||
signed_msg, message.size(),
|
||||
oec_session_id_, signed_msg, message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
|
||||
msg_nonce,
|
||||
msg_rsa_key, enc_rsa_key_length,
|
||||
msg_rsa_key_iv,
|
||||
NULL,
|
||||
msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv, NULL,
|
||||
&wrapped_rsa_key_length);
|
||||
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||
LOGE("OEMCrypto_RewrapDeviceRSAKey fails to get wrapped_rsa_key_length");
|
||||
@@ -547,20 +635,16 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
|
||||
wrapped_rsa_key->resize(wrapped_rsa_key_length);
|
||||
status = OEMCrypto_RewrapDeviceRSAKey(
|
||||
oec_session_id_,
|
||||
signed_msg,
|
||||
message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()),
|
||||
signature.size(),
|
||||
msg_nonce,
|
||||
msg_rsa_key,
|
||||
enc_rsa_key_length,
|
||||
msg_rsa_key_iv,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(wrapped_rsa_key->data())),
|
||||
oec_session_id_, signed_msg, message.size(),
|
||||
reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
|
||||
msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv,
|
||||
reinterpret_cast<uint8_t*>(&(*wrapped_rsa_key)[0]),
|
||||
&wrapped_rsa_key_length);
|
||||
|
||||
// TODO(fredgc): remove in K, when L1 library reports correct length.
|
||||
// TODO(fredgc): b/8878371
|
||||
// remove in K, when L1 library reports correct length.
|
||||
wrapped_rsa_key->resize(wrapped_rsa_key_length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status);
|
||||
return false;
|
||||
@@ -569,4 +653,15 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetRandom(uint8_t* random_data, size_t data_length) {
|
||||
OEMCryptoResult sts = OEMCrypto_GetRandom(random_data, data_length);
|
||||
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
LOGE("OEMCrypto_GetRandom fails with %d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
@@ -4,21 +4,12 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "device_files.pb.h"
|
||||
#include "file_store.h"
|
||||
#include "log.h"
|
||||
#include "openssl/sha.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// TODO(rfrias): Make this work for non-unix paths
|
||||
const char* DeviceFiles::kBasePath = "/data/mediadrm/IDM";
|
||||
const char* DeviceFiles::kPathDelimiter = "/";
|
||||
const char* DeviceFiles::kDeviceCertificateFileName = "cert.bin";
|
||||
const char* DeviceFiles::kLicenseFileNameExt = ".lic";
|
||||
#include "properties.h"
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
@@ -27,6 +18,23 @@ using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
|
||||
namespace {
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kLicenseFileNameExt[] = ".lic";
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
|
||||
bool DeviceFiles::Init(File* handle) {
|
||||
file_ = handle;
|
||||
if (handle == NULL) {
|
||||
LOGW("DeviceFiles::Init: Invalid file handle parameter");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
const std::string& wrapped_private_key) {
|
||||
// Fill in file information
|
||||
@@ -35,7 +43,7 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
file.set_type(video_widevine_client::sdk::File::DEVICE_CERTIFICATE);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
|
||||
DeviceCertificate *device_certificate = file.mutable_device_certificate();
|
||||
DeviceCertificate* device_certificate = file.mutable_device_certificate();
|
||||
device_certificate->set_certificate(certificate);
|
||||
device_certificate->set_wrapped_private_key(wrapped_private_key);
|
||||
|
||||
@@ -49,21 +57,20 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
return false;
|
||||
}
|
||||
|
||||
// File in hashed file data
|
||||
// Fill in hashed file data
|
||||
HashedFile hashed_file;
|
||||
hashed_file.set_file(serialized_string);
|
||||
hashed_file.set_hash(hash);
|
||||
|
||||
hashed_file.SerializeToString(&serialized_string);
|
||||
|
||||
return StoreFile(kDeviceCertificateFileName, serialized_string);
|
||||
return StoreFile(kCertificateFileName, serialized_string);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
std::string* wrapped_private_key) {
|
||||
|
||||
std::string serialized_hashed_file;
|
||||
if (!RetrieveFile(kDeviceCertificateFileName, &serialized_hashed_file))
|
||||
if (!RetrieveFile(kCertificateFileName, &serialized_hashed_file))
|
||||
return false;
|
||||
|
||||
HashedFile hashed_file;
|
||||
@@ -244,49 +251,80 @@ bool DeviceFiles::RetrieveLicense(
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
|
||||
return File::Remove(path);
|
||||
if (!file_) {
|
||||
LOGW("DeviceFiles::DeleteLicense: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(&path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(key_set_id);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Remove(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
std::string path = GetBasePath(kBasePath) + key_set_id + kLicenseFileNameExt;
|
||||
return File::Exists(path);
|
||||
if (!file_) {
|
||||
LOGW("DeviceFiles::LicenseExists: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(&path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(key_set_id);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash)
|
||||
return false;
|
||||
if (!hash) return false;
|
||||
|
||||
hash->resize(SHA256_DIGEST_LENGTH);
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
SHA256_Update(&sha256, data.data(), data.size());
|
||||
SHA256_Final(reinterpret_cast<unsigned char*>(const_cast<char*>(hash->data())), &sha256);
|
||||
SHA256_Final(reinterpret_cast<unsigned char*>(&(*hash)[0]), &sha256);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||
if (!name)
|
||||
if (!file_) {
|
||||
LOGW("DeviceFiles::StoreFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = GetBasePath(kBasePath);
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::StoreFile: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!File::IsDirectory(path)) {
|
||||
if (!File::CreateDirectory(path))
|
||||
return false;
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(&path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_->IsDirectory(path)) {
|
||||
if (!file_->CreateDirectory(path)) return false;
|
||||
}
|
||||
|
||||
path += name;
|
||||
|
||||
File file(path, File::kCreate | File::kTruncate | File::kBinary);
|
||||
if (file.IsBad()) {
|
||||
if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) {
|
||||
LOGW("DeviceFiles::StoreFile: File open failed: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t bytes = file.Write(data.data(), data.size());
|
||||
|
||||
file.Close();
|
||||
ssize_t bytes = file_->Write(data.data(), data.size());
|
||||
file_->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(data.size())) {
|
||||
LOGW("DeviceFiles::StoreFile: write failed: %d %d", data.size(), bytes);
|
||||
@@ -298,36 +336,51 @@ bool DeviceFiles::StoreFile(const char* name, const std::string& data) {
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
if (!data)
|
||||
if (!file_) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = GetBasePath(kBasePath) + name;
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!File::Exists(path)) {
|
||||
if (!data) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unspecified data parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(&path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
path += name;
|
||||
|
||||
if (!file_->Exists(path)) {
|
||||
LOGW("DeviceFiles::RetrieveFile: %s does not exist", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t bytes = File::FileSize(path);
|
||||
|
||||
ssize_t bytes = file_->FileSize(path);
|
||||
if (bytes <= 0) {
|
||||
LOGW("DeviceFiles::RetrieveFile: File size invalid: %d", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
File file(path, File::kReadOnly | File::kBinary);
|
||||
if (file.IsBad()) {
|
||||
if (!file_->Open(path, File::kReadOnly | File::kBinary)) {
|
||||
LOGW("DeviceFiles::RetrieveFile: File open failed: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
data->resize(bytes);
|
||||
|
||||
bytes = file.Read(reinterpret_cast<void*>(const_cast<char*>(data->data())),
|
||||
data->size());
|
||||
bytes = file_->Read(&(*data)[0], data->size());
|
||||
file_->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(data->size())) {
|
||||
LOGW("DeviceFiles::StoreFile: write failed: %d %d", data->size(), bytes);
|
||||
LOGW("DeviceFiles::RetrieveFile: read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -336,11 +389,12 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetBasePath(const char* dir) {
|
||||
// TODO(rfrias): Make this work for non-unix paths
|
||||
std::stringstream ss;
|
||||
ss << dir << getuid() << kPathDelimiter;
|
||||
return ss.str();
|
||||
std::string DeviceFiles::GetCertificateFileName() {
|
||||
return kCertificateFileName;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetLicenseFileNameExtension() {
|
||||
return kLicenseFileNameExt;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "crypto_engine.h"
|
||||
#include "crypto_key.h"
|
||||
#include "crypto_session.h"
|
||||
#include "log.h"
|
||||
#include "policy_engine.h"
|
||||
@@ -13,13 +13,13 @@
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
std::string kCompanyNameKey = "company_name";
|
||||
std::string kModelNameKey = "model_name";
|
||||
std::string kArchitectureNameKey = "architecture_name";
|
||||
std::string kDeviceNameKey = "device_name";
|
||||
std::string kProductNameKey = "product_name";
|
||||
std::string kBuildInfoKey = "build_info";
|
||||
std::string kDeviceIdKey = "device_id";
|
||||
std::string kCompanyNameKey = "company_name";
|
||||
std::string kModelNameKey = "model_name";
|
||||
std::string kArchitectureNameKey = "architecture_name";
|
||||
std::string kDeviceNameKey = "device_name";
|
||||
std::string kProductNameKey = "product_name";
|
||||
std::string kBuildInfoKey = "build_info";
|
||||
std::string kDeviceIdKey = "device_id";
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -30,13 +30,13 @@ using video_widevine_server::sdk::ClientIdentification_NameValue;
|
||||
using video_widevine_server::sdk::LicenseRequest;
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_ExistingLicense;
|
||||
using video_widevine_server::sdk::
|
||||
LicenseRequest_ContentIdentification_ExistingLicense;
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::License_KeyContainer;
|
||||
using video_widevine_server::sdk::LicenseError;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
|
||||
|
||||
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
std::vector<CryptoKey> key_array;
|
||||
|
||||
@@ -50,7 +50,11 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
key.set_key_id(license.key(i).id());
|
||||
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
|
||||
// the padding will always be 16 bytes.
|
||||
length = license.key(i).key().size() - 16;
|
||||
if (license.key(i).key().size() > 16) {
|
||||
length = license.key(i).key().size() - 16;
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
key.set_key_data(license.key(i).key().substr(0, length));
|
||||
key.set_key_data_iv(license.key(i).iv());
|
||||
if (license.key(i).has_key_control()) {
|
||||
@@ -62,7 +66,9 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
case License_KeyContainer::KEY_CONTROL:
|
||||
if (license.key(i).has_key_control()) {
|
||||
key.set_key_control(license.key(i).key_control().key_control_block());
|
||||
key.set_key_control_iv(license.key(i).key_control().iv());
|
||||
if (license.key(i).key_control().has_iv()) {
|
||||
key.set_key_control_iv(license.key(i).key_control().iv());
|
||||
}
|
||||
key_array.push_back(key);
|
||||
}
|
||||
break;
|
||||
@@ -75,17 +81,14 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
return key_array;
|
||||
}
|
||||
|
||||
CdmLicense::CdmLicense(): session_(NULL) {}
|
||||
CdmLicense::CdmLicense() : session_(NULL) {}
|
||||
|
||||
CdmLicense::~CdmLicense() {}
|
||||
|
||||
bool CdmLicense::Init(const std::string& token,
|
||||
CryptoSession* session,
|
||||
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine) {
|
||||
if (token.size() == 0)
|
||||
return false;
|
||||
if (session == NULL || !session->IsValid() || !session->IsOpen())
|
||||
return false;
|
||||
if (token.size() == 0) return false;
|
||||
if (session == NULL || !session->IsOpen()) return false;
|
||||
token_ = token;
|
||||
session_ = session;
|
||||
policy_engine_ = policy_engine;
|
||||
@@ -97,8 +100,7 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!session_ ||
|
||||
token_.empty()) {
|
||||
if (!session_ || token_.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (init_data.empty()) {
|
||||
@@ -109,10 +111,6 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
LOGE("CdmLicense::PrepareKeyRequest : No signed request provided.");
|
||||
return false;
|
||||
}
|
||||
if (!server_url) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest : No server url provided.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(gmorgan): Request ID owned by session?
|
||||
std::string request_id;
|
||||
@@ -135,38 +133,38 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
client_info->set_value(iter->second);
|
||||
}
|
||||
std::string value;
|
||||
if (Properties::GetCompanyName(value)) {
|
||||
if (Properties::GetCompanyName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kCompanyNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetModelName(value)) {
|
||||
if (Properties::GetModelName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kModelNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetArchitectureName(value)) {
|
||||
if (Properties::GetArchitectureName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kArchitectureNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetDeviceName(value)) {
|
||||
if (Properties::GetDeviceName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetProductName(value)) {
|
||||
if (Properties::GetProductName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kProductNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetBuildInfo(value)) {
|
||||
if (Properties::GetBuildInfo(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kBuildInfoKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
|
||||
if (CryptoEngine::GetInstance()->GetDeviceUniqueId(&value)) {
|
||||
if (session_->GetDeviceUniqueId(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(value);
|
||||
@@ -188,8 +186,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
cenc_content_id->set_license_type(video_widevine_server::sdk::STREAMING);
|
||||
break;
|
||||
default:
|
||||
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %u",
|
||||
(int)license_type);
|
||||
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
|
||||
license_type);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -220,8 +218,8 @@ bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
|
||||
|
||||
// Derive signing and encryption keys and construct signature.
|
||||
std::string license_request_signature;
|
||||
if (!session_->PrepareRequest(serialized_license_req,
|
||||
&license_request_signature, false)) {
|
||||
if (!session_->PrepareRequest(serialized_license_req, false,
|
||||
&license_request_signature)) {
|
||||
signed_request->clear();
|
||||
return false;
|
||||
}
|
||||
@@ -268,7 +266,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
||||
|
||||
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
|
||||
license_request.mutable_content_id()->mutable_license();
|
||||
current_license->mutable_license_id()->CopyFrom(license_id_);
|
||||
current_license->mutable_license_id()->CopyFrom(policy_engine_->license_id());
|
||||
|
||||
// Get/set the nonce. This value will be reflected in the Key Control Block
|
||||
// of the license response.
|
||||
@@ -318,23 +316,19 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
}
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(license_response))
|
||||
return KEY_ERROR;
|
||||
if (!signed_response.ParseFromString(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()) return KEY_ERROR;
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg()))
|
||||
return KEY_ERROR;
|
||||
if (!license.ParseFromString(signed_response.msg())) return KEY_ERROR;
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!signed_response.has_session_key())
|
||||
return KEY_ERROR;
|
||||
if (!signed_response.has_session_key()) return KEY_ERROR;
|
||||
|
||||
if (!session_->GenerateDerivedKeys(key_request_,
|
||||
signed_response.session_key()))
|
||||
@@ -344,8 +338,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
// Extract mac key
|
||||
std::string mac_key_iv;
|
||||
std::string mac_key;
|
||||
if (license.policy().can_renew())
|
||||
{
|
||||
if (license.policy().can_renew()) {
|
||||
for (int i = 0; i < license.key_size(); ++i) {
|
||||
if (license.key(i).type() == License_KeyContainer::SIGNING) {
|
||||
mac_key_iv.assign(license.key(i).iv());
|
||||
@@ -355,15 +348,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
}
|
||||
}
|
||||
|
||||
if (mac_key_iv.size() != KEY_IV_SIZE ||
|
||||
mac_key.size() != MAC_KEY_SIZE) {
|
||||
if (mac_key_iv.size() != KEY_IV_SIZE || mac_key.size() != MAC_KEY_SIZE) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
// License Id should not be empty for renewable license
|
||||
if (!license.has_id()) return KEY_ERROR;
|
||||
|
||||
license_id_.CopyFrom(license.id());
|
||||
}
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
@@ -376,17 +363,16 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
|
||||
// TODO(kqyang, jfore, gmorgan): change SetLicense function signature to
|
||||
// be able to return true/false to accept/reject the license. (Pending code
|
||||
// merge from Eureka)
|
||||
policy_engine_->SetLicense(license);
|
||||
|
||||
if (session_->LoadKeys(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
mac_key_iv,
|
||||
mac_key,
|
||||
key_array.size(),
|
||||
if (session_->LoadKeys(signed_response.msg(), signed_response.signature(),
|
||||
mac_key_iv, mac_key, key_array.size(),
|
||||
&key_array[0])) {
|
||||
return KEY_ADDED;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -429,41 +415,31 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (license.id().version() > license_id_.version()) {
|
||||
// This is the normal case.
|
||||
license_id_.CopyFrom(license.id());
|
||||
|
||||
if (is_renewal) {
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
license.policy().renewal_server_url().size() > 0) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
}
|
||||
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
if (!is_renewal)
|
||||
return KEY_ADDED;
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
|
||||
if (session_->RefreshKeys(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
key_array.size(),
|
||||
&key_array[0])) {
|
||||
return KEY_ADDED;
|
||||
}
|
||||
else {
|
||||
return KEY_ERROR;
|
||||
if (is_renewal) {
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
license.policy().renewal_server_url().size() > 0) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
}
|
||||
|
||||
// This isn't supposed to happen.
|
||||
// TODO(jfore): Handle wrap? We can miss responses and that should be
|
||||
// considered normal until retries are exhausted.
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: license version: expected > %u,"
|
||||
" actual = %u", license_id_.version(), license.id().version());
|
||||
return KEY_ERROR;
|
||||
// TODO(kqyang, jfore, gmorgan): change UpdateLicense function signature to
|
||||
// be able to return true/false to accept/reject the license. (Pending code
|
||||
// merge from Eureka)
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
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])) {
|
||||
return KEY_ADDED;
|
||||
} else {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
bool CdmLicense::RestoreOfflineLicense(
|
||||
|
||||
@@ -12,6 +12,7 @@ bool Properties::oem_crypto_use_secure_buffers_;
|
||||
bool Properties::oem_crypto_use_fifo_;
|
||||
bool Properties::oem_crypto_use_userspace_buffers_;
|
||||
bool Properties::use_certificates_as_identification_;
|
||||
bool Properties::extract_pssh_data_;
|
||||
|
||||
void Properties::Init() {
|
||||
begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived;
|
||||
@@ -21,6 +22,7 @@ void Properties::Init() {
|
||||
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
|
||||
use_certificates_as_identification_ =
|
||||
kPropertyUseCertificatesAsIdentification;
|
||||
extract_pssh_data_ = kExtractPsshData;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -10,45 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace {
|
||||
/*
|
||||
* Returns a 8-bit char that is mapped to the 6-bit base64 in_ch.
|
||||
*
|
||||
* Extracted from http://www.ietf.org/rfc/rfc3548.txt.
|
||||
*
|
||||
The "URL and Filename safe" Base 64 Alphabet
|
||||
|
||||
Value Encoding Value Encoding Value Encoding Value Encoding
|
||||
0 A 17 R 34 i 51 z
|
||||
1 B 18 S 35 j 52 0
|
||||
2 C 19 T 36 k 53 1
|
||||
3 D 20 U 37 l 54 2
|
||||
4 E 21 V 38 m 55 3
|
||||
5 F 22 W 39 n 56 4
|
||||
6 G 23 X 40 o 57 5
|
||||
7 H 24 Y 41 p 58 6
|
||||
8 I 25 Z 42 q 59 7
|
||||
9 J 26 a 43 r 60 8
|
||||
10 K 27 b 44 s 61 9
|
||||
11 L 28 c 45 t 62 - (minus)
|
||||
12 M 29 d 46 u 63 _
|
||||
13 N 30 e 47 v (underline)
|
||||
14 O 31 f 48 w
|
||||
15 P 32 g 49 x
|
||||
16 Q 33 h 50 y (pad) =
|
||||
*/
|
||||
char B64ToBin(char in_ch) {
|
||||
if (in_ch >= 'A' && in_ch <= 'Z') return in_ch - 'A';
|
||||
if (in_ch >= 'a' && in_ch <= 'z') return in_ch - 'a' + 26;
|
||||
if (in_ch >= '0' && in_ch <= '9') return in_ch - '0' + 52;
|
||||
if (in_ch == '-') return 62;
|
||||
if (in_ch == '_') return 63;
|
||||
|
||||
// arbitrary delimiter not in Base64 encoded alphabet, do not pick 0
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
#include "modp_b64w.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
@@ -104,181 +66,56 @@ std::string b2a_hex(const std::string& byte) {
|
||||
|
||||
// Filename-friendly base64 encoding (RFC4648), commonly referred as
|
||||
// Base64WebSafeEncode.
|
||||
// This is the encoding required by GooglePlay for certain
|
||||
// license server transactions. It is also used for logging
|
||||
// certain strings.
|
||||
// This is the encoding required by GooglePlay to interface with the
|
||||
// provisioning server's Apiary interface as well as for certain license server
|
||||
// transactions. It is also used for logging certain strings.
|
||||
// The difference between web safe encoding vs regular encoding is that
|
||||
// the web safe version replaces '+' with '-' and '/' with '_'.
|
||||
std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
||||
static const char kBase64Chars[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789-_";
|
||||
if (bin_input.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
int in_size = bin_input.size();
|
||||
int final_quantum_in_bytes = in_size % 3;
|
||||
int full_in_chunks = in_size / 3;
|
||||
int out_size = full_in_chunks * 4;
|
||||
if (final_quantum_in_bytes) out_size += 4;
|
||||
std::string b64_output(modp_b64w_encode_len(in_size), 0);
|
||||
|
||||
std::string b64_output(out_size, '\0');
|
||||
int in_index = 0;
|
||||
int out_index = 0;
|
||||
unsigned long buffer;
|
||||
unsigned char out_cc;
|
||||
static const unsigned long kInMask = 0xff;
|
||||
static const unsigned long kOutMask = 0x3f;
|
||||
|
||||
for (int i = 0; i < full_in_chunks; ++i) {
|
||||
// up to 3 bytes (0..255) in
|
||||
buffer = (bin_input.at(in_index) & kInMask);
|
||||
buffer <<= 8;
|
||||
buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask);
|
||||
buffer <<= 8;
|
||||
buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask);
|
||||
++in_index;
|
||||
|
||||
// up to 4 bytes (0..63) out
|
||||
out_cc = (buffer >> 18) & kOutMask;
|
||||
b64_output.at(out_index) = kBase64Chars[out_cc];
|
||||
if (++out_index >= out_size)
|
||||
break;
|
||||
out_cc = (buffer >> 12) & kOutMask;
|
||||
b64_output.at(out_index) = kBase64Chars[out_cc];
|
||||
if (++out_index >= out_size)
|
||||
break;
|
||||
out_cc = (buffer >> 6) & kOutMask;
|
||||
b64_output.at(out_index) = kBase64Chars[out_cc];
|
||||
if (++out_index >= out_size)
|
||||
break;
|
||||
out_cc = buffer & kOutMask;
|
||||
b64_output.at(out_index) = kBase64Chars[out_cc];
|
||||
++out_index;
|
||||
int out_size = modp_b64w_encode(&b64_output[0],
|
||||
reinterpret_cast<const char*>(&bin_input[0]),
|
||||
in_size);
|
||||
if (out_size == -1) {
|
||||
LOGE("Base64SafeEncode failed");
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (final_quantum_in_bytes) {
|
||||
switch(final_quantum_in_bytes) {
|
||||
case 1: {
|
||||
// reads 24-bits data, which is made up of one 8-bits char
|
||||
buffer = (bin_input.at(in_index++) & kInMask);
|
||||
buffer <<= 16;
|
||||
b64_output.resize(out_size);
|
||||
return b64_output;
|
||||
}
|
||||
|
||||
// writes two 6-bits chars followed by two '=' padding char
|
||||
out_cc = (buffer >> 18) & kOutMask;
|
||||
b64_output.at(out_index++) = kBase64Chars[out_cc];
|
||||
out_cc = (buffer >> 12) & kOutMask;
|
||||
b64_output.at(out_index++) = kBase64Chars[out_cc];
|
||||
b64_output.at(out_index++) = '=';
|
||||
b64_output.at(out_index) = '=';
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// reads 24-bits data, which is made up of two 8-bits chars
|
||||
buffer = (bin_input.at(in_index++) & kInMask);
|
||||
buffer <<= 8;
|
||||
buffer |= (bin_input.at(in_index++) & kInMask);
|
||||
buffer <<= 8;
|
||||
|
||||
// writes three 6-bits chars followed by one '=' padding char
|
||||
out_cc = (buffer >> 18) & kOutMask;
|
||||
b64_output.at(out_index++) = kBase64Chars[out_cc];
|
||||
out_cc = (buffer >> 12) & kOutMask;
|
||||
b64_output.at(out_index++) = kBase64Chars[out_cc];
|
||||
out_cc = (buffer >> 6) & kOutMask;
|
||||
b64_output.at(out_index++) = kBase64Chars[out_cc];
|
||||
b64_output.at(out_index) = '=';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input) {
|
||||
std::string b64_output = Base64SafeEncode(bin_input);
|
||||
// Output size: ceiling [ bin_input.size() * 4 / 3 ].
|
||||
b64_output.resize((bin_input.size() * 4 + 2) / 3);
|
||||
return b64_output;
|
||||
}
|
||||
|
||||
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
|
||||
// as Base64WebSafeDecode.
|
||||
// This is the encoding required by GooglePlay for certain
|
||||
// license server transactions. It is also used for logging
|
||||
// certain strings.
|
||||
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
||||
if (b64_input.empty()) {
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
int in_size = b64_input.size();
|
||||
int out_size = in_size;
|
||||
std::vector<uint8_t> bin_output(out_size, '\0');
|
||||
int in_index = 0;
|
||||
int out_index = 0;
|
||||
unsigned long buffer;
|
||||
unsigned char out_cc;
|
||||
static const unsigned long kOutMask = 0xff;
|
||||
|
||||
int counter = 0;
|
||||
size_t delimiter_pos = b64_input.rfind('=');
|
||||
if (delimiter_pos != std::string::npos) {
|
||||
// Special case for partial last quantum indicated by '='
|
||||
// at the end of encoded input.
|
||||
counter = 1;
|
||||
}
|
||||
for (; counter < (in_size / 4); ++counter) {
|
||||
// up to 4 bytes (0..63) in
|
||||
buffer = B64ToBin(b64_input.at(in_index));
|
||||
buffer <<= 6;
|
||||
buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index));
|
||||
buffer <<= 6;
|
||||
buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index));
|
||||
buffer <<= 6;
|
||||
buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index));
|
||||
++in_index;
|
||||
// up to 3 bytes (0..255) out
|
||||
out_cc = (buffer >> 16) & kOutMask;
|
||||
bin_output.at(out_index) = out_cc;
|
||||
if (++out_index >= out_size)
|
||||
break;
|
||||
out_cc = (buffer >> 8) & kOutMask;
|
||||
bin_output.at(out_index) = out_cc;
|
||||
if (++out_index >= out_size)
|
||||
break;
|
||||
out_cc = buffer & kOutMask;
|
||||
bin_output.at(out_index) = out_cc;
|
||||
++out_index;
|
||||
std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0);
|
||||
int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]),
|
||||
b64_input.data(),
|
||||
in_size);
|
||||
if (out_size == -1) {
|
||||
LOGE("Base64SafeDecode failed");
|
||||
return std::vector<uint8_t>(0);
|
||||
}
|
||||
|
||||
if (delimiter_pos != std::string::npos) {
|
||||
// it is either 2 chars plus 2 '=' or 3 chars plus one '='
|
||||
buffer = B64ToBin(b64_input.at(in_index++));
|
||||
buffer <<= 6;
|
||||
buffer |= B64ToBin(b64_input.at(in_index++));
|
||||
buffer <<= 6;
|
||||
char special_char = b64_input.at(in_index++);
|
||||
if ('=' == special_char) {
|
||||
// we have 2 chars and 2 '='
|
||||
buffer <<= 6;
|
||||
out_cc = (buffer >> 16) & kOutMask;
|
||||
bin_output.at(out_index++) = out_cc;
|
||||
out_cc = (buffer >> 8) & kOutMask;
|
||||
bin_output.at(out_index) = out_cc;
|
||||
} else {
|
||||
// we have 3 chars and 1 '='
|
||||
buffer |= B64ToBin(special_char);
|
||||
buffer <<= 6;
|
||||
buffer |= B64ToBin(b64_input.at(in_index));
|
||||
out_cc = (buffer >> 16) & kOutMask;
|
||||
bin_output.at(out_index++) = out_cc;
|
||||
out_cc = (buffer >> 8) & kOutMask;
|
||||
bin_output.at(out_index++) = out_cc;
|
||||
out_cc = buffer & kOutMask;
|
||||
bin_output.at(out_index) = out_cc;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust vector to reflect true size
|
||||
bin_output.resize(out_index);
|
||||
bin_output.resize(out_size);
|
||||
return bin_output;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user