Changes JSON format for provisioning request and response to match server change.
This new format uses the SignedProvisioningMessage proto buffer definition so the client does not have to parse the message and signature from the JSON response separately. This change makes it more flexible to extend the fields in the SignedProvisioningMessage. Adds Apiary API key to the default provisioning server url. Fixes a bug in GetCertRequestResponse() where a LOGD() can generate a fault if there is no response data. Bug: 8620943 Merge of https://widevine-internal-review.googlesource.com/#/c/5230/ from Widevine CDM repository Change-Id: I4945ee2d16f88666e41edf990dd07102a9271105
This commit is contained in:
@@ -102,7 +102,6 @@ class CdmEngine : public TimerHandler {
|
|||||||
bool CancelSessions();
|
bool CancelSessions();
|
||||||
void CleanupProvisioningSession(const CdmSessionId& cdm_session_id);
|
void CleanupProvisioningSession(const CdmSessionId& cdm_session_id);
|
||||||
void ComposeJsonRequest(const std::string& message,
|
void ComposeJsonRequest(const std::string& message,
|
||||||
const std::string& signature,
|
|
||||||
CdmProvisioningRequest* request);
|
CdmProvisioningRequest* request);
|
||||||
|
|
||||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const std::string kDefaultProvisioningServerUrl =
|
const std::string kDefaultProvisioningServerUrl =
|
||||||
"http://www-googleapis-test.sandbox.google.com/certificateprovisioning/v1/devicecertificates/create";
|
"http://www-googleapis-test.sandbox.google.com/"
|
||||||
|
"certificateprovisioning/v1/devicecertificates/create"
|
||||||
|
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -340,38 +342,29 @@ void CdmEngine::CleanupProvisioningSession(const CdmSessionId& cdm_session_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function converts message and signature into base64 format.
|
* This function converts SignedProvisioningRequest into base64 format.
|
||||||
* It then wraps it in JSON format expected by the Apiary frontend.
|
* It then wraps it in JSON format expected by the Apiary frontend.
|
||||||
* Apiary requires the base64 encoding to replace '+' with minus '-',
|
* Apiary requires the base64 encoding to replace '+' with minus '-',
|
||||||
* and '/' with underscore '_'; opposite to stubby's.
|
* and '/' with underscore '_'; opposite to stubby's.
|
||||||
*
|
*
|
||||||
* Returns the JSON formated string in *request.
|
* Returns the JSON formated string in *request.
|
||||||
* The JSON formated request takes the following format:
|
* The JSON formated request takes the following format:
|
||||||
* {
|
*
|
||||||
* 'signedRequest': {
|
* {'signedRequest':'base64 encoded message'}
|
||||||
* 'message': 'base64 encoded message',
|
|
||||||
* 'signature': 'base64 encoded signature'
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
void CdmEngine::ComposeJsonRequest(
|
void CdmEngine::ComposeJsonRequest(
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
const std::string& signature,
|
|
||||||
CdmProvisioningRequest* request) {
|
CdmProvisioningRequest* request) {
|
||||||
|
|
||||||
// performs base64 encoding for message
|
// performs base64 encoding for message
|
||||||
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
||||||
std::string message_b64 = Base64SafeEncode(message_vector);
|
std::string message_b64 = Base64SafeEncode(message_vector);
|
||||||
|
|
||||||
// performs base64 encoding for signature
|
request->assign("{'signedRequest':'");
|
||||||
std::vector<uint8_t> signature_vector(signature.begin(), signature.end());
|
|
||||||
std::string signature_b64 = Base64SafeEncode(signature_vector);
|
|
||||||
|
|
||||||
request->assign("{'signedRequest':{'message':'");
|
|
||||||
request->append(message_b64);
|
request->append(message_b64);
|
||||||
request->append("','signature':'");
|
request->append("'}");
|
||||||
request->append(signature_b64);
|
|
||||||
request->append("'}}");
|
LOGD("json request:\r\n%s", request->c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -458,27 +451,33 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
|||||||
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
|
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
|
||||||
provisioning_request.set_nonce(the_nonce);
|
provisioning_request.set_nonce(the_nonce);
|
||||||
|
|
||||||
// Serializes the provisioning request.
|
std::string serialized_message;
|
||||||
std::string serialized_request;
|
provisioning_request.SerializeToString(&serialized_message);
|
||||||
provisioning_request.SerializeToString(&serialized_request);
|
|
||||||
|
|
||||||
// Derives signing and encryption keys and constructs signature.
|
// Derives signing and encryption keys and constructs signature.
|
||||||
std::string request_signature;
|
std::string request_signature;
|
||||||
if (!crypto_session->PrepareRequest(serialized_request,
|
if (!crypto_session->PrepareRequest(serialized_message,
|
||||||
&request_signature, true)) {
|
&request_signature, true)) {
|
||||||
request->clear();
|
request->clear();
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request_signature.empty()) {
|
if (request_signature.empty()) {
|
||||||
request->clear();
|
request->clear();
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
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
|
// converts request into JSON string
|
||||||
ComposeJsonRequest(serialized_request, request_signature, request);
|
ComposeJsonRequest(serialized_request, request);
|
||||||
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,26 +526,18 @@ bool CdmEngine::ParseJsonResponse(
|
|||||||
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||||
CdmProvisioningResponse& response) {
|
CdmProvisioningResponse& response) {
|
||||||
if (response.empty()) {
|
if (response.empty()) {
|
||||||
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
LOGE("Empty provisioning response.");
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Extracts response from JSON string, decodes base64 signed message
|
// Extracts signed response from JSON string, decodes base64 signed response
|
||||||
const std::string kMessageStart = "\"message\": \"";
|
const std::string kMessageStart = "\"signedResponse\": \"";
|
||||||
const std::string kMessageEnd = "\",";
|
const std::string kMessageEnd = "\"";
|
||||||
std::string signed_message;
|
std::string serialized_signed_response;
|
||||||
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd, &signed_message)) {
|
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
|
||||||
LOGE("Fails to extract signed message from JSON response");
|
&serialized_signed_response)) {
|
||||||
return UNKNOWN_ERROR;
|
LOGE("Fails to extract signed serialized response from JSON response");
|
||||||
}
|
|
||||||
|
|
||||||
// Extracts signature from JSON string, decodes base64 signature
|
|
||||||
const std::string kSignatureStart = "\"signature\": \"";
|
|
||||||
const std::string kSignatureEnd = "\"";
|
|
||||||
std::string signature;
|
|
||||||
if (!ParseJsonResponse(response, kSignatureStart, kSignatureEnd, &signature)) {
|
|
||||||
LOGE("Fails to extract signature from JSON response");
|
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,7 +559,8 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
CdmSessionId cdm_session_id = provisioning_session_->session_id();
|
CdmSessionId cdm_session_id = provisioning_session_->session_id();
|
||||||
CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id);
|
CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id);
|
||||||
if (!crypto_session) {
|
if (!crypto_session) {
|
||||||
LOGE("HandleProvisioningResponse: fails to find %s", cdm_session_id.c_str());
|
LOGE("HandleProvisioningResponse: fails to find %s",
|
||||||
|
cdm_session_id.c_str());
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,23 +568,38 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
|||||||
// Authenticates provisioning response using D1s (server key derived from
|
// Authenticates provisioning response using D1s (server key derived from
|
||||||
// the provisioing request's input). Validate provisioning response and
|
// the provisioing request's input). Validate provisioning response and
|
||||||
// stores private device RSA key and certificate.
|
// 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(cdm_session_id);
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!signed_response.has_signature() || !signed_response.has_message()) {
|
||||||
|
LOGE("Invalid response - signature or message not found");
|
||||||
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& signed_message = signed_response.message();
|
||||||
ProvisioningResponse provisioning_response;
|
ProvisioningResponse provisioning_response;
|
||||||
|
|
||||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||||
LOGE("HandleProvisioningResponse: fails to parse signed message");
|
LOGE("Fails to parse signed message");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provisioning_response.has_device_rsa_key()) {
|
if (!provisioning_response.has_device_rsa_key()) {
|
||||||
LOGE("HandleProvisioningResponse: invalid response - key not found");
|
LOGE("Invalid response - key not found");
|
||||||
CleanupProvisioningSession(cdm_session_id);
|
CleanupProvisioningSession(cdm_session_id);
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
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 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;
|
std::string wrapped_rsa_key;
|
||||||
if (!crypto_session->RewrapDeviceRSAKey(signed_message,
|
if (!crypto_session->RewrapDeviceRSAKey(signed_message,
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ wvcdm::KeyId g_wrong_key_id;
|
|||||||
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
||||||
|
|
||||||
const std::string kDefaultProvisioningServerUrl =
|
const std::string kDefaultProvisioningServerUrl =
|
||||||
"http://www-googleapis-test.sandbox.google.com/certificateprovisioning/v1/devicecertificates/create";
|
"http://www-googleapis-test.sandbox.google.com/"
|
||||||
|
"certificateprovisioning/v1/devicecertificates/create"
|
||||||
|
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
@@ -56,7 +58,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
app_parameters,
|
app_parameters,
|
||||||
&key_msg_,
|
&key_msg_,
|
||||||
&server_url), wvcdm::KEY_MESSAGE);
|
&server_url), wvcdm::KEY_MESSAGE);
|
||||||
EXPECT_EQ((size_t)0, server_url.size());
|
EXPECT_EQ(0, static_cast<int>(server_url.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateRenewalRequest(const std::string& key_system,
|
void GenerateRenewalRequest(const std::string& key_system,
|
||||||
@@ -71,7 +73,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
app_parameters,
|
app_parameters,
|
||||||
&key_msg_,
|
&key_msg_,
|
||||||
&server_url), wvcdm::KEY_MESSAGE);
|
&server_url), wvcdm::KEY_MESSAGE);
|
||||||
EXPECT_NE((size_t)0, server_url.size());
|
EXPECT_NE(0, static_cast<int>(server_url.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// posts a request and extracts the drm message from the response
|
// posts a request and extracts the drm message from the response
|
||||||
@@ -122,9 +124,11 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
|||||||
url_request.PostCertRequest(key_msg_);
|
url_request.PostCertRequest(key_msg_);
|
||||||
std::string response;
|
std::string response;
|
||||||
int resp_bytes = url_request.GetResponse(response);
|
int resp_bytes = url_request.GetResponse(response);
|
||||||
LOGD("size=%u, response start: %s", response.size(),
|
if (resp_bytes) {
|
||||||
response.substr(0, 1024).c_str());
|
LOGD("size=%u, response start:\t\rn%s", response.size(),
|
||||||
LOGD("end: %s", response.substr(response.size() - 256).c_str());
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user