Certificate provisioning verification
bug: 8620943 This is a merge of changes made to the Widevine CDM repository during certificate provisioning verification. The following changes are included: Fixes for certificate based licensing https://widevine-internal-review.googlesource.com/#/c/5162/ Base64 encode and decode now handles non-multiple of 24-bits input https://widevine-internal-review.googlesource.com/#/c/4981/ Fixed issues with device provisioning response handling https://widevine-internal-review.googlesource.com/#/c/5153/ Persistent storage to support device certificates https://widevine-internal-review.googlesource.com/#/c/5161/ Enable loading of certificates https://widevine-internal-review.googlesource.com/#/c/5172/ Provide license server url https://widevine-internal-review.googlesource.com/#/c/5173/ Change-Id: I0c032c1ae0055dcc1a7a77ad4b0ea0898030dc7d
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include "buffer_reader.h"
|
||||
#include "cdm_session.h"
|
||||
#include "crypto_engine.h"
|
||||
#include "device_files.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
@@ -29,7 +30,7 @@ using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
|
||||
typedef std::map<CdmSessionId,CdmSession*>::const_iterator CdmSessionIter;
|
||||
|
||||
CdmEngine::CdmEngine() {
|
||||
CdmEngine::CdmEngine() : provisioning_session_(NULL) {
|
||||
Properties::Init();
|
||||
}
|
||||
|
||||
@@ -105,7 +106,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
const CdmInitData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request) {
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
@@ -140,7 +142,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
CdmResponseType sts = iter->second->GenerateKeyRequest(extracted_pssh,
|
||||
license_type,
|
||||
app_parameters,
|
||||
key_request);
|
||||
key_request,
|
||||
server_url);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d",
|
||||
@@ -156,9 +159,6 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
|
||||
CdmResponseType CdmEngine::AddKey(
|
||||
const CdmSessionId& session_id,
|
||||
bool is_key_system_init_data_present,
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmInitData& init_data,
|
||||
const CdmKeyResponse& key_data) {
|
||||
LOGI("CdmEngine::AddKey");
|
||||
|
||||
@@ -168,14 +168,6 @@ CdmResponseType CdmEngine::AddKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (is_key_system_init_data_present) {
|
||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
||||
}
|
||||
|
||||
if (is_key_system_init_data_present) {
|
||||
// TODO(edwinwong, rfrias): validate init_data has not changed
|
||||
}
|
||||
|
||||
if (key_data.empty()) {
|
||||
LOGE("CdmEngine::AddKey: no key_data");
|
||||
return KEY_ERROR;
|
||||
@@ -218,10 +210,8 @@ CdmResponseType CdmEngine::CancelKeyRequest(
|
||||
|
||||
CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
const CdmSessionId& session_id,
|
||||
bool is_key_system_init_data_present,
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmInitData& init_data,
|
||||
CdmKeyMessage* key_request) {
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
LOGI("CdmEngine::GenerateRenewalRequest");
|
||||
|
||||
CdmSessionIter iter = sessions_.find(session_id);
|
||||
@@ -230,14 +220,6 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (is_key_system_init_data_present) {
|
||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
||||
}
|
||||
|
||||
if (is_key_system_init_data_present) {
|
||||
// TODO(edwinwong, rfrias): validate init_data has not changed
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination provided");
|
||||
return KEY_ERROR;
|
||||
@@ -245,7 +227,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
|
||||
key_request->clear();
|
||||
|
||||
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
|
||||
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request,
|
||||
server_url);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: key request generation failed, sts=%d",
|
||||
@@ -258,9 +241,6 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
|
||||
CdmResponseType CdmEngine::RenewKey(
|
||||
const CdmSessionId& session_id,
|
||||
bool is_key_system_init_data_present,
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmInitData& init_data,
|
||||
const CdmKeyResponse& key_data) {
|
||||
LOGI("CdmEngine::RenewKey");
|
||||
|
||||
@@ -270,14 +250,6 @@ CdmResponseType CdmEngine::RenewKey(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (is_key_system_init_data_present) {
|
||||
// TODO(edwinwong, rfrias): validate key_system has not changed
|
||||
}
|
||||
|
||||
if (is_key_system_init_data_present) {
|
||||
// TODO(edwinwong, rfrias): validate init_data has not changed
|
||||
}
|
||||
|
||||
if (key_data.empty()) {
|
||||
LOGE("CdmEngine::RenewKey: no key_data");
|
||||
return KEY_ERROR;
|
||||
@@ -357,15 +329,9 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
|
||||
return iter->second->QueryKeyControlInfo(key_info);
|
||||
}
|
||||
|
||||
void CdmEngine::CleanupProvisioingSessions(
|
||||
CdmSession* cdm_session,
|
||||
CryptoEngine* crypto_engine,
|
||||
const CdmSessionId& cdm_session_id) {
|
||||
if (NULL == cdm_session) return;
|
||||
|
||||
cdm_session->DestroySession();
|
||||
if (crypto_engine) crypto_engine->DestroySession(cdm_session_id);
|
||||
delete cdm_session;
|
||||
void CdmEngine::CleanupProvisioningSession(const CdmSessionId& cdm_session_id) {
|
||||
CloseSession(cdm_session_id);
|
||||
provisioning_session_ = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -391,21 +357,16 @@ void CdmEngine::ComposeJsonRequest(
|
||||
// performs base64 encoding for message
|
||||
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
||||
std::string message_b64 = Base64SafeEncode(message_vector);
|
||||
LOGD("b64 serialized req:\r\n%s", message_b64.data());
|
||||
|
||||
// performs base64 encoding for signature
|
||||
std::vector<uint8_t> signature_vector(signature.begin(), signature.end());
|
||||
std::string signature_b64 = Base64SafeEncode(signature_vector);
|
||||
LOGD("b64 signature:\r\n%s", signature_b64.data());
|
||||
|
||||
// TODO(edwinwong): write a function to escape JSON output
|
||||
request->assign("{'signedRequest':{'message':'");
|
||||
request->append(message_b64);
|
||||
request->append("','signature':'");
|
||||
request->append(signature_b64);
|
||||
request->append("'}}");
|
||||
LOGD("json str:\r\n%s", request->c_str());
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -419,7 +380,12 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!request || !default_url) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
||||
LOGE("GetProvisioningRequest: invalid input parameters");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (provisioning_session_) {
|
||||
LOGE("GetProvisioningRequest: duplicate provisioning request?");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -430,19 +396,19 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
//
|
||||
CdmSession* cdm_session = new CdmSession();
|
||||
if (!cdm_session) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: fails to create a cdm session");
|
||||
LOGE("GetProvisioningRequest: fails to create a cdm session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (cdm_session->session_id().empty()) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: fails to generate session ID");
|
||||
LOGE("GetProvisioningRequest: fails to generate session ID");
|
||||
delete cdm_session;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (!crypto_engine) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: fails to create a crypto engine");
|
||||
LOGE("GetProvisioningRequest: fails to create a crypto engine");
|
||||
delete cdm_session;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -450,11 +416,13 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
CdmSessionId cdm_session_id = cdm_session->session_id();
|
||||
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
|
||||
if (!crypto_session) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: fails to create a crypto session");
|
||||
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
||||
delete cdm_session;
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
provisioning_session_ = cdm_session;
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// Prepares device provisioning request.
|
||||
@@ -464,20 +432,24 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
client_id->set_type(ClientIdentification::KEYBOX);
|
||||
std::string token;
|
||||
if (!crypto_engine->GetToken(&token)) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: fails to get token");
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
LOGE("GetProvisioningRequest: fails to get token");
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
client_id->set_token(token);
|
||||
|
||||
uint32_t nonce;
|
||||
if (!crypto_session->GenerateNonce(&nonce)) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: fails to generate a nonce");
|
||||
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
||||
crypto_engine->DestroySession(cdm_session_id);
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
provisioning_request.set_nonce(UintToString(nonce));
|
||||
|
||||
// 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);
|
||||
|
||||
// Serializes the provisioning request.
|
||||
std::string serialized_request;
|
||||
@@ -487,27 +459,23 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
std::string request_signature;
|
||||
if (!crypto_session->PrepareRequest(serialized_request, &request_signature)) {
|
||||
request->clear();
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (request_signature.empty()) {
|
||||
request->clear();
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// converts request into JSON string
|
||||
ComposeJsonRequest(serialized_request, request_signature, request);
|
||||
|
||||
// TODO(edwinwong): returns default provisioning server url
|
||||
default_url->clear();
|
||||
static const std::string kDefaultProvisioningServerUrl =
|
||||
"http://www-googleapis-test.sandbox.google.com/certificateprovisioning/v1/devicecertificates/create";
|
||||
default_url->assign(kDefaultProvisioningServerUrl);
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// Closes the cdm session.
|
||||
//
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -537,12 +505,6 @@ bool CdmEngine::ParseJsonResponse(
|
||||
|
||||
size_t b64_string_size = end - start - start_substr.length();
|
||||
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
|
||||
|
||||
// Due to the size of the message, debug string cannot dump out the
|
||||
// entire string. Dump the beginning and end to verify instead.
|
||||
LOGD("size=%u, b64_string start=%s, end=%s", b64_string.length(),
|
||||
b64_string.substr(0, 16).c_str(),
|
||||
b64_string.substr(b64_string_size - 16).c_str());
|
||||
}
|
||||
|
||||
// Decodes base64 substring and returns it in *result
|
||||
@@ -587,32 +549,23 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// First creates a cdm session, then creates a crypto session.
|
||||
// Creates a crypto session using provisioning_session_.
|
||||
//
|
||||
CdmSession* cdm_session = new CdmSession();
|
||||
if (!cdm_session) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to create a cdm session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (cdm_session->session_id().empty()) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to generate session ID");
|
||||
delete cdm_session;
|
||||
if (!provisioning_session_) {
|
||||
LOGE("HandleProvisioningResponse: invalid provisioning session");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
|
||||
if (!crypto_engine) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to create a crypto engine");
|
||||
delete cdm_session;
|
||||
LOGE("HandleProvisioningResponse: fails to create a crypto engine");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmSessionId cdm_session_id = cdm_session->session_id();
|
||||
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
|
||||
CdmSessionId cdm_session_id = provisioning_session_->session_id();
|
||||
CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id);
|
||||
if (!crypto_session) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to create a crypto session");
|
||||
delete cdm_session;
|
||||
LOGE("HandleProvisioningResponse: fails to find %s", cdm_session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -621,36 +574,50 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
// the provisioing request's input). Validate provisioning response and
|
||||
// stores private device RSA key and certificate.
|
||||
ProvisioningResponse provisioning_response;
|
||||
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: fails to parse signed message");
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
const std::string& enc_rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
||||
uint32_t nonce = strtoul(provisioning_response.nonce().data(), NULL, 10);
|
||||
std::vector<uint8_t> wrapped_rsa_key;
|
||||
size_t wrapped_rsa_key_length = 0;
|
||||
if (!crypto_session->RewrapDeviceRSAKey(response,
|
||||
&nonce,
|
||||
reinterpret_cast<const uint8_t*>(enc_rsa_key.data()),
|
||||
enc_rsa_key.length(),
|
||||
reinterpret_cast<const uint8_t*>(enc_rsa_key_iv.data()),
|
||||
&wrapped_rsa_key[0],
|
||||
&wrapped_rsa_key_length)) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
LOGE("HandleProvisioningResponse: fails to parse signed message");
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!provisioning_response.has_device_rsa_key()) {
|
||||
LOGE("HandleProvisioningResponse: invalid response - key not found");
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
||||
const std::string& nonce = provisioning_response.nonce();
|
||||
|
||||
const int kRsaKeySize = 256;
|
||||
size_t wrapped_rsa_key_length = kRsaKeySize + enc_rsa_key.length();
|
||||
std::vector<uint8_t> wrapped_rsa_key;
|
||||
wrapped_rsa_key.resize(wrapped_rsa_key_length);
|
||||
|
||||
if (!crypto_session->RewrapDeviceRSAKey(signed_message,
|
||||
signature,
|
||||
nonce.data(),
|
||||
enc_rsa_key,
|
||||
enc_rsa_key.size(),
|
||||
rsa_key_iv,
|
||||
&wrapped_rsa_key[0],
|
||||
&wrapped_rsa_key_length)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
// TODO(edwinwong): stores private device RSA key and cert
|
||||
const std::string& device_certificate = provisioning_response.device_certificate();
|
||||
std::string the_wrapped_rsa_key(wrapped_rsa_key.begin(), wrapped_rsa_key.end());
|
||||
DeviceFiles::StoreCertificate(device_certificate, the_wrapped_rsa_key);
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// Closes the cdm session.
|
||||
//
|
||||
CleanupProvisioingSessions(cdm_session, crypto_engine, cdm_session_id);
|
||||
CleanupProvisioningSession(cdm_session_id);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user