Initial import of Widevine Common Encryption DRM engine

Builds libwvmdrmengine.so, which is loaded by the new
MediaDrm APIs to support playback of Widevine/CENC
protected content.

Change-Id: I6f57dd37083dfd96c402cb9dd137c7d74edc8f1c
This commit is contained in:
Jeff Tinker
2013-03-21 17:39:02 -07:00
parent 38334efbe7
commit 1a8aa0dd05
211 changed files with 51913 additions and 144 deletions

View File

@@ -0,0 +1,298 @@
// Copyright 2012 Google Inc. All Rights Reserved.
#include "license.h"
#include "crypto_session.h"
#include "log.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::LicenseRequest;
using video_widevine_server::sdk::LicenseRequest_ClientIdentification;
using video_widevine_server::sdk::LicenseRequest_ClientIdentification_NameValue;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_KeyContainer;
using video_widevine_server::sdk::SignedMessage;
using video_widevine_server::sdk::STREAMING;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_ExistingLicense;
CdmLicense::CdmLicense(): session_(NULL) {}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(const std::string& token, CryptoSession* session) {
if (token.size() == 0)
return false;
if (session == NULL || !session->IsValid() || !session->IsOpen())
return false;
token_ = token;
session_ = session;
return true;
}
bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data,
CdmKeyMessage* signed_request) {
if (!session_ ||
token_.empty()) {
return false;
}
if (init_data.empty()) {
LOGE("CdmLicense::PrepareKeyRequest : No init data provided;");
return false;
}
if (!signed_request) {
LOGE("CdmLicense::PrepareKeyRequest : No signed request provided.");
return false;
}
// TODO(gmorgan): Request ID owned by session?
std::string request_id;
session_->GenerateRequestId(request_id);
LicenseRequest license_request;
LicenseRequest_ClientIdentification* client_id =
license_request.mutable_client_id();
client_id->set_type(LicenseRequest_ClientIdentification::KEYBOX);
client_id->set_token(token_);
// Content Identification may be a cenc_id, a webm_id or a license_id
LicenseRequest_ContentIdentification* content_id =
license_request.mutable_content_id();
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id();
cenc_content_id->add_pssh(init_data);
cenc_content_id->set_license_type(STREAMING);
cenc_content_id->set_request_id(request_id);
// TODO(jfore): The time field will be updated once the cdm wrapper
// has been updated to pass us in the time.
license_request.set_request_time(0);
license_request.set_type(LicenseRequest::NEW);
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
uint32_t nonce;
if (!session_->GenerateNonce(&nonce)) {
return false;
}
license_request.set_key_control_nonce(UintToString(nonce));
LOGD("PrepareKeyRequest: nonce=%u", nonce);
// License request is complete. Serialize it.
std::string serialized_license_req;
license_request.SerializeToString(&serialized_license_req);
// Derive signing and encryption keys and construct signature.
std::string license_request_signature;
if (!session_->PrepareRequest(serialized_license_req,
&license_request_signature)) {
signed_request->clear();
return false;
}
if (license_request_signature.empty()) {
signed_request->clear();
return false;
}
// Put serialize license request and signature together
SignedMessage signed_message;
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature);
signed_message.set_msg(serialized_license_req);
signed_message.SerializeToString(signed_request);
return true;
}
bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request) {
if (!session_) {
return false;
}
if (!signed_request) {
LOGE("CdmLicense::PrepareKeyRenewalRequest : No signed request provided.");
return false;
}
LicenseRequest license_request;
license_request.set_type(LicenseRequest::RENEWAL);
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
license_request.mutable_content_id()->mutable_license();
current_license->mutable_license_id()->CopyFrom(license_id_);
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
uint32_t nonce;
if (!session_->GenerateNonce(&nonce)) {
return false;
}
license_request.set_key_control_nonce(UintToString(nonce));
LOGD("PrepareKeyRenewalRequest: nonce=%u", nonce);
// License request is complete. Serialize it.
std::string serialized_license_req;
license_request.SerializeToString(&serialized_license_req);
// Construct signature.
std::string license_request_signature;
if (!session_->PrepareRenewalRequest(serialized_license_req,
&license_request_signature))
return false;
if (license_request_signature.empty()) return false;
// Put serialize license request and signature together
SignedMessage signed_message;
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature);
signed_message.set_msg(serialized_license_req);
signed_message.SerializeToString(signed_request);
return true;
}
bool CdmLicense::HandleKeyResponse(const CdmKeyResponse& license_response) {
if (!session_) {
return false;
}
if (license_response.empty()) {
LOGE("CdmLicense::HandleKeyResponse : Empty license response.");
return false;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response))
return false;
if (!signed_response.has_signature())
return false;
License license;
if (!license.ParseFromString(signed_response.msg()))
return false;
// Extract mac key
std::string mac_key_iv;
std::string mac_key;
if (license.policy().can_renew())
{
for (int i = 0; i < license.key_size(); ++i) {
if (license.key(i).type() == License_KeyContainer::SIGNING) {
mac_key_iv.assign(license.key(i).iv());
// Strip off PKCS#5 padding
mac_key.assign(license.key(i).key().data(), MAC_KEY_SIZE);
}
}
if (mac_key_iv.size() != KEY_IV_SIZE ||
mac_key.size() != MAC_KEY_SIZE)
{
return false;
}
// License Id should not be empty for renewable license
if (!license.has_id()) return false;
license_id_.CopyFrom(license.id());
}
CryptoKey* key_array = new CryptoKey[license.key_size()];
if (key_array == NULL) return false;
// Extract content key(s)
int num_keys = 0;
for (int i = 0; i < license.key_size(); ++i) {
// TODO(kqyang): Key ID size is not fixed in spec, but conventionally we
// always use 16 bytes key id. We'll need to update oemcrypto to support
// variable size key id.
if (license.key(i).id().size() == KEY_ID_SIZE &&
license.key(i).key().size() == KEY_SIZE + KEY_PAD_SIZE &&
license.key(i).type() == License_KeyContainer::CONTENT) {
key_array[num_keys].set_key_id(license.key(i).id());
// Strip off PKCS#5 padding
key_array[num_keys].set_key_data(
license.key(i).key().substr(0, KEY_SIZE));
key_array[num_keys].set_key_data_iv(license.key(i).iv());
if (license.key(i).has_key_control()) {
key_array[num_keys].set_key_control(
license.key(i).key_control().struct_());
key_array[num_keys].set_key_control_iv(
license.key(i).key_control().iv());
}
num_keys++;
}
}
if (num_keys == 0) return false;
// TODO(kqyang): move protocol buffer related stuff in policy
// engine to this file.
// policy_engine_.SetLicense(license);
bool status = session_->LoadKeys(signed_response.msg(),
signed_response.signature(),
mac_key_iv,
mac_key,
num_keys,
key_array);
delete[] key_array;
return status;
}
bool CdmLicense::HandleKeyRenewalResponse(
const CdmKeyResponse& license_response) {
if (!session_) {
return false;
}
if (license_response.empty()) {
LOGE("CdmLicense::HandleKeyRenewalResponse : Empty license response.");
return false;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response))
return false;
if (!signed_response.has_signature())
return false;
// TODO(jfore): refresh the keys in oemcrypto
License license;
if (!license.ParseFromString(signed_response.msg()))
return false;
if (!license.has_id()) return false;
if (license.id().version() > license_id_.version()) {
//This is the normal case.
license_id_.CopyFrom(license.id());
// TODO(kqyang): should we move protocol buffer related stuff in policy
// engine to this file instead?
// policy_engine_.UpdateLicense(license);
} else {
// This isn't supposed to happen.
// TODO(jfore): Handle wrap? We can miss responses and that should be
// considered normal until retries are exhausted.
// policy_.set_can_play(false);
}
return true;
}
} // namespace wvcdm