am 7aa99d4a: Squashed commit of 3 CLs related to provisioning retries
* commit '7aa99d4a36434af618db6240d4a74ea1ef641802': Squashed commit of 3 CLs related to provisioning retries
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
#ifndef CDM_BASE_CDM_ENGINE_H_
|
#ifndef CDM_BASE_CDM_ENGINE_H_
|
||||||
#define CDM_BASE_CDM_ENGINE_H_
|
#define CDM_BASE_CDM_ENGINE_H_
|
||||||
|
|
||||||
|
#include "crypto_session.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "wv_cdm_types.h"
|
#include "wv_cdm_types.h"
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ class CdmEngine : public TimerHandler {
|
|||||||
// private methods
|
// private methods
|
||||||
// Cancel all sessions
|
// Cancel all sessions
|
||||||
bool CancelSessions();
|
bool CancelSessions();
|
||||||
void CleanupProvisioningSession(const CdmSessionId& cdm_session_id);
|
void CleanupProvisioningSession();
|
||||||
void ComposeJsonRequestAsQueryString(const std::string& message,
|
void ComposeJsonRequestAsQueryString(const std::string& message,
|
||||||
CdmProvisioningRequest* request);
|
CdmProvisioningRequest* request);
|
||||||
|
|
||||||
|
|||||||
@@ -327,9 +327,29 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
|
|||||||
return iter->second->QueryKeyControlInfo(key_info);
|
return iter->second->QueryKeyControlInfo(key_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CdmEngine::CleanupProvisioningSession(const CdmSessionId& cdm_session_id) {
|
/*
|
||||||
CloseSession(cdm_session_id);
|
* The certificate provisioning process creates a cdm and a crypto session.
|
||||||
provisioning_session_ = NULL;
|
* 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -382,8 +402,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
default_url->assign(kDefaultProvisioningServerUrl);
|
default_url->assign(kDefaultProvisioningServerUrl);
|
||||||
|
|
||||||
if (provisioning_session_) {
|
if (provisioning_session_) {
|
||||||
LOGE("GetProvisioningRequest: duplicate provisioning request?");
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -418,7 +437,10 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(edwinwong): replace this cdm session pointer with crypto session
|
||||||
|
// pointer if feasible
|
||||||
provisioning_session_ = cdm_session;
|
provisioning_session_ = cdm_session;
|
||||||
|
LOGV("provisioning session id=%s", cdm_session_id.c_str());
|
||||||
|
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
@@ -430,7 +452,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
std::string token;
|
std::string token;
|
||||||
if (!crypto_engine->GetToken(&token)) {
|
if (!crypto_engine->GetToken(&token)) {
|
||||||
LOGE("GetProvisioningRequest: fails to get token");
|
LOGE("GetProvisioningRequest: fails to get token");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
client_id->set_token(token);
|
client_id->set_token(token);
|
||||||
@@ -438,8 +460,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
uint32_t nonce;
|
uint32_t nonce;
|
||||||
if (!crypto_session->GenerateNonce(&nonce)) {
|
if (!crypto_session->GenerateNonce(&nonce)) {
|
||||||
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
||||||
crypto_engine->DestroySession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,12 +477,12 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
if (!crypto_session->PrepareRequest(serialized_message,
|
if (!crypto_session->PrepareRequest(serialized_message,
|
||||||
&request_signature, true)) {
|
&request_signature, true)) {
|
||||||
request->clear();
|
request->clear();
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
if (request_signature.empty()) {
|
if (request_signature.empty()) {
|
||||||
request->clear();
|
request->clear();
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,14 +589,14 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
SignedProvisioningMessage signed_response;
|
SignedProvisioningMessage signed_response;
|
||||||
if (!signed_response.ParseFromString(serialized_signed_response)) {
|
if (!signed_response.ParseFromString(serialized_signed_response)) {
|
||||||
LOGE("Fails to parse signed serialized response");
|
LOGE("Fails to parse signed serialized response");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!signed_response.has_signature() || !signed_response.has_message()) {
|
if (!signed_response.has_signature() || !signed_response.has_message()) {
|
||||||
LOGE("Invalid response - signature or message not found");
|
LOGE("Invalid response - signature or message not found");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& signed_message = signed_response.message();
|
const std::string& signed_message = signed_response.message();
|
||||||
@@ -583,13 +604,13 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
|
|
||||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||||
LOGE("Fails to parse signed message");
|
LOGE("Fails to parse signed message");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provisioning_response.has_device_rsa_key()) {
|
if (!provisioning_response.has_device_rsa_key()) {
|
||||||
LOGE("Invalid response - key not found");
|
LOGE("Invalid response - key not found");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,7 +628,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
rsa_key_iv,
|
rsa_key_iv,
|
||||||
&wrapped_rsa_key)) {
|
&wrapped_rsa_key)) {
|
||||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,10 +637,9 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
|
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Closes the cdm session.
|
// Deletes cdm and crypto sessions created for provisioning.
|
||||||
//
|
//
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession();
|
||||||
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,81 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
EXPECT_NE(0, static_cast<int>(server_url.size()));
|
EXPECT_NE(0, static_cast<int>(server_url.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DumpResponse(const std::string& description,
|
||||||
|
const std::string& response) {
|
||||||
|
if (description.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOGD("%s (%d bytes):", description.c_str(), response.size());
|
||||||
|
|
||||||
|
size_t remaining = response.size();
|
||||||
|
size_t portion = 0;
|
||||||
|
size_t start = 0;
|
||||||
|
while (remaining > 0) {
|
||||||
|
// LOGX may not get to empty its buffer if it is too large,
|
||||||
|
// pick an arbitrary small size to be safe
|
||||||
|
portion = (remaining < 512) ? remaining : 512;
|
||||||
|
LOGD("%s", response.substr(start, portion).c_str());
|
||||||
|
start += portion;
|
||||||
|
remaining -= portion;
|
||||||
|
}
|
||||||
|
LOGD("total bytes dumped(%d)", start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// concatinates all chunks into one blob
|
||||||
|
// TODO (edwinwong) move this function to url_request class as GetMessageBody
|
||||||
|
void ConcatenateChunkedResponse(const std::string http_response,
|
||||||
|
std::string* message_body) {
|
||||||
|
if (http_response.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
message_body->clear();
|
||||||
|
const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n";
|
||||||
|
size_t chunked_tag_pos = http_response.find(kChunkedTag);
|
||||||
|
if (std::string::npos != chunked_tag_pos) {
|
||||||
|
// processes chunked encoding
|
||||||
|
size_t chunk_size = 0;
|
||||||
|
size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size();
|
||||||
|
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
|
||||||
|
if (chunk_size > http_response.size()) {
|
||||||
|
// precaution, in case we misread chunk size
|
||||||
|
LOGE("invalid chunk size %u", chunk_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* searches for chunks
|
||||||
|
*
|
||||||
|
* header
|
||||||
|
* chunk size\r\n <-- chunk_size_pos @ beginning of chunk size
|
||||||
|
* chunk data\r\n <-- chunk_pos @ beginning of chunk data
|
||||||
|
* chunk size\r\n
|
||||||
|
* chunk data\r\n
|
||||||
|
* 0\r\n
|
||||||
|
*/
|
||||||
|
const std::string kCrLf = "\r\n";
|
||||||
|
size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos) +
|
||||||
|
kCrLf.size();
|
||||||
|
message_body->assign(&http_response[0], chunk_size_pos);
|
||||||
|
while ((chunk_size > 0) && (std::string::npos != chunk_pos)) {
|
||||||
|
message_body->append(&http_response[chunk_pos], chunk_size);
|
||||||
|
|
||||||
|
// searches for next chunk
|
||||||
|
chunk_size_pos = chunk_pos + chunk_size + kCrLf.size();
|
||||||
|
sscanf(&http_response[chunk_size_pos], "%x", &chunk_size);
|
||||||
|
if (chunk_size > http_response.size()) {
|
||||||
|
// precaution, in case we misread chunk size
|
||||||
|
LOGE("invalid chunk size %u", chunk_size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chunk_pos = http_response.find(kCrLf, chunk_size_pos) + kCrLf.size();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// response is not chunked encoded
|
||||||
|
message_body->assign(http_response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// posts a request and extracts the drm message from the response
|
// posts a request and extracts the drm message from the response
|
||||||
std::string GetKeyRequestResponse(const std::string& server_url,
|
std::string GetKeyRequestResponse(const std::string& server_url,
|
||||||
const std::string& client_auth,
|
const std::string& client_auth,
|
||||||
@@ -88,14 +163,17 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
url_request.PostRequest(key_msg_);
|
url_request.PostRequest(key_msg_);
|
||||||
std::string response;
|
std::string http_response;
|
||||||
int resp_bytes = url_request.GetResponse(response);
|
std::string message_body;
|
||||||
LOGD("response:\r\n%s", response.c_str());
|
int resp_bytes = url_request.GetResponse(http_response);
|
||||||
|
if (resp_bytes) {
|
||||||
|
ConcatenateChunkedResponse(http_response, &message_body);
|
||||||
|
}
|
||||||
LOGD("end %d bytes response dump", resp_bytes);
|
LOGD("end %d bytes response dump", resp_bytes);
|
||||||
|
|
||||||
// Youtube server returns 400 for invalid message while play server returns
|
// Youtube server returns 400 for invalid message while play server returns
|
||||||
// 500, so just test inequity here for invalid message
|
// 500, so just test inequity here for invalid message
|
||||||
int status_code = url_request.GetStatusCode(response);
|
int status_code = url_request.GetStatusCode(message_body);
|
||||||
if (expected_response == 200) {
|
if (expected_response == 200) {
|
||||||
EXPECT_EQ(200, status_code);
|
EXPECT_EQ(200, status_code);
|
||||||
} else {
|
} else {
|
||||||
@@ -105,7 +183,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
std::string drm_msg;
|
std::string drm_msg;
|
||||||
if (200 == status_code) {
|
if (200 == status_code) {
|
||||||
LicenseRequest lic_request;
|
LicenseRequest lic_request;
|
||||||
lic_request.GetDrmMessage(response, drm_msg);
|
lic_request.GetDrmMessage(message_body, drm_msg);
|
||||||
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
|
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
|
||||||
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
||||||
drm_msg.size()).c_str());
|
drm_msg.size()).c_str());
|
||||||
@@ -123,24 +201,23 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
url_request.PostCertRequestInQueryString(key_msg_);
|
url_request.PostCertRequestInQueryString(key_msg_);
|
||||||
std::string response;
|
std::string http_response;
|
||||||
int resp_bytes = url_request.GetResponse(response);
|
std::string message_body;
|
||||||
|
int resp_bytes = url_request.GetResponse(http_response);
|
||||||
if (resp_bytes) {
|
if (resp_bytes) {
|
||||||
LOGD("size=%u, response start:\t\rn%s", response.size(),
|
ConcatenateChunkedResponse(http_response, &message_body);
|
||||||
response.substr(0, 1024).c_str());
|
|
||||||
LOGD("end:\r\n%s", response.substr(response.size() - 256).c_str());
|
|
||||||
}
|
}
|
||||||
LOGD("end %d bytes response dump", resp_bytes);
|
LOGD("end %d bytes response dump", resp_bytes);
|
||||||
|
|
||||||
// Youtube server returns 400 for invalid message while play server returns
|
// Youtube server returns 400 for invalid message while play server returns
|
||||||
// 500, so just test inequity here for invalid message
|
// 500, so just test inequity here for invalid message
|
||||||
int status_code = url_request.GetStatusCode(response);
|
int status_code = url_request.GetStatusCode(message_body);
|
||||||
if (expected_response == 200) {
|
if (expected_response == 200) {
|
||||||
EXPECT_EQ(200, status_code);
|
EXPECT_EQ(200, status_code);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_NE(200, status_code);
|
EXPECT_NE(200, status_code);
|
||||||
}
|
}
|
||||||
return response;
|
return message_body;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifyKeyRequestResponse(const std::string& server_url,
|
void VerifyKeyRequestResponse(const std::string& server_url,
|
||||||
@@ -170,12 +247,28 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
|||||||
decryptor_.OpenSession(g_key_system, &session_id_);
|
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||||
std::string provisioning_server_url = "";
|
std::string provisioning_server_url = "";
|
||||||
|
|
||||||
decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url);
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url));
|
||||||
EXPECT_STREQ(provisioning_server_url.data(), kDefaultProvisioningServerUrl.data());
|
EXPECT_STREQ(provisioning_server_url.data(), kDefaultProvisioningServerUrl.data());
|
||||||
|
|
||||||
std::string response = GetCertRequestResponse(kDefaultProvisioningServerUrl, 200);
|
std::string response = GetCertRequestResponse(kDefaultProvisioningServerUrl, 200);
|
||||||
if (!response.empty())
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
decryptor_.HandleProvisioningResponse(response);
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
||||||
|
decryptor_.CloseSession(session_id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
||||||
|
decryptor_.OpenSession(g_key_system, &session_id_);
|
||||||
|
std::string provisioning_server_url = "";
|
||||||
|
|
||||||
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url));
|
||||||
|
EXPECT_STREQ(provisioning_server_url.data(), kDefaultProvisioningServerUrl.data());
|
||||||
|
|
||||||
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.GetProvisioningRequest(&key_msg_, &provisioning_server_url));
|
||||||
|
EXPECT_STREQ(provisioning_server_url.data(), kDefaultProvisioningServerUrl.data());
|
||||||
|
|
||||||
|
std::string response = GetCertRequestResponse(kDefaultProvisioningServerUrl, 200);
|
||||||
|
EXPECT_NE(0, static_cast<int>(response.size()));
|
||||||
|
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse(response));
|
||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user