Source release v3.3.0
This commit is contained in:
@@ -19,37 +19,64 @@ const std::string kProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
/*
|
||||
* Provisioning response is a base64-encoded protobuf, optionally within a
|
||||
* JSON wrapper. If the JSON wrapper is present, extract the embedded response
|
||||
* message. Then perform the base64 decode and return the result.
|
||||
*
|
||||
* If an error occurs during the parse or the decode, return an empty string.
|
||||
*/
|
||||
void ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
|
||||
std::string* result) {
|
||||
const std::string json_start_substr("\"signedResponse\": \"");
|
||||
const std::string json_end_substr("\"");
|
||||
std::string message_string;
|
||||
|
||||
size_t start = provisioning_response.find(json_start_substr);
|
||||
|
||||
if (start == provisioning_response.npos) {
|
||||
// Message is not properly wrapped - reject it.
|
||||
LOGE("ExtractAndDecodeSignedMessage: cannot locate start substring");
|
||||
result->clear();
|
||||
return;
|
||||
} else {
|
||||
// Appears to be JSON-wrapped protobuf - find end of protobuf portion.
|
||||
size_t end = provisioning_response.find(json_end_substr,
|
||||
start + json_start_substr.length());
|
||||
if (end == provisioning_response.npos) {
|
||||
LOGE("ExtractAndDecodeSignedMessage: cannot locate end substring");
|
||||
result->clear();
|
||||
return;
|
||||
}
|
||||
size_t b64_string_size = end - start - json_start_substr.length();
|
||||
message_string.assign(provisioning_response,
|
||||
start + json_start_substr.length(), b64_string_size);
|
||||
}
|
||||
|
||||
if (message_string.empty()) {
|
||||
LOGE("ExtractAndDecodeSignedMessage: CdmProvisioningResponse is empty");
|
||||
result->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode the base64-encoded message.
|
||||
const std::vector<uint8_t> decoded_message =
|
||||
wvcdm::Base64SafeDecode(message_string);
|
||||
result->assign(decoded_message.begin(), decoded_message.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
// Protobuf generated classes.
|
||||
using video_widevine::ClientIdentification;
|
||||
using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::ProvisioningOptions;
|
||||
using video_widevine::ProvisioningRequest;
|
||||
using video_widevine::ProvisioningResponse;
|
||||
using video_widevine::SignedProvisioningMessage;
|
||||
|
||||
/*
|
||||
* This function converts SignedProvisioningRequest into base64 string. It then
|
||||
* wraps it in JSON format expected by the frontend. This server requires a
|
||||
* "web-safe" base 64 encoding, where '+' becomes '-' and '/' becomes '_'.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the ClientIdentification message token type for provisioning request.
|
||||
* NOTE: a DRM Cert should never be presented to the provisioning server.
|
||||
@@ -88,7 +115,7 @@ bool CertificateProvisioning::SetSpoidParameter(
|
||||
// Use the SPOID that has been pre-provided
|
||||
request->set_spoid(spoid);
|
||||
} else if (Properties::UseProviderIdInProvisioningRequest()) {
|
||||
if (service_certificate_->HasProviderId()) {
|
||||
if (!service_certificate_->provider_id().empty()) {
|
||||
request->set_provider_id(service_certificate_->provider_id());
|
||||
} else {
|
||||
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
|
||||
@@ -121,9 +148,9 @@ SignedProvisioningMessage::ProtocolVersion
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Compose a device provisioning request and output *request in a
|
||||
* JSON-compatible format (web-safe base64).
|
||||
* Also return *default_url of the provisioning server.
|
||||
*
|
||||
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
|
||||
*/
|
||||
@@ -162,17 +189,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
client_id->set_token(token);
|
||||
client_id->set_type(token_type);
|
||||
|
||||
#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification.
|
||||
if (service_certificate_->HasCertificate()) {
|
||||
#if 0 // TODO(gmorgan) Encrypt ClientIdentification. Pending Design.
|
||||
if (service_certificate_->has_certificate()) {
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
provisioning_request->mutable_encrypted_client_id();
|
||||
provisioning_request.mutable_encrypted_client_id();
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->EncryptClientId(&crypto_session_, client_id,
|
||||
encrypted_client_id);
|
||||
if (status == NO_ERROR) {
|
||||
provisioning_request->clear_client_id();
|
||||
provisioning_request.clear_client_id();
|
||||
} else {
|
||||
provisioning_request->clear_encrypted_client_id();
|
||||
provisioning_request.clear_encrypted_client_id();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -234,43 +261,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
|
||||
// Converts request into JSON string
|
||||
ComposeJsonRequestAsQueryString(serialized_request, request);
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
// Return request as web-safe base64 string
|
||||
std::vector<uint8_t> request_vector(serialized_request.begin(),
|
||||
serialized_request.end());
|
||||
request->assign(Base64SafeEncodeNoPad(request_vector));
|
||||
} else {
|
||||
request->swap(serialized_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
|
||||
@@ -281,13 +282,19 @@ bool CertificateProvisioning::ParseJsonResponse(
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
FileSystem* file_system, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key) {
|
||||
// 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");
|
||||
|
||||
std::string raw_string;
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
// The response is base64 encoded in a JSON wrapper.
|
||||
// Extract it and decode it. If errors, return an empty string.
|
||||
ExtractAndDecodeSignedMessage(response, &raw_string);
|
||||
} else {
|
||||
raw_string.assign(response);
|
||||
}
|
||||
|
||||
if (raw_string.empty()) {
|
||||
LOGE("HandleProvisioningResponse: response message is empty or "
|
||||
"an invalid JSON/base64 string.");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_1;
|
||||
}
|
||||
|
||||
@@ -295,7 +302,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
// 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)) {
|
||||
if (!signed_response.ParseFromString(raw_string)) {
|
||||
LOGE("HandleProvisioningResponse: fails to parse signed response");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_2;
|
||||
}
|
||||
@@ -314,6 +321,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
const std::string& signature = signed_response.signature();
|
||||
ProvisioningResponse provisioning_response;
|
||||
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
@@ -326,15 +334,29 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_5;
|
||||
}
|
||||
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
// If Provisioning 3.0 (OEM Cert provisioned), verify that the
|
||||
// message is properly signed.
|
||||
if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert) {
|
||||
if (service_certificate_->VerifySignedMessage(signed_message, signature)
|
||||
!= NO_ERROR) {
|
||||
LOGE("HandleProvisioningResponse: message not properly signed");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_6;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& new_private_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");
|
||||
const std::string& iv = provisioning_response.device_rsa_key_iv();
|
||||
|
||||
const std::string& wrapping_key = (provisioning_response.has_wrapping_key()) ?
|
||||
provisioning_response.wrapping_key() : std::string();
|
||||
|
||||
std::string wrapped_private_key;
|
||||
|
||||
if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce,
|
||||
new_private_key, iv, wrapping_key,
|
||||
&wrapped_private_key)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapCertificate fails");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_6;
|
||||
}
|
||||
|
||||
@@ -342,7 +364,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
|
||||
if (cert_type_ == kCertificateX509) {
|
||||
*cert = provisioning_response.device_certificate();
|
||||
*wrapped_key = wrapped_rsa_key;
|
||||
*wrapped_key = wrapped_private_key;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -357,7 +379,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_7;
|
||||
}
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_private_key)) {
|
||||
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_8;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user