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:
Jeff Tinker
2013-04-22 20:05:55 -07:00
parent 3a28eeeb68
commit 958bbe6d05
30 changed files with 1497 additions and 290 deletions

View File

@@ -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;
}