Merges to android Pi release (part: 1)

Below are a set of CLs being merged from the wv cdm repo to the android repo.

* Fix handling of OEM Cert public key.

  Author: Srujan Gaddam <srujzs@google.com>

  [ Merge of http://go/wvgerrit/27921 ]

  This is a potential fix for b/36656190. Set aside public
  key on first call to get the public key, and use it afterwards.
  This gets rid of extra calls to OEMCrypto_GetOEMPublicCertificate(),
  which has side-effect of staging the OEM private key.

  This also fixes a problem where the public cert string was
  not being trimmed to match the size returned by
  OEMCrypto_GetOEMPublicCertificate().

* Complete provisioning request/response for Provisioning 3.0

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27780 ]

  Fix bug on provisioning request path where GenerateDerivedKeys()
  was being called when preparing to generate the signature.

  Add message signature verification, and call correct OEMCrypto
  routine to rewrap the private key (OEMCrypto_RewrapDeviceRSAKey30).

* Implement Cdm::deleteAllUsageRecords()

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27780 ]

  Delete all usage records for current origin.  Removes usage
  records from file system and retains the PSTs.  The deletes
  any usage entries matching those PSTs held by OEMCrypto.

  BUG: 35319024

* Remove stringencoders library from third_party.

  Author: Jacob Trimble <modmaker@google.com>

  [ Merge of http://go/wvgerrit/27585 ]

  We have a fork of the stringencoders library that we use for base64
  encoding.  This reimplements base64 encoding to remove the extra
  dependency and to reduce the amount of code.

* Add Cdm::deleteUsageRecord() based on key_set_id.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27605 ]

  Delete specified usage record from file system usage info and
  from OEMCrypto.

  BUG: 35319024

* Modifiable OEMCrypto

  Author: Fred Gylys-Colwell <fredgc@google.com>

  [ Merge of http://go/wvgerrit/24729 ]

  This CL adds a new variant of the OEMCrypto mock code that adjusts its
  behavior based on a configuration file.  This is intended for
  testing.

  For example, a tester can set current_hdcp to 2 in the options.txt
  file, push it to the device, and verify that a license is granted for
  HDCP 2.0.  Then the tester can edit the value of current_hdcp to 1 and
  push the file to the device.  Playback should stop because the license
  is no longer valid.

  This variant uses a real level 1 liboemcrypto.so to push data to a
  secure buffer.  That means we can test playback for a license that
  requires secure buffers on an Android device with real secure buffers.

  BUG: 35141278
  BUG: 37353534

BUG: 71650075
Test: Not currently passing. Will be addressed in a subsequent
      commit in the chain.

Change-Id: I58443c510919e992bb455192e70373490a00e2b6
This commit is contained in:
Rahul Frias
2018-01-05 17:05:18 -08:00
parent e34f83cdce
commit 0419b55222
120 changed files with 5402 additions and 6827 deletions

View File

@@ -19,45 +19,71 @@ 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.
*/
bool CertificateProvisioning::GetProvisioningTokenType(
ClientIdentification::TokenType* token_type) {
CdmClientTokenType token = crypto_session_.GetPreProvisionTokenType();
switch (token) {
switch (crypto_session_.GetPreProvisionTokenType()) {
case kClientTokenKeybox:
*token_type = ClientIdentification::KEYBOX;
return true;
@@ -67,8 +93,6 @@ bool CertificateProvisioning::GetProvisioningTokenType(
case kClientTokenDrmCert:
default:
// shouldn't happen
LOGE("CertificateProvisioning::GetProvisioningTokenType: unexpected "
"provisioning type: %d", token);
return false;
}
}
@@ -83,7 +107,7 @@ bool CertificateProvisioning::SetSpoidParameter(
const std::string& origin, const std::string& spoid,
ProvisioningRequest* request) {
if (!request) {
LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer "
LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer "
"passed to method.");
return false;
}
@@ -102,7 +126,8 @@ bool CertificateProvisioning::SetSpoidParameter(
// Legacy behavior - Concatenate Unique ID with Origin
std::string device_unique_id;
if (!crypto_session_.GetInternalDeviceUniqueId(&device_unique_id)) {
LOGE("CryptoSession::GetStableIdField: Failure to get device unique ID");
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
"device unique ID");
return false;
}
request->set_stable_id(device_unique_id + origin);
@@ -123,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.
*/
@@ -164,6 +189,22 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
client_id->set_token(token);
client_id->set_type(token_type);
#if 0 // TODO(gmorgan) Encrypt ClientIdentification. Pending Design.
if (service_certificate_->has_certificate()) {
EncryptedClientIdentification* 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();
} else {
provisioning_request.clear_encrypted_client_id();
}
return status;
}
#endif
uint32_t nonce;
if (!crypto_session_.GenerateNonce(&nonce)) {
LOGE("GetProvisioningRequest: fails to generate a nonce");
@@ -220,43 +261,13 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
// Converts request into JSON string
ComposeJsonRequestAsQueryString(serialized_request, request);
// Return request as web-safe base64 string
std::vector<uint8_t> request_vector(serialized_request.begin(),
serialized_request.end());
request->assign(Base64SafeEncodeNoPad(request_vector));
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
@@ -267,13 +278,15 @@ 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;
// The response is base64 encoded in a JSON wrapper.
// Extract it and decode it. If errors, return an empty string.
ExtractAndDecodeSignedMessage(response, &raw_string);
if (raw_string.empty()) {
LOGE("HandleProvisioningResponse: response message is empty or "
"an invalid JSON/base64 string.");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
}
@@ -281,7 +294,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;
}
@@ -300,6 +313,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)) {
@@ -312,17 +326,28 @@ 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)) {
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& iv = provisioning_response.device_rsa_key_iv();
const std::string& wrapping_key = (provisioning_response.has_wrapping_key()) ?
provisioning_response.wrapping_key() : std::string();
const std::string& signature = signed_response.signature();
std::string wrapped_rsa_key;
provisioning_response.wrapping_key() : std::string();
std::string wrapped_private_key;
if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce,
enc_rsa_key, rsa_key_iv, wrapping_key,
&wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
new_private_key, iv, wrapping_key,
&wrapped_private_key)) {
LOGE("HandleProvisioningResponse: RewrapCertificate fails");
return CERT_PROVISIONING_RESPONSE_ERROR_6;
}
@@ -330,10 +355,13 @@ 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;
}
// This is the entire certificate (SignedDrmDeviceCertificate).
// This will be stored to the device as the final step in the device
// provisioning process.
const std::string& device_certificate =
provisioning_response.device_certificate();
@@ -342,7 +370,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;
}