OEMCrypto v16.1
Merge of http://go/wvgerrit/93404 This CL updates the Widevine CDM to support OEMCrypto v16.1 Test: Tested in 16.2 CL Bug: 141247171 Change-Id: I69bd993500f6fb63bf6010c8b0250dc7acc3d71b
This commit is contained in:
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
* source code may only be used and distributed under the Widevine Master
|
||||
* License Agreement.
|
||||
*/
|
||||
|
||||
#include "oec_util.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "odk_overflow.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
using namespace oec_util;
|
||||
|
||||
namespace oec_util {
|
||||
|
||||
namespace {
|
||||
|
||||
/* @ private functions */
|
||||
|
||||
const int CURRENT_OEC_VERSION = 16;
|
||||
|
||||
/**
|
||||
* Template for parsing requests
|
||||
*
|
||||
* Template arguments:
|
||||
* S: kdo output struct
|
||||
* T: struct serialized by odk
|
||||
* U: auto-generated deserializing function for |T|
|
||||
*/
|
||||
template <typename S, typename T, typename U>
|
||||
bool ParseRequest(uint32_t message_type, const string& oemcrypto_core_message,
|
||||
S* core_request, T* prepared, const U unpacker) {
|
||||
if (!core_request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* buf =
|
||||
reinterpret_cast<const uint8_t*>(oemcrypto_core_message.c_str());
|
||||
size_t buf_length = oemcrypto_core_message.size();
|
||||
|
||||
Message* msg = NULL;
|
||||
AllocateMessage(&msg, message_block);
|
||||
InitMessage(msg, const_cast<uint8_t*>(buf), buf_length);
|
||||
SetSize(msg, buf_length);
|
||||
|
||||
unpacker(msg, prepared);
|
||||
if (!ValidMessage(msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& core_message = prepared->core_message;
|
||||
core_request->api_version = core_message.nonce_values.api_version;
|
||||
core_request->nonce = core_message.nonce_values.nonce;
|
||||
core_request->session_id = core_message.nonce_values.session_id;
|
||||
return core_message.message_type == message_type &&
|
||||
core_message.message_length == GetOffset(msg) &&
|
||||
core_request->api_version == CURRENT_OEC_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template for parsing requests
|
||||
*
|
||||
* Template arguments:
|
||||
* T: struct to be deserialized by odk
|
||||
* S: kdo input struct
|
||||
* P: auto-generated serializing function for |T|
|
||||
*/
|
||||
template <typename T, typename S, typename P>
|
||||
bool CreateResponse(uint32_t message_type, const S& core_request,
|
||||
string* oemcrypto_core_message, T& response,
|
||||
const P& packer) {
|
||||
if (!oemcrypto_core_message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* header = reinterpret_cast<ODK_CoreMessage*>(&response);
|
||||
header->message_type = message_type;
|
||||
header->nonce_values.api_version = core_request.api_version;
|
||||
header->nonce_values.nonce = core_request.nonce;
|
||||
header->nonce_values.session_id = core_request.session_id;
|
||||
|
||||
uint8_t buf[2048] = {0};
|
||||
Message* msg = NULL;
|
||||
AllocateMessage(&msg, message_block);
|
||||
InitMessage(msg, buf, sizeof(buf));
|
||||
packer(msg, &response);
|
||||
if (!ValidMessage(msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t message_length = GetSize(msg);
|
||||
InitMessage(msg, buf + sizeof(header->message_type),
|
||||
sizeof(header->message_length));
|
||||
Pack_uint32_t(msg, &message_length);
|
||||
oemcrypto_core_message->assign(reinterpret_cast<const char*>(buf),
|
||||
message_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyDeviceId(ODK_ProvisioningResponse& dest,
|
||||
const ODK_ProvisioningRequest& src) {
|
||||
auto& core_provisioning = dest.core_provisioning;
|
||||
const string& device_id = src.device_id;
|
||||
core_provisioning.device_id_length = device_id.size();
|
||||
if (core_provisioning.device_id_length >
|
||||
sizeof(core_provisioning.device_id)) {
|
||||
return false;
|
||||
}
|
||||
memset(core_provisioning.device_id, 0, sizeof(core_provisioning.device_id));
|
||||
memcpy(core_provisioning.device_id, device_id.data(),
|
||||
core_provisioning.device_id_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// @ public parse request (deserializer) functions
|
||||
|
||||
bool ParseLicenseRequest(const string& oemcrypto_core_message,
|
||||
ODK_LicenseRequest* core_license_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedLicense;
|
||||
ODK_PreparedLicense prepared_license = {};
|
||||
return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message,
|
||||
core_license_request, &prepared_license, unpacker);
|
||||
}
|
||||
|
||||
bool ParseRenewalRequest(const string& oemcrypto_core_message,
|
||||
ODK_RenewalRequest* core_renewal_request) {
|
||||
const auto unpacker = Unpack_ODK_RenewalMessage;
|
||||
ODK_RenewalMessage prepared_renewal = {};
|
||||
if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message,
|
||||
core_renewal_request, &prepared_renewal, unpacker)) {
|
||||
return false;
|
||||
}
|
||||
core_renewal_request->playback_time = prepared_renewal.playback_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseProvisioningRequest(
|
||||
const string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||
const auto unpacker = Unpack_ODK_ProvisioningMessage;
|
||||
ODK_ProvisioningMessage prepared_provision = {};
|
||||
if (!ParseRequest(ODK_Provisioning_Request_Type, oemcrypto_core_message,
|
||||
core_provisioning_request, &prepared_provision, unpacker)) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t* device_id = prepared_provision.device_id;
|
||||
const uint32_t device_id_length = prepared_provision.device_id_length;
|
||||
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
|
||||
return false;
|
||||
}
|
||||
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {};
|
||||
if (memcmp(zero, device_id + device_id_length,
|
||||
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
|
||||
return false;
|
||||
}
|
||||
core_provisioning_request->device_id.assign(
|
||||
reinterpret_cast<const char*>(device_id), device_id_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
// @ public create response functions
|
||||
|
||||
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
|
||||
const ODK_LicenseRequest& core_request,
|
||||
string* oemcrypto_core_message) {
|
||||
ODK_LicenseResponse license_response{
|
||||
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic)};
|
||||
return CreateResponse(ODK_License_Response_Type, core_request,
|
||||
oemcrypto_core_message, license_response,
|
||||
Pack_ODK_LicenseResponse);
|
||||
}
|
||||
|
||||
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
|
||||
string* oemcrypto_core_message) {
|
||||
ODK_RenewalMessage renewal{{}, core_request.playback_time};
|
||||
renewal.playback_time = core_request.playback_time;
|
||||
return CreateResponse(ODK_Renewal_Response_Type, core_request,
|
||||
oemcrypto_core_message, renewal,
|
||||
Pack_ODK_RenewalMessage);
|
||||
}
|
||||
|
||||
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
string* oemcrypto_core_message) {
|
||||
ODK_ProvisioningResponse prov_response{
|
||||
{}, const_cast<ODK_ParsedProvisioning*>(&parsed_prov)};
|
||||
if (!CopyDeviceId(prov_response, core_request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CreateResponse(ODK_Provisioning_Response_Type, core_request,
|
||||
oemcrypto_core_message, prov_response,
|
||||
Pack_ODK_ProvisioningResponse);
|
||||
}
|
||||
|
||||
} // namespace oec_util
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
* source code may only be used and distributed under the Widevine Master
|
||||
* License Agreement.
|
||||
*/
|
||||
|
||||
#include "oec_util_proto.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "odk_overflow.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
using namespace oec_util;
|
||||
|
||||
namespace oec_util {
|
||||
|
||||
namespace {
|
||||
|
||||
/* @ private functions */
|
||||
|
||||
/**
|
||||
* Extract OEMCrypto_Substring (offset, length) from serialized protobuf
|
||||
*
|
||||
* Parameters:
|
||||
* message: serialized license protobuf
|
||||
* field: substring value
|
||||
*/
|
||||
OEMCrypto_Substring GetOecSubstring(const std::string& message,
|
||||
const std::string& field) {
|
||||
OEMCrypto_Substring substring = {};
|
||||
size_t pos = message.find(field);
|
||||
if (pos != std::string::npos) {
|
||||
substring = OEMCrypto_Substring{pos, field.length()};
|
||||
}
|
||||
return substring;
|
||||
}
|
||||
|
||||
OEMCrypto_KeyObject KeyContainerToOecKey(const string& proto,
|
||||
const License::KeyContainer& k) {
|
||||
OEMCrypto_KeyObject obj = {};
|
||||
obj.key_id = GetOecSubstring(proto, k.id());
|
||||
obj.key_data_iv = GetOecSubstring(proto, k.iv());
|
||||
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
|
||||
// the padding will always be 16 bytes.
|
||||
const string& key_data = k.key();
|
||||
const size_t PKCS5_PADDING_SIZE = 16;
|
||||
obj.key_data = GetOecSubstring(
|
||||
proto, key_data.substr(0, std::max(PKCS5_PADDING_SIZE, key_data.size()) -
|
||||
PKCS5_PADDING_SIZE));
|
||||
if (k.has_key_control()) {
|
||||
const auto& key_control = k.key_control();
|
||||
obj.key_control_iv = GetOecSubstring(proto, key_control.iv());
|
||||
obj.key_control = GetOecSubstring(proto, key_control.key_control_block());
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// @ public create response functions
|
||||
|
||||
bool CreateCoreLicenseResponse(const video_widevine::License& lic,
|
||||
const ODK_LicenseRequest& core_request,
|
||||
string* oemcrypto_core_message) {
|
||||
string proto;
|
||||
if (!lic.SerializeToString(&proto)) {
|
||||
return false;
|
||||
}
|
||||
ODK_ParsedLicense parsed_lic{};
|
||||
|
||||
for (int i = 0; i < lic.key_size(); ++i) {
|
||||
const auto& k = lic.key(i);
|
||||
switch (k.type()) {
|
||||
case License_KeyContainer::SIGNING: {
|
||||
parsed_lic.enc_mac_keys_iv = GetOecSubstring(proto, k.iv());
|
||||
// Strip off PKCS#5 padding
|
||||
string mac_keys(k.key(), 2 * wvoec::MAC_KEY_SIZE);
|
||||
parsed_lic.enc_mac_keys = GetOecSubstring(proto, mac_keys);
|
||||
break;
|
||||
}
|
||||
case License_KeyContainer::CONTENT: {
|
||||
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
|
||||
return false;
|
||||
}
|
||||
uint32_t& n = parsed_lic.key_array_length;
|
||||
parsed_lic.key_array[n++] = KeyContainerToOecKey(proto, k);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& license_id = lic.id();
|
||||
if (license_id.has_provider_session_token()) {
|
||||
parsed_lic.pst =
|
||||
GetOecSubstring(proto, license_id.provider_session_token());
|
||||
}
|
||||
|
||||
if (lic.has_srm_requirement()) {
|
||||
parsed_lic.srm_restriction_data =
|
||||
GetOecSubstring(proto, lic.srm_requirement());
|
||||
}
|
||||
|
||||
parsed_lic.license_type = license_id.type();
|
||||
// todo: nonce_required
|
||||
const auto& policy = lic.policy();
|
||||
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
|
||||
timer_limits.soft_expiry = policy.soft_enforce_playback_duration();
|
||||
timer_limits.earliest_playback_start_seconds = 0;
|
||||
timer_limits.latest_playback_start_seconds =
|
||||
policy.license_duration_seconds();
|
||||
timer_limits.initial_playback_duration_seconds =
|
||||
policy.playback_duration_seconds();
|
||||
timer_limits.renewal_playback_duration_seconds =
|
||||
policy.playback_duration_seconds();
|
||||
timer_limits.license_duration_seconds = policy.license_duration_seconds();
|
||||
|
||||
return CreateCoreLicenseResponse(parsed_lic, core_request,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
|
||||
bool CreateCoreProvisioningResponse(
|
||||
const video_widevine::ProvisioningResponse& prov,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
string* oemcrypto_core_message) {
|
||||
ODK_ParsedProvisioning parsed_prov{};
|
||||
string proto;
|
||||
if (!prov.SerializeToString(&proto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed_prov.key_type = 0; // todo: ECC or RSA
|
||||
if (prov.has_device_rsa_key()) {
|
||||
parsed_prov.enc_private_key = GetOecSubstring(proto, prov.device_rsa_key());
|
||||
}
|
||||
if (prov.has_device_rsa_key_iv()) {
|
||||
parsed_prov.enc_private_key_iv =
|
||||
GetOecSubstring(proto, prov.device_rsa_key_iv());
|
||||
}
|
||||
if (prov.has_wrapping_key()) {
|
||||
parsed_prov.encrypted_message_key =
|
||||
GetOecSubstring(proto, prov.wrapping_key());
|
||||
}
|
||||
|
||||
return CreateCoreProvisioningResponse(parsed_prov, core_request,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
|
||||
} // namespace oec_util
|
||||
Reference in New Issue
Block a user