Source release 17.1.1
This commit is contained in:
181
oemcrypto/odk/src/core_message_deserialize.cpp
Normal file
181
oemcrypto/odk/src/core_message_deserialize.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "core_message_deserialize.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
namespace deserialize {
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* 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 std::string& oemcrypto_core_message, S* core_request,
|
||||
T* prepared, const U unpacker) {
|
||||
if (core_request == nullptr || prepared == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* buf =
|
||||
reinterpret_cast<const uint8_t*>(oemcrypto_core_message.c_str());
|
||||
const size_t buf_length = oemcrypto_core_message.size();
|
||||
|
||||
ODK_Message msg = ODK_Message_Create(const_cast<uint8_t*>(buf), buf_length);
|
||||
ODK_Message_SetSize(&msg, buf_length);
|
||||
|
||||
unpacker(&msg, prepared);
|
||||
if (!ODK_Message_IsValid(&msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& core_message = prepared->core_message;
|
||||
core_request->api_major_version = core_message.nonce_values.api_major_version;
|
||||
core_request->api_minor_version = core_message.nonce_values.api_minor_version;
|
||||
core_request->nonce = core_message.nonce_values.nonce;
|
||||
core_request->session_id = core_message.nonce_values.session_id;
|
||||
|
||||
// Verify that the minor version matches the released version for the given
|
||||
// major version.
|
||||
if (core_request->api_major_version < ODK_FIRST_VERSION) {
|
||||
// Non existing versions are not supported.
|
||||
return false;
|
||||
} else if (core_request->api_major_version == 16) {
|
||||
// For version 16, we demand a minor version of at least 2.
|
||||
// We accept 16.2, 16.3, or higher.
|
||||
if (core_request->api_minor_version < 2) return false;
|
||||
} else {
|
||||
// Other versions do not (yet) have a restriction on minor number.
|
||||
// In particular, future versions are accepted for forward compatibility.
|
||||
}
|
||||
// For v16, a release and a renewal use the same message structure.
|
||||
// However, for future API versions, the release might be a separate
|
||||
// message. Otherwise, we expect an exact match of message types.
|
||||
// A provisioning request may contain a renewed provisioning message.
|
||||
if (message_type != ODK_Common_Request_Type &&
|
||||
core_message.message_type != message_type &&
|
||||
!(message_type == ODK_Renewal_Request_Type &&
|
||||
core_message.message_type == ODK_Release_Request_Type) &&
|
||||
!(message_type == ODK_Provisioning_Request_Type &&
|
||||
core_message.message_type == ODK_Renewed_Provisioning_Request_Type)) {
|
||||
return false;
|
||||
}
|
||||
// Verify that the amount of buffer we read, which is GetOffset, is not more
|
||||
// than the total message size. We allow the total message size to be larger
|
||||
// for forward compatibility because future messages might have extra fields
|
||||
// that we can ignore.
|
||||
if (core_message.message_length < ODK_Message_GetOffset(&msg)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||
ODK_LicenseRequest* core_license_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedLicenseRequest;
|
||||
ODK_PreparedLicenseRequest prepared_license = {};
|
||||
return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message,
|
||||
core_license_request, &prepared_license, unpacker);
|
||||
}
|
||||
|
||||
bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||
ODK_RenewalRequest* core_renewal_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedRenewalRequest;
|
||||
ODK_PreparedRenewalRequest prepared_renewal = {};
|
||||
if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message,
|
||||
core_renewal_request, &prepared_renewal, unpacker)) {
|
||||
return false;
|
||||
}
|
||||
core_renewal_request->playback_time_seconds = prepared_renewal.playback_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreProvisioningRequestFromMessage(
|
||||
const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedProvisioningRequest;
|
||||
ODK_PreparedProvisioningRequest 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);
|
||||
core_provisioning_request->renewal_type = OEMCrypto_NoRenewal;
|
||||
core_provisioning_request->renewal_data.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreRenewedProvisioningRequestFromMessage(
|
||||
const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedRenewedProvisioningRequest;
|
||||
ODK_PreparedRenewedProvisioningRequest prepared_provision = {};
|
||||
if (!ParseRequest(ODK_Renewed_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);
|
||||
|
||||
if (prepared_provision.renewal_data_length >
|
||||
sizeof(prepared_provision.renewal_data)) {
|
||||
return false;
|
||||
}
|
||||
core_provisioning_request->renewal_type = OEMCrypto_RenewalACert;
|
||||
core_provisioning_request->renewal_data.assign(
|
||||
reinterpret_cast<const char*>(prepared_provision.renewal_data),
|
||||
prepared_provision.renewal_data_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreCommonRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||
ODK_CommonRequest* common_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedCommonRequest;
|
||||
ODK_PreparedCommonRequest prepared_common = {};
|
||||
return ParseRequest(ODK_Common_Request_Type, oemcrypto_core_message,
|
||||
common_request, &prepared_common, unpacker);
|
||||
}
|
||||
|
||||
} // namespace deserialize
|
||||
} // namespace oemcrypto_core_message
|
||||
41
oemcrypto/odk/src/core_message_features.cpp
Normal file
41
oemcrypto/odk/src/core_message_features.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2021 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "core_message_features.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
namespace features {
|
||||
const CoreMessageFeatures CoreMessageFeatures::kDefaultFeatures;
|
||||
|
||||
bool CoreMessageFeatures::operator==(const CoreMessageFeatures &other) const {
|
||||
return maximum_major_version == other.maximum_major_version &&
|
||||
maximum_minor_version == other.maximum_minor_version;
|
||||
}
|
||||
|
||||
CoreMessageFeatures CoreMessageFeatures::DefaultFeatures(
|
||||
uint32_t maximum_major_version) {
|
||||
CoreMessageFeatures features;
|
||||
features.maximum_major_version = maximum_major_version;
|
||||
// The default minor version is the highest for each major version.
|
||||
switch (maximum_major_version) {
|
||||
case 16:
|
||||
features.maximum_minor_version = 5; // 16.5
|
||||
break;
|
||||
case 17:
|
||||
features.maximum_minor_version = 1; // 17.1
|
||||
break;
|
||||
default:
|
||||
features.maximum_minor_version = 0;
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os,
|
||||
const CoreMessageFeatures &features) {
|
||||
return os << "v" << features.maximum_major_version << "."
|
||||
<< features.maximum_minor_version;
|
||||
}
|
||||
|
||||
} // namespace features
|
||||
} // namespace oemcrypto_core_message
|
||||
204
oemcrypto/odk/src/core_message_serialize.cpp
Normal file
204
oemcrypto/odk/src/core_message_serialize.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "core_message_serialize.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "odk_target.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
namespace serialize {
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Template for copying nonce values from request to response, and also
|
||||
* computing the API version of the response.
|
||||
*
|
||||
* Template arguments:
|
||||
* T: struct to be deserialized by odk
|
||||
* S: kdo input struct
|
||||
*/
|
||||
template <typename T, typename S>
|
||||
bool CreateResponseHeader(const CoreMessageFeatures& features,
|
||||
ODK_MessageType message_type, const S& core_request,
|
||||
T& response) {
|
||||
// Bad major version.
|
||||
if ((features.maximum_major_version > ODK_MAJOR_VERSION) ||
|
||||
(features.maximum_major_version == ODK_MAJOR_VERSION &&
|
||||
features.maximum_minor_version > ODK_MINOR_VERSION)) {
|
||||
// TODO(b/147513335): this should be logged.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* header = &response.request.core_message;
|
||||
header->message_type = message_type;
|
||||
header->nonce_values.api_major_version = core_request.api_major_version;
|
||||
header->nonce_values.api_minor_version = core_request.api_minor_version;
|
||||
header->nonce_values.nonce = core_request.nonce;
|
||||
header->nonce_values.session_id = core_request.session_id;
|
||||
// The message API version for the response is the minimum of our version and
|
||||
// the request's version.
|
||||
if (core_request.api_major_version > features.maximum_major_version) {
|
||||
header->nonce_values.api_major_version = features.maximum_major_version;
|
||||
header->nonce_values.api_minor_version = features.maximum_minor_version;
|
||||
} else if (core_request.api_major_version == features.maximum_major_version &&
|
||||
core_request.api_minor_version > features.maximum_minor_version) {
|
||||
header->nonce_values.api_minor_version = features.maximum_minor_version;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template for parsing requests and packing response
|
||||
*
|
||||
* 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(ODK_MessageType message_type, const S& core_request,
|
||||
std::string* oemcrypto_core_message, T& response,
|
||||
const P& packer) {
|
||||
if (!oemcrypto_core_message) {
|
||||
return false;
|
||||
}
|
||||
auto* header = &response.request.core_message;
|
||||
if (header->message_type != message_type ||
|
||||
header->nonce_values.api_major_version < ODK_FIRST_VERSION) {
|
||||
// This indicates CreateResponseHeader was not called.
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr size_t BUF_CAPACITY = 2048;
|
||||
std::vector<uint8_t> buf(BUF_CAPACITY, 0);
|
||||
ODK_Message msg = ODK_Message_Create(buf.data(), buf.capacity());
|
||||
packer(&msg, &response);
|
||||
if (!ODK_Message_IsValid(&msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t message_length = static_cast<uint32_t>(ODK_Message_GetSize(&msg));
|
||||
msg = ODK_Message_Create(buf.data() + sizeof(header->message_type),
|
||||
sizeof(header->message_length));
|
||||
Pack_uint32_t(&msg, &message_length);
|
||||
oemcrypto_core_message->assign(reinterpret_cast<const char*>(buf.data()),
|
||||
message_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyDeviceId(const ODK_ProvisioningRequest& src,
|
||||
ODK_ProvisioningResponse* dest) {
|
||||
auto& request = dest->request;
|
||||
const std::string& device_id = src.device_id;
|
||||
if (request.device_id_length > sizeof(request.device_id)) {
|
||||
return false;
|
||||
}
|
||||
request.device_id_length = static_cast<uint32_t>(device_id.size());
|
||||
memset(request.device_id, 0, sizeof(request.device_id));
|
||||
memcpy(request.device_id, device_id.data(), request.device_id_length);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CreateCoreLicenseResponse(const CoreMessageFeatures& features,
|
||||
const ODK_ParsedLicense& parsed_lic,
|
||||
const ODK_LicenseRequest& core_request,
|
||||
const std::string& core_request_sha256,
|
||||
std::string* oemcrypto_core_message) {
|
||||
ODK_LicenseResponse license_response{
|
||||
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic)};
|
||||
if (!CreateResponseHeader(features, ODK_License_Response_Type, core_request,
|
||||
license_response)) {
|
||||
return false;
|
||||
}
|
||||
if (ODK_MAX_NUM_KEYS < license_response.parsed_license->key_array_length) {
|
||||
return false;
|
||||
}
|
||||
if (license_response.request.core_message.nonce_values.api_major_version ==
|
||||
16) {
|
||||
ODK_LicenseResponseV16 license_response_v16;
|
||||
license_response_v16.request = license_response.request;
|
||||
license_response_v16.parsed_license.enc_mac_keys_iv =
|
||||
license_response.parsed_license->enc_mac_keys_iv;
|
||||
license_response_v16.parsed_license.enc_mac_keys =
|
||||
license_response.parsed_license->enc_mac_keys;
|
||||
license_response_v16.parsed_license.pst =
|
||||
license_response.parsed_license->pst;
|
||||
license_response_v16.parsed_license.srm_restriction_data =
|
||||
license_response.parsed_license->srm_restriction_data;
|
||||
license_response_v16.parsed_license.license_type =
|
||||
license_response.parsed_license->license_type;
|
||||
license_response_v16.parsed_license.nonce_required =
|
||||
license_response.parsed_license->nonce_required;
|
||||
license_response_v16.parsed_license.timer_limits =
|
||||
license_response.parsed_license->timer_limits;
|
||||
license_response_v16.parsed_license.key_array_length =
|
||||
license_response.parsed_license->key_array_length;
|
||||
uint32_t i;
|
||||
for (i = 0; i < license_response_v16.parsed_license.key_array_length &&
|
||||
i < license_response.parsed_license->key_array_length;
|
||||
i++) {
|
||||
license_response_v16.parsed_license.key_array[i] =
|
||||
license_response.parsed_license->key_array[i];
|
||||
}
|
||||
if (core_request_sha256.size() != sizeof(license_response_v16.request_hash))
|
||||
return false;
|
||||
memcpy(license_response_v16.request_hash, core_request_sha256.data(),
|
||||
sizeof(license_response_v16.request_hash));
|
||||
return CreateResponse(ODK_License_Response_Type, core_request,
|
||||
oemcrypto_core_message, license_response_v16,
|
||||
Pack_ODK_LicenseResponseV16);
|
||||
}
|
||||
return CreateResponse(ODK_License_Response_Type, core_request,
|
||||
oemcrypto_core_message, license_response,
|
||||
Pack_ODK_LicenseResponse);
|
||||
}
|
||||
|
||||
bool CreateCoreRenewalResponse(const CoreMessageFeatures& features,
|
||||
const ODK_RenewalRequest& core_request,
|
||||
uint64_t renewal_duration_seconds,
|
||||
std::string* oemcrypto_core_message) {
|
||||
ODK_RenewalResponse renewal_response{{}, core_request.playback_time_seconds};
|
||||
renewal_response.request.playback_time = core_request.playback_time_seconds;
|
||||
renewal_response.renewal_duration_seconds = renewal_duration_seconds;
|
||||
if (!CreateResponseHeader(features, ODK_Renewal_Response_Type, core_request,
|
||||
renewal_response)) {
|
||||
return false;
|
||||
}
|
||||
return CreateResponse(ODK_Renewal_Response_Type, core_request,
|
||||
oemcrypto_core_message, renewal_response,
|
||||
Pack_ODK_RenewalResponse);
|
||||
}
|
||||
|
||||
bool CreateCoreProvisioningResponse(const CoreMessageFeatures& features,
|
||||
const ODK_ParsedProvisioning& parsed_prov,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
std::string* oemcrypto_core_message) {
|
||||
ODK_ProvisioningResponse prov_response{
|
||||
{}, const_cast<ODK_ParsedProvisioning*>(&parsed_prov)};
|
||||
if (!CopyDeviceId(core_request, &prov_response)) {
|
||||
return false;
|
||||
}
|
||||
if (!CreateResponseHeader(features, ODK_Provisioning_Response_Type,
|
||||
core_request, prov_response)) {
|
||||
return false;
|
||||
}
|
||||
return CreateResponse(ODK_Provisioning_Response_Type, core_request,
|
||||
oemcrypto_core_message, prov_response,
|
||||
Pack_ODK_ProvisioningResponse);
|
||||
}
|
||||
|
||||
} // namespace serialize
|
||||
} // namespace oemcrypto_core_message
|
||||
198
oemcrypto/odk/src/core_message_serialize_proto.cpp
Normal file
198
oemcrypto/odk/src/core_message_serialize_proto.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "core_message_serialize_proto.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "core_message_serialize.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
namespace oemcrypto_core_message {
|
||||
namespace serialize {
|
||||
namespace {
|
||||
using oemcrypto_core_message::features::CoreMessageFeatures;
|
||||
|
||||
/* @ 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 std::string& proto, const video_widevine::License::KeyContainer& k,
|
||||
const bool uses_padding) {
|
||||
OEMCrypto_KeyObject obj = {};
|
||||
obj.key_id = GetOecSubstring(proto, k.id());
|
||||
obj.key_data_iv = GetOecSubstring(proto, k.iv());
|
||||
|
||||
OEMCrypto_Substring key_data = GetOecSubstring(proto, k.key());
|
||||
|
||||
// Strip off PKCS#5 padding. A key can either be 16 of 32 bytes, but that
|
||||
// makes it hard to know if a key (when 32 bytes) is a 16 byte key with
|
||||
// padding or a 32 byte key without padding.
|
||||
if (uses_padding) {
|
||||
const size_t PKCS5_PADDING_SIZE = 16;
|
||||
key_data.length -= PKCS5_PADDING_SIZE;
|
||||
}
|
||||
obj.key_data = key_data;
|
||||
|
||||
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 CreateCoreLicenseResponseFromProto(const CoreMessageFeatures& features,
|
||||
const std::string& serialized_license,
|
||||
const ODK_LicenseRequest& core_request,
|
||||
const std::string& core_request_sha256,
|
||||
const bool nonce_required,
|
||||
const bool uses_padding,
|
||||
std::string* oemcrypto_core_message) {
|
||||
video_widevine::License lic;
|
||||
if (!lic.ParseFromString(serialized_license)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ODK_ParsedLicense parsed_lic{};
|
||||
bool any_content = false;
|
||||
bool any_entitlement = false;
|
||||
|
||||
for (int i = 0; i < lic.key_size(); ++i) {
|
||||
const auto& k = lic.key(i);
|
||||
switch (k.type()) {
|
||||
case video_widevine::License_KeyContainer::SIGNING: {
|
||||
if (!k.has_key()) {
|
||||
continue;
|
||||
}
|
||||
parsed_lic.enc_mac_keys_iv =
|
||||
GetOecSubstring(serialized_license, k.iv());
|
||||
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, k.key());
|
||||
break;
|
||||
}
|
||||
case video_widevine::License_KeyContainer::CONTENT:
|
||||
case video_widevine::License_KeyContainer::OPERATOR_SESSION:
|
||||
case video_widevine::License_KeyContainer::OEM_CONTENT:
|
||||
case video_widevine::License_KeyContainer::OEM_ENTITLEMENT:
|
||||
case video_widevine::License_KeyContainer::ENTITLEMENT: {
|
||||
if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT ||
|
||||
k.type() == video_widevine::License_KeyContainer::OEM_ENTITLEMENT) {
|
||||
any_entitlement = true;
|
||||
} else {
|
||||
any_content = true;
|
||||
}
|
||||
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(serialized_license, k, uses_padding);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (any_content && any_entitlement) {
|
||||
// TODO(b/147513335): this should be logged -- both type of keys.
|
||||
return false;
|
||||
}
|
||||
if (!any_content && !any_entitlement) {
|
||||
// TODO(b/147513335): this should be logged -- no keys?
|
||||
return false;
|
||||
}
|
||||
parsed_lic.license_type =
|
||||
any_content ? OEMCrypto_ContentLicense : OEMCrypto_EntitlementLicense;
|
||||
const auto& lid = lic.id();
|
||||
if (lid.has_provider_session_token()) {
|
||||
parsed_lic.pst =
|
||||
GetOecSubstring(serialized_license, lid.provider_session_token());
|
||||
}
|
||||
if (lic.has_srm_requirement()) {
|
||||
parsed_lic.srm_restriction_data =
|
||||
GetOecSubstring(serialized_license, lic.srm_requirement());
|
||||
}
|
||||
if (lic.policy().has_watermarking_control()) {
|
||||
parsed_lic.watermarking = lic.policy().watermarking_control();
|
||||
}
|
||||
parsed_lic.nonce_required = nonce_required;
|
||||
const auto& policy = lic.policy();
|
||||
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
|
||||
timer_limits.soft_enforce_rental_duration =
|
||||
policy.soft_enforce_rental_duration();
|
||||
timer_limits.soft_enforce_playback_duration =
|
||||
policy.soft_enforce_playback_duration();
|
||||
timer_limits.earliest_playback_start_seconds = 0;
|
||||
timer_limits.rental_duration_seconds = policy.rental_duration_seconds();
|
||||
timer_limits.total_playback_duration_seconds =
|
||||
policy.playback_duration_seconds();
|
||||
timer_limits.initial_renewal_duration_seconds =
|
||||
policy.renewal_delay_seconds() +
|
||||
policy.renewal_recovery_duration_seconds();
|
||||
|
||||
return CreateCoreLicenseResponse(features, parsed_lic, core_request,
|
||||
core_request_sha256, oemcrypto_core_message);
|
||||
}
|
||||
|
||||
bool CreateCoreProvisioningResponseFromProto(
|
||||
const CoreMessageFeatures& features,
|
||||
const std::string& serialized_provisioning_resp,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
std::string* oemcrypto_core_message) {
|
||||
ODK_ParsedProvisioning parsed_prov{};
|
||||
video_widevine::ProvisioningResponse prov;
|
||||
if (!prov.ParseFromString(serialized_provisioning_resp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed_prov.key_type =
|
||||
OEMCrypto_RSA_Private_Key; // TODO(b/148404408): ECC or RSA
|
||||
if (prov.has_device_rsa_key()) {
|
||||
parsed_prov.enc_private_key =
|
||||
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key());
|
||||
}
|
||||
if (prov.has_device_rsa_key_iv()) {
|
||||
parsed_prov.enc_private_key_iv =
|
||||
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key_iv());
|
||||
}
|
||||
if (prov.has_wrapping_key()) {
|
||||
parsed_prov.encrypted_message_key =
|
||||
GetOecSubstring(serialized_provisioning_resp, prov.wrapping_key());
|
||||
}
|
||||
|
||||
return CreateCoreProvisioningResponse(features, parsed_prov, core_request,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
|
||||
} // namespace serialize
|
||||
} // namespace oemcrypto_core_message
|
||||
19
oemcrypto/odk/src/kdo.gypi
Normal file
19
oemcrypto/odk/src/kdo.gypi
Normal file
@@ -0,0 +1,19 @@
|
||||
# Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
# source code may only be used and distributed under the Widevine License
|
||||
# Agreement.
|
||||
|
||||
# These files are used by the server and by some ODK test code. These files are
|
||||
# not built into the ODK library on the device.
|
||||
{
|
||||
'sources': [
|
||||
'core_message_deserialize.cpp',
|
||||
'core_message_features.cpp',
|
||||
'core_message_serialize.cpp',
|
||||
'core_message_serialize_proto.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'src',
|
||||
'../include',
|
||||
],
|
||||
}
|
||||
|
||||
511
oemcrypto/odk/src/odk.c
Normal file
511
oemcrypto/odk/src/odk.c
Normal file
@@ -0,0 +1,511 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "odk.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "odk_overflow.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "odk_util.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
/* @ private odk functions */
|
||||
|
||||
static OEMCryptoResult ODK_PrepareRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
ODK_MessageType message_type, const ODK_NonceValues* nonce_values,
|
||||
void* prepared_request_buffer, size_t prepared_request_buffer_length) {
|
||||
if (nonce_values == NULL || core_message_length == NULL ||
|
||||
prepared_request_buffer == NULL ||
|
||||
*core_message_length > message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_Message msg = ODK_Message_Create(message, *core_message_length);
|
||||
|
||||
/* The core message should be at the beginning of the buffer, and with a
|
||||
* shorter length. */
|
||||
if (sizeof(ODK_CoreMessage) > prepared_request_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_CoreMessage* core_message = (ODK_CoreMessage*)prepared_request_buffer;
|
||||
*core_message = (ODK_CoreMessage){
|
||||
message_type,
|
||||
0,
|
||||
*nonce_values,
|
||||
};
|
||||
|
||||
/* Set core message length, and pack prepared request into message if the
|
||||
* message buffer has been correctly initialized by the caller. */
|
||||
switch (message_type) {
|
||||
case ODK_License_Request_Type: {
|
||||
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
|
||||
if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Pack_ODK_PreparedLicenseRequest(
|
||||
&msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer);
|
||||
break;
|
||||
}
|
||||
case ODK_Renewal_Request_Type: {
|
||||
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
|
||||
if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Pack_ODK_PreparedRenewalRequest(
|
||||
&msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer);
|
||||
break;
|
||||
}
|
||||
case ODK_Provisioning_Request_Type: {
|
||||
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
|
||||
if (sizeof(ODK_PreparedProvisioningRequest) >
|
||||
prepared_request_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Pack_ODK_PreparedProvisioningRequest(
|
||||
&msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
|
||||
break;
|
||||
}
|
||||
case ODK_Renewed_Provisioning_Request_Type: {
|
||||
core_message->message_length = ODK_RENEWED_PROVISIONING_REQUEST_SIZE;
|
||||
if (sizeof(ODK_PreparedRenewedProvisioningRequest) >
|
||||
prepared_request_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Pack_ODK_PreparedRenewedProvisioningRequest(
|
||||
&msg,
|
||||
(ODK_PreparedRenewedProvisioningRequest*)prepared_request_buffer);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
*core_message_length = core_message->message_length;
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK) {
|
||||
/* This is to indicate the caller that the core_message_length has been
|
||||
* appropriately set, but the message buffer is either empty or too small,
|
||||
* which needs to be initialized and filled in the subsequent call. */
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
if (ODK_Message_GetSize(&msg) != *core_message_length) {
|
||||
/* This should not happen. Something is wrong. */
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* Parse the core message and verify that it has the right type. The nonce
|
||||
* values are updated to hold the response's API version.
|
||||
*/
|
||||
static OEMCryptoResult ODK_ParseCoreHeader(const uint8_t* message,
|
||||
size_t message_length,
|
||||
size_t core_message_length,
|
||||
ODK_MessageType message_type,
|
||||
ODK_NonceValues* nonce_values) {
|
||||
// The core_message_length is the length of the core message, which is a
|
||||
// substring of the complete message.
|
||||
if (message == NULL || core_message_length > message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_CoreMessage core_message;
|
||||
ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length);
|
||||
|
||||
/* The core message should be at the beginning of the buffer. The core message
|
||||
* is the part we are parsing. */
|
||||
ODK_Message_SetSize(&msg, core_message_length);
|
||||
Unpack_ODK_CoreMessage(&msg, &core_message);
|
||||
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK ||
|
||||
message_type != core_message.message_type) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
// The current offset should be the end of the header, which is the message
|
||||
// type, message length, api version, and nonce fields. The header can't be
|
||||
// larger than the whole core message. Also, the core message specifies its
|
||||
// length, which should be exactly the length of the core message buffer.
|
||||
if (ODK_Message_GetOffset(&msg) > core_message.message_length ||
|
||||
core_message.message_length != core_message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
/* We do not support future API version. Also, this function should not be
|
||||
* used for legacy licenses without a core message. */
|
||||
if (core_message.nonce_values.api_major_version > ODK_MAJOR_VERSION ||
|
||||
core_message.nonce_values.api_major_version < ODK_FIRST_VERSION) {
|
||||
return ODK_UNSUPPORTED_API;
|
||||
}
|
||||
if (nonce_values) {
|
||||
/* If the server sent us an older format, record the message's API version.
|
||||
*/
|
||||
if (nonce_values->api_major_version >
|
||||
core_message.nonce_values.api_major_version) {
|
||||
// If the major version is smaller, use both values from the server.
|
||||
nonce_values->api_major_version =
|
||||
core_message.nonce_values.api_major_version;
|
||||
nonce_values->api_minor_version =
|
||||
core_message.nonce_values.api_minor_version;
|
||||
} else if (nonce_values->api_major_version ==
|
||||
core_message.nonce_values.api_major_version &&
|
||||
nonce_values->api_minor_version >
|
||||
core_message.nonce_values.api_minor_version) {
|
||||
// Otherwise, if the major versions are equal, but the minor is smaller,
|
||||
// then we should lower the minor version.
|
||||
nonce_values->api_minor_version =
|
||||
core_message.nonce_values.api_minor_version;
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* @ public odk functions */
|
||||
|
||||
/* @@ prepare request functions */
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedLicenseRequest license_request = {0};
|
||||
return ODK_PrepareRequest(
|
||||
message, message_length, core_message_length, ODK_License_Request_Type,
|
||||
nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||
size_t message_length,
|
||||
size_t* core_message_size,
|
||||
ODK_NonceValues* nonce_values,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds) {
|
||||
if (core_message_size == NULL || nonce_values == NULL ||
|
||||
clock_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
/* If the license has not been loaded, then this is release instead of a
|
||||
* renewal. All releases use v15. */
|
||||
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED ||
|
||||
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE) {
|
||||
nonce_values->api_major_version = ODK_FIRST_VERSION - 1;
|
||||
}
|
||||
if (nonce_values->api_major_version < ODK_FIRST_VERSION) {
|
||||
*core_message_size = 0;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
ODK_PreparedRenewalRequest renewal_request = {0};
|
||||
/* First, we compute the time this request was made relative to the playback
|
||||
* clock. */
|
||||
if (clock_values->time_of_first_decrypt == 0) {
|
||||
/* It is OK to preemptively request a renewal before playback starts.
|
||||
* We'll treat this as asking for a renewal at playback time 0. */
|
||||
renewal_request.playback_time = 0;
|
||||
} else {
|
||||
/* Otherwise, playback_time is relative to the first decrypt. */
|
||||
if (odk_sub_overflow_u64(system_time_seconds,
|
||||
clock_values->time_of_first_decrypt,
|
||||
&renewal_request.playback_time)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save time for this request so that we can verify the response. This makes
|
||||
* all earlier requests invalid. If preparing this request fails, then all
|
||||
* requests will be bad. */
|
||||
clock_values->time_of_renewal_request = renewal_request.playback_time;
|
||||
|
||||
return ODK_PrepareRequest(
|
||||
message, message_length, core_message_size, ODK_Renewal_Request_Type,
|
||||
nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest));
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||
size_t device_id_length) {
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedProvisioningRequest provisioning_request = {0};
|
||||
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
provisioning_request.device_id_length = (uint32_t)device_id_length;
|
||||
if (device_id) {
|
||||
memcpy(provisioning_request.device_id, device_id, device_id_length);
|
||||
}
|
||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||
ODK_Provisioning_Request_Type, nonce_values,
|
||||
&provisioning_request,
|
||||
sizeof(ODK_PreparedProvisioningRequest));
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreRenewedProvisioningRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||
size_t device_id_length, uint16_t renewal_type, const uint8_t* renewal_data,
|
||||
size_t renewal_data_length) {
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedRenewedProvisioningRequest provisioning_request = {0};
|
||||
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
provisioning_request.device_id_length = (uint32_t)device_id_length;
|
||||
if (device_id) {
|
||||
memcpy(provisioning_request.device_id, device_id, device_id_length);
|
||||
}
|
||||
if (renewal_data_length > sizeof(provisioning_request.renewal_data)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
provisioning_request.renewal_type = renewal_type;
|
||||
provisioning_request.renewal_data_length = (uint32_t)renewal_data_length;
|
||||
if (renewal_data) {
|
||||
memcpy(provisioning_request.renewal_data, renewal_data,
|
||||
renewal_data_length);
|
||||
}
|
||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||
ODK_Renewed_Provisioning_Request_Type, nonce_values,
|
||||
&provisioning_request,
|
||||
sizeof(provisioning_request));
|
||||
}
|
||||
|
||||
/* @@ parse response functions */
|
||||
|
||||
OEMCryptoResult ODK_ParseLicense(
|
||||
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||
bool initial_license_load, bool usage_entry_present,
|
||||
ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
|
||||
ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license) {
|
||||
if (message == NULL || timer_limits == NULL || clock_values == NULL ||
|
||||
nonce_values == NULL || parsed_license == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
const OEMCryptoResult err =
|
||||
ODK_ParseCoreHeader(message, message_length, core_message_length,
|
||||
ODK_License_Response_Type, nonce_values);
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
ODK_LicenseResponse license_response = {0};
|
||||
license_response.parsed_license = parsed_license;
|
||||
|
||||
ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length);
|
||||
ODK_Message_SetSize(&msg, core_message_length);
|
||||
if (nonce_values->api_major_version == 16) {
|
||||
ODK_LicenseResponseV16 license_response_v16 = {0};
|
||||
Unpack_ODK_LicenseResponseV16(&msg, &license_response_v16);
|
||||
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK ||
|
||||
ODK_Message_GetOffset(&msg) != core_message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
// Need to manually set parsed_license fields to
|
||||
// license_response_v16.parsed_license field values since
|
||||
// license_response_v16 is no longer a pointer so parsed_license doesn't get
|
||||
// updated during the unpacking.
|
||||
parsed_license->enc_mac_keys_iv =
|
||||
license_response_v16.parsed_license.enc_mac_keys_iv;
|
||||
parsed_license->enc_mac_keys =
|
||||
license_response_v16.parsed_license.enc_mac_keys;
|
||||
parsed_license->pst = license_response_v16.parsed_license.pst;
|
||||
parsed_license->srm_restriction_data =
|
||||
license_response_v16.parsed_license.srm_restriction_data;
|
||||
parsed_license->license_type =
|
||||
license_response_v16.parsed_license.license_type;
|
||||
parsed_license->nonce_required =
|
||||
license_response_v16.parsed_license.nonce_required;
|
||||
parsed_license->timer_limits =
|
||||
license_response_v16.parsed_license.timer_limits;
|
||||
parsed_license->key_array_length =
|
||||
license_response_v16.parsed_license.key_array_length;
|
||||
uint32_t i;
|
||||
for (i = 0; i < parsed_license->key_array_length; i++) {
|
||||
parsed_license->key_array[i] =
|
||||
license_response_v16.parsed_license.key_array[i];
|
||||
}
|
||||
// Set fields not used in V16 to default values.
|
||||
parsed_license->watermarking = 0;
|
||||
// Set fields not used in V16 to default values.
|
||||
parsed_license->dtcp2_required.dtcp2_required = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_0.id = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_0.extension = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_0.length = 1;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_0.data = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_1.id = 1;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_1.extension = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_1.length = 3;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_1.data[0] = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_1.data[1] = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_1.data[2] = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_2.id = 2;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_2.extension = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_2.length = 3;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_2.data[0] = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_2.data[1] = 0;
|
||||
parsed_license->dtcp2_required.cmi_descriptor_2.data[2] = 0;
|
||||
license_response.request = license_response_v16.request;
|
||||
} else {
|
||||
Unpack_ODK_LicenseResponse(&msg, &license_response);
|
||||
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK ||
|
||||
ODK_Message_GetOffset(&msg) != core_message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the license has a provider session token (pst), then OEMCrypto should
|
||||
* have a usage entry loaded. The opposite is also an error. */
|
||||
if ((usage_entry_present && parsed_license->pst.length == 0) ||
|
||||
(!usage_entry_present && parsed_license->pst.length > 0)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
/* If this is the first time we load this license, then we verify that the
|
||||
* nonce values are the correct, otherwise we copy the nonce values. If the
|
||||
* nonce values are not required to be correct, then we don't know if this is
|
||||
* an initial load or not. In that case, we also copy the values so that we
|
||||
* can use the nonce values later for a renewal.
|
||||
*/
|
||||
if (parsed_license->nonce_required && initial_license_load) {
|
||||
if (nonce_values->nonce !=
|
||||
license_response.request.core_message.nonce_values.nonce ||
|
||||
nonce_values->session_id !=
|
||||
license_response.request.core_message.nonce_values.session_id) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
} else { /* !initial_license_load, or can't tell if initial. */
|
||||
nonce_values->nonce =
|
||||
license_response.request.core_message.nonce_values.nonce;
|
||||
nonce_values->session_id =
|
||||
license_response.request.core_message.nonce_values.session_id;
|
||||
}
|
||||
*timer_limits = parsed_license->timer_limits;
|
||||
/* And update the clock values state. */
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||
size_t core_message_length,
|
||||
const ODK_NonceValues* nonce_values,
|
||||
uint64_t system_time,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t* timer_value) {
|
||||
if (message == NULL || nonce_values == NULL || timer_limits == NULL ||
|
||||
clock_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
const OEMCryptoResult err =
|
||||
ODK_ParseCoreHeader(message, message_length, core_message_length,
|
||||
ODK_Renewal_Response_Type, NULL);
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
ODK_RenewalResponse renewal_response = {0};
|
||||
ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length);
|
||||
ODK_Message_SetSize(&msg, core_message_length);
|
||||
Unpack_ODK_RenewalResponse(&msg, &renewal_response);
|
||||
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK ||
|
||||
ODK_Message_GetOffset(&msg) != core_message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
/* always verify nonce_values for Renewal and Provisioning responses */
|
||||
if (!ODK_NonceValuesEqualExcludingVersion(
|
||||
nonce_values,
|
||||
&(renewal_response.request.core_message.nonce_values))) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
|
||||
/* Reference:
|
||||
* Doc: License Duration and Renewal (Changes for OEMCrypto v16)
|
||||
* Section: Renewal Message
|
||||
*/
|
||||
/* If a renewal request is lost in transit, we should throw it out and create
|
||||
* a new one. We use the timestamp to make sure we have the latest request.
|
||||
* We only do this if playback has already started. This allows us to reload
|
||||
* an offline license and also reload a renewal before starting playback.
|
||||
*/
|
||||
if (clock_values->timer_status != ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED &&
|
||||
clock_values->time_of_renewal_request <
|
||||
renewal_response.request.playback_time) {
|
||||
return ODK_STALE_RENEWAL;
|
||||
}
|
||||
return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time,
|
||||
renewal_response.renewal_duration_seconds,
|
||||
timer_value);
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_ParseProvisioning(
|
||||
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||
size_t device_id_length, ODK_ParsedProvisioning* parsed_response) {
|
||||
if (message == NULL || nonce_values == NULL || device_id == NULL ||
|
||||
parsed_response == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
const OEMCryptoResult err =
|
||||
ODK_ParseCoreHeader(message, message_length, core_message_length,
|
||||
ODK_Provisioning_Response_Type, NULL);
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
ODK_ProvisioningResponse provisioning_response = {0};
|
||||
provisioning_response.parsed_provisioning = parsed_response;
|
||||
|
||||
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length);
|
||||
ODK_Message_SetSize(&msg, core_message_length);
|
||||
Unpack_ODK_ProvisioningResponse(&msg, &provisioning_response);
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK ||
|
||||
ODK_Message_GetOffset(&msg) != core_message_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
/* always verify nonce_values for Renewal and Provisioning responses */
|
||||
if (!ODK_NonceValuesEqualExcludingVersion(
|
||||
nonce_values,
|
||||
&(provisioning_response.request.core_message.nonce_values))) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
|
||||
if (crypto_memcmp(device_id, provisioning_response.request.device_id,
|
||||
device_id_length) != 0) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
const uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
/* check bytes beyond device_id_length are 0 */
|
||||
if (crypto_memcmp(zero,
|
||||
provisioning_response.request.device_id + device_id_length,
|
||||
ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
bool CheckApiVersionAtMost(const ODK_NonceValues* nonce_values,
|
||||
uint16_t major_version, uint16_t minor_version) {
|
||||
return nonce_values->api_major_version < major_version ||
|
||||
(nonce_values->api_major_version == major_version &&
|
||||
nonce_values->api_minor_version <= minor_version);
|
||||
}
|
||||
41
oemcrypto/odk/src/odk.gyp
Normal file
41
oemcrypto/odk/src/odk.gyp
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
# source code may only be used and distributed under the Widevine License
|
||||
# Agreement.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'toolsets' : [ 'target' ],
|
||||
'target_name': 'odk',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library' : 1,
|
||||
'hard_dependency': 1,
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
'../../include',
|
||||
],
|
||||
'includes' : [
|
||||
'odk.gypi',
|
||||
],
|
||||
'cflags': [
|
||||
# TODO(b/172518513): Remove this
|
||||
'-Wno-error=cast-qual',
|
||||
],
|
||||
'defines': [
|
||||
# Needed for <endian.h> to work.
|
||||
'_DEFAULT_SOURCE',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'defines': [
|
||||
# Needed for <endian.h> to work.
|
||||
'_DEFAULT_SOURCE',
|
||||
],
|
||||
'include_dirs': [
|
||||
'.',
|
||||
'../include',
|
||||
'../../include',
|
||||
],
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
18
oemcrypto/odk/src/odk.gypi
Normal file
18
oemcrypto/odk/src/odk.gypi
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
# source code may only be used and distributed under the Widevine License
|
||||
# Agreement.
|
||||
|
||||
# These files are built into the ODK library on the device. They are also used
|
||||
# by the server and by test cocde. These files should compile on C99 compilers.
|
||||
{
|
||||
'sources': [
|
||||
'odk.c',
|
||||
'odk_message.c',
|
||||
'odk_overflow.c',
|
||||
'odk_serialize.c',
|
||||
'odk_timer.c',
|
||||
'odk_util.c',
|
||||
'serialization_base.c',
|
||||
],
|
||||
}
|
||||
|
||||
24
oemcrypto/odk/src/odk_assert.h
Normal file
24
oemcrypto/odk/src/odk_assert.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_ASSERT_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_ASSERT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if (__STDC_VERSION__ >= 201112L)
|
||||
#include <assert.h>
|
||||
#define odk_static_assert static_assert
|
||||
#else
|
||||
#define odk_static_assert(msg, e) \
|
||||
enum { odk_static_assert = 1 / (!!((msg) && (e))) };
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_ASSERT_H_
|
||||
41
oemcrypto/odk/src/odk_endian.h
Normal file
41
oemcrypto/odk/src/odk_endian.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_ENDIAN_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_ENDIAN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
||||
#include <endian.h>
|
||||
#define oemcrypto_htobe16 htobe16
|
||||
#define oemcrypto_be16toh be16toh
|
||||
#define oemcrypto_htobe32 htobe32
|
||||
#define oemcrypto_be32toh be32toh
|
||||
#define oemcrypto_htobe64 htobe64
|
||||
#define oemcrypto_be64toh be64toh
|
||||
#elif defined(__APPLE__)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define oemcrypto_htobe16 OSSwapHostToBigInt16
|
||||
#define oemcrypto_be16toh OSSwapBigToHostInt16
|
||||
#define oemcrypto_htobe32 OSSwapHostToBigInt32
|
||||
#define oemcrypto_be32toh OSSwapBigToHostInt32
|
||||
#define oemcrypto_htobe64 OSSwapHostToBigInt64
|
||||
#define oemcrypto_be64toh OSSwapBigToHostInt64
|
||||
#else /* defined(__linux__) || defined(__ANDROID__) */
|
||||
uint32_t oemcrypto_htobe16(uint16_t u16);
|
||||
uint32_t oemcrypto_be16toh(uint16_t u16);
|
||||
uint32_t oemcrypto_htobe32(uint32_t u32);
|
||||
uint32_t oemcrypto_be32toh(uint32_t u32);
|
||||
uint64_t oemcrypto_htobe64(uint64_t u64);
|
||||
uint64_t oemcrypto_be64toh(uint64_t u64);
|
||||
#endif /* defined(__linux__) || defined(__ANDROID__) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_ENDIAN_H_
|
||||
170
oemcrypto/odk/src/odk_message.c
Normal file
170
oemcrypto/odk/src/odk_message.c
Normal file
@@ -0,0 +1,170 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "odk_message.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "odk_message_priv.h"
|
||||
|
||||
/*
|
||||
* C11 defines static_assert in assert.h. If it is available, force a compile
|
||||
* time error if the abstract ODK_Message struct size does not match its
|
||||
* implementation. If static_assert is not available, the runtime assert in
|
||||
* InitMessage will catch the mismatch at the time a message is initialized.
|
||||
*/
|
||||
#ifdef static_assert
|
||||
static_assert(
|
||||
sizeof(ODK_Message) >= sizeof(ODK_Message_Impl),
|
||||
"sizeof(ODK_Message) is too small. You can increase "
|
||||
"SIZE_OF_ODK_MESSAGE_IMPL in odk_message.h to make it large enough.");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create a message structure that references a separate data buffer. An
|
||||
* initialized message is returned. The caller is responsible for ensuring that
|
||||
* the buffer remains allocated for the lifetime of the message. |buffer| may be
|
||||
* NULL. Serialization into a message with a NULL buffer will cause the message
|
||||
* size to be incremented, but no data will be written into the message
|
||||
* buffer. This is useful for calculating the amount of space a message will
|
||||
* need, prior to doing the actual serialization. The buffer contents are
|
||||
* unchanged by this function.
|
||||
*/
|
||||
ODK_Message ODK_Message_Create(uint8_t* buffer, size_t capacity) {
|
||||
assert(sizeof(ODK_Message) >= sizeof(ODK_Message_Impl));
|
||||
ODK_Message message;
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)&message;
|
||||
message_impl->base = buffer;
|
||||
message_impl->capacity = capacity;
|
||||
message_impl->size = 0;
|
||||
message_impl->read_offset = 0;
|
||||
message_impl->status = MESSAGE_STATUS_OK;
|
||||
return message;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase the contents of the message, set it to an empty state by setting the
|
||||
* message size and read offset to 0, effectively erasing the contents of the
|
||||
* message. The message data buffer pointer remains unchanged, i.e. the message
|
||||
* retains ownership of the buffer. The message buffer is zero-filled. The
|
||||
* message status is reset to MESSAGE_STATUS_OK.
|
||||
*/
|
||||
void ODK_Message_Clear(ODK_Message* message) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
message_impl->read_offset = 0;
|
||||
message_impl->size = 0;
|
||||
message_impl->status = MESSAGE_STATUS_OK;
|
||||
if (message_impl->base) {
|
||||
memset(message_impl->base, 0, message_impl->capacity);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset read pointer to the beginning of the message and clear status
|
||||
* so that parsing of the message will restart at the beginning of the
|
||||
* message. The message status is reset to MESSAGE_STATUS_OK.
|
||||
*/
|
||||
void ODK_Message_Reset(ODK_Message* message) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
message_impl->read_offset = 0;
|
||||
message_impl->status = MESSAGE_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pointer to the message data buffer, i.e. the message payload.
|
||||
* This is the buffer address that was passed into ODK_Message_Create.
|
||||
*/
|
||||
uint8_t* ODK_Message_GetBase(ODK_Message* message) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
return message_impl->base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the maximum number of bytes the message can hold.
|
||||
*/
|
||||
size_t ODK_Message_GetCapacity(ODK_Message* message) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
return message_impl->capacity;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the number of bytes currently in the message
|
||||
*/
|
||||
size_t ODK_Message_GetSize(ODK_Message* message) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
return message_impl->size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the offset of where the next bytes will be read from the message data
|
||||
* buffer.
|
||||
*/
|
||||
size_t ODK_Message_GetOffset(ODK_Message* message) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
return message_impl->read_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the status of the message
|
||||
*/
|
||||
ODK_MessageStatus ODK_Message_GetStatus(ODK_Message* message) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
return message_impl->status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the message status to a specific value
|
||||
*/
|
||||
void ODK_Message_SetStatus(ODK_Message* message, ODK_MessageStatus status) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
/* preserve the first error */
|
||||
if (message_impl->status == MESSAGE_STATUS_OK) {
|
||||
message_impl->status = status;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the size of the message to a value. This may be needed after writing data
|
||||
* into the message data buffer.
|
||||
*/
|
||||
void ODK_Message_SetSize(ODK_Message* message, size_t size) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
assert(message_impl != NULL);
|
||||
assert(size <= message_impl->capacity);
|
||||
message_impl->size = size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if the integrity of a message. This means that the status must be
|
||||
* MESSAGE_STATUS_OK and that the base, read_offset, size and capacity of the
|
||||
* message are within the range of valid values. The message's base pointer
|
||||
* may be NULL if the buffer has not been assigned yet, that is not invalid.
|
||||
*/
|
||||
bool ODK_Message_IsValid(ODK_Message* message) {
|
||||
assert(message);
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
if (message_impl == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (message_impl->status != MESSAGE_STATUS_OK) {
|
||||
return false;
|
||||
}
|
||||
if (message_impl->read_offset > message_impl->capacity ||
|
||||
message_impl->size > message_impl->capacity ||
|
||||
message_impl->read_offset > message_impl->size) {
|
||||
message_impl->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
41
oemcrypto/odk/src/odk_message_priv.h
Normal file
41
oemcrypto/odk/src/odk_message_priv.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_MESSAGE_PRIV_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_MESSAGE_PRIV_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This file must only be included by odk_message.c and serialization_base.c.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "odk_message.h"
|
||||
|
||||
/*
|
||||
* This is the implementation of a message. This structure is private, i.e. it
|
||||
* should only be included by files that are allowed to modify the internals of
|
||||
* a message, that being odk_message.c and serialization_base.c. To ensure
|
||||
* proper alignment and message size, an ODK_Message_Impl should never be
|
||||
* allocated directly, instead allocate ODK_Message and cast to ODK_Message_Impl
|
||||
* because ODK_Message_Impl may be smaller than ODK_Message.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t* base;
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
size_t read_offset;
|
||||
ODK_MessageStatus status;
|
||||
} ODK_Message_Impl;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_MESSAGE_PRIV_H_
|
||||
46
oemcrypto/odk/src/odk_overflow.c
Normal file
46
oemcrypto/odk/src/odk_overflow.c
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
|
||||
if (a >= b) {
|
||||
if (c) {
|
||||
*c = a - b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c) {
|
||||
if (UINT64_MAX - a >= b) {
|
||||
if (c) {
|
||||
*c = a + b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int odk_add_overflow_ux(size_t a, size_t b, size_t* c) {
|
||||
if (SIZE_MAX - a >= b) {
|
||||
if (c) {
|
||||
*c = a + b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int odk_mul_overflow_ux(size_t a, size_t b, size_t* c) {
|
||||
if (b > 0 && a > SIZE_MAX / b) {
|
||||
return 1;
|
||||
}
|
||||
if (c) {
|
||||
*c = a * b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
24
oemcrypto/odk/src/odk_overflow.h
Normal file
24
oemcrypto/odk/src/odk_overflow.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
|
||||
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
|
||||
int odk_add_overflow_ux(size_t a, size_t b, size_t* c);
|
||||
int odk_mul_overflow_ux(size_t a, size_t b, size_t* c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
|
||||
352
oemcrypto/odk/src/odk_serialize.c
Normal file
352
oemcrypto/odk/src/odk_serialize.c
Normal file
@@ -0,0 +1,352 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
/*
|
||||
* This code is auto-generated, do not edit
|
||||
*/
|
||||
|
||||
#include "odk_structs_priv.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
/* @ serialize */
|
||||
|
||||
/* @@ private serialize */
|
||||
|
||||
static void Pack_ODK_NonceValues(ODK_Message* msg, ODK_NonceValues const* obj) {
|
||||
Pack_uint16_t(msg, &obj->api_minor_version);
|
||||
Pack_uint16_t(msg, &obj->api_major_version);
|
||||
Pack_uint32_t(msg, &obj->nonce);
|
||||
Pack_uint32_t(msg, &obj->session_id);
|
||||
}
|
||||
|
||||
static void Pack_ODK_CoreMessage(ODK_Message* msg, ODK_CoreMessage const* obj) {
|
||||
Pack_uint32_t(msg, &obj->message_type);
|
||||
Pack_uint32_t(msg, &obj->message_length);
|
||||
Pack_ODK_NonceValues(msg, &obj->nonce_values);
|
||||
}
|
||||
|
||||
static void Pack_OEMCrypto_KeyObject(ODK_Message* msg,
|
||||
OEMCrypto_KeyObject const* obj) {
|
||||
Pack_OEMCrypto_Substring(msg, &obj->key_id);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->key_data_iv);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->key_data);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->key_control_iv);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->key_control);
|
||||
}
|
||||
|
||||
static void Pack_ODK_TimerLimits(ODK_Message* msg, ODK_TimerLimits const* obj) {
|
||||
Pack_bool(msg, &obj->soft_enforce_rental_duration);
|
||||
Pack_bool(msg, &obj->soft_enforce_playback_duration);
|
||||
Pack_uint64_t(msg, &obj->earliest_playback_start_seconds);
|
||||
Pack_uint64_t(msg, &obj->rental_duration_seconds);
|
||||
Pack_uint64_t(msg, &obj->total_playback_duration_seconds);
|
||||
Pack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
|
||||
}
|
||||
|
||||
static void Pack_ODK_ParsedLicense(ODK_Message* msg,
|
||||
ODK_ParsedLicense const* obj) {
|
||||
/* hand-coded */
|
||||
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||
ODK_Message_SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||
return;
|
||||
}
|
||||
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->pst);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||
Pack_enum(msg, obj->license_type);
|
||||
Pack_bool(msg, &obj->nonce_required);
|
||||
Pack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||
Pack_uint32_t(msg, &obj->watermarking);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.dtcp2_required);
|
||||
if (obj->dtcp2_required.dtcp2_required) {
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_0.id);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_0.extension);
|
||||
Pack_uint16_t(msg, &obj->dtcp2_required.cmi_descriptor_0.length);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_0.data);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.id);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.extension);
|
||||
Pack_uint16_t(msg, &obj->dtcp2_required.cmi_descriptor_1.length);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.data[0]);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.data[1]);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.data[2]);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.id);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.extension);
|
||||
Pack_uint16_t(msg, &obj->dtcp2_required.cmi_descriptor_2.length);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.data[0]);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.data[1]);
|
||||
Pack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.data[2]);
|
||||
}
|
||||
Pack_uint32_t(msg, &obj->key_array_length);
|
||||
size_t i;
|
||||
for (i = 0; i < (size_t)obj->key_array_length; i++) {
|
||||
Pack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Pack_ODK_ParsedLicenseV16(ODK_Message* msg,
|
||||
ODK_ParsedLicenseV16 const* obj) {
|
||||
/* hand-coded */
|
||||
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||
ODK_Message_SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||
return;
|
||||
}
|
||||
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->pst);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||
Pack_enum(msg, obj->license_type);
|
||||
Pack_bool(msg, &obj->nonce_required);
|
||||
Pack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||
Pack_uint32_t(msg, &obj->key_array_length);
|
||||
size_t i;
|
||||
for (i = 0; i < (size_t)obj->key_array_length; i++) {
|
||||
Pack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Pack_ODK_ParsedProvisioning(ODK_Message* msg,
|
||||
ODK_ParsedProvisioning const* obj) {
|
||||
Pack_enum(msg, obj->key_type);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
|
||||
}
|
||||
|
||||
/* @@ odk serialize */
|
||||
|
||||
void Pack_ODK_PreparedLicenseRequest(ODK_Message* msg,
|
||||
ODK_PreparedLicenseRequest const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
}
|
||||
|
||||
void Pack_ODK_PreparedRenewalRequest(ODK_Message* msg,
|
||||
ODK_PreparedRenewalRequest const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Pack_uint64_t(msg, &obj->playback_time);
|
||||
}
|
||||
|
||||
void Pack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedProvisioningRequest* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Pack_uint32_t(msg, &obj->device_id_length);
|
||||
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
}
|
||||
|
||||
void Pack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedRenewedProvisioningRequest* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Pack_uint32_t(msg, &obj->device_id_length);
|
||||
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
Pack_uint16_t(msg, &obj->renewal_type);
|
||||
Pack_uint32_t(msg, &obj->renewal_data_length);
|
||||
PackArray(msg, &obj->renewal_data[0], sizeof(obj->renewal_data));
|
||||
}
|
||||
|
||||
/* @@ kdo serialize */
|
||||
|
||||
void Pack_ODK_LicenseResponse(ODK_Message* msg,
|
||||
ODK_LicenseResponse const* obj) {
|
||||
Pack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
|
||||
}
|
||||
|
||||
void Pack_ODK_LicenseResponseV16(ODK_Message* msg,
|
||||
ODK_LicenseResponseV16 const* obj) {
|
||||
Pack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||
Pack_ODK_ParsedLicenseV16(msg, &obj->parsed_license);
|
||||
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||
}
|
||||
|
||||
void Pack_ODK_RenewalResponse(ODK_Message* msg,
|
||||
ODK_RenewalResponse const* obj) {
|
||||
Pack_ODK_PreparedRenewalRequest(msg, &obj->request);
|
||||
Pack_uint64_t(msg, &obj->renewal_duration_seconds);
|
||||
}
|
||||
|
||||
void Pack_ODK_ProvisioningResponse(ODK_Message* msg,
|
||||
const ODK_ProvisioningResponse* obj) {
|
||||
Pack_ODK_PreparedProvisioningRequest(msg, &obj->request);
|
||||
Pack_ODK_ParsedProvisioning(
|
||||
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
|
||||
}
|
||||
|
||||
/* @ deserialize */
|
||||
|
||||
/* @@ private deserialize */
|
||||
|
||||
static void Unpack_ODK_NonceValues(ODK_Message* msg, ODK_NonceValues* obj) {
|
||||
Unpack_uint16_t(msg, &obj->api_minor_version);
|
||||
Unpack_uint16_t(msg, &obj->api_major_version);
|
||||
Unpack_uint32_t(msg, &obj->nonce);
|
||||
Unpack_uint32_t(msg, &obj->session_id);
|
||||
}
|
||||
|
||||
void Unpack_ODK_CoreMessage(ODK_Message* msg, ODK_CoreMessage* obj) {
|
||||
Unpack_uint32_t(msg, &obj->message_type);
|
||||
Unpack_uint32_t(msg, &obj->message_length);
|
||||
Unpack_ODK_NonceValues(msg, &obj->nonce_values);
|
||||
}
|
||||
|
||||
static void Unpack_OEMCrypto_KeyObject(ODK_Message* msg,
|
||||
OEMCrypto_KeyObject* obj) {
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->key_id);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->key_data_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->key_data);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->key_control_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->key_control);
|
||||
}
|
||||
|
||||
static void Unpack_ODK_TimerLimits(ODK_Message* msg, ODK_TimerLimits* obj) {
|
||||
Unpack_bool(msg, &obj->soft_enforce_rental_duration);
|
||||
Unpack_bool(msg, &obj->soft_enforce_playback_duration);
|
||||
Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds);
|
||||
Unpack_uint64_t(msg, &obj->rental_duration_seconds);
|
||||
Unpack_uint64_t(msg, &obj->total_playback_duration_seconds);
|
||||
Unpack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
|
||||
}
|
||||
|
||||
static void Unpack_ODK_ParsedLicense(ODK_Message* msg, ODK_ParsedLicense* obj) {
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->pst);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||
obj->license_type = (OEMCrypto_LicenseType)Unpack_enum(msg);
|
||||
Unpack_bool(msg, &obj->nonce_required);
|
||||
Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||
Unpack_uint32_t(msg, &obj->watermarking);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.dtcp2_required);
|
||||
if (obj->dtcp2_required.dtcp2_required) {
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_0.id);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_0.extension);
|
||||
Unpack_uint16_t(msg, &obj->dtcp2_required.cmi_descriptor_0.length);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_0.data);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.id);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.extension);
|
||||
Unpack_uint16_t(msg, &obj->dtcp2_required.cmi_descriptor_1.length);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.data[0]);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.data[1]);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_1.data[2]);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.id);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.extension);
|
||||
Unpack_uint16_t(msg, &obj->dtcp2_required.cmi_descriptor_2.length);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.data[0]);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.data[1]);
|
||||
Unpack_uint8_t(msg, &obj->dtcp2_required.cmi_descriptor_2.data[2]);
|
||||
} else {
|
||||
obj->dtcp2_required.dtcp2_required = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_0.id = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_0.extension = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_0.length = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_0.data = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_1.id = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_1.extension = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_1.length = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_1.data[0] = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_1.data[1] = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_1.data[2] = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_2.id = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_2.extension = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_2.length = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_2.data[0] = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_2.data[1] = 0;
|
||||
obj->dtcp2_required.cmi_descriptor_2.data[2] = 0;
|
||||
}
|
||||
Unpack_uint32_t(msg, &obj->key_array_length);
|
||||
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||
ODK_Message_SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||
return;
|
||||
}
|
||||
uint32_t i;
|
||||
for (i = 0; i < obj->key_array_length; i++) {
|
||||
Unpack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Unpack_ODK_ParsedLicenseV16(ODK_Message* msg,
|
||||
ODK_ParsedLicenseV16* obj) {
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->pst);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||
obj->license_type = (OEMCrypto_LicenseType)Unpack_enum(msg);
|
||||
Unpack_bool(msg, &obj->nonce_required);
|
||||
Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||
Unpack_uint32_t(msg, &obj->key_array_length);
|
||||
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||
ODK_Message_SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||
return;
|
||||
}
|
||||
uint32_t i;
|
||||
for (i = 0; i < obj->key_array_length; i++) {
|
||||
Unpack_OEMCrypto_KeyObject(msg, &obj->key_array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Unpack_ODK_ParsedProvisioning(ODK_Message* msg,
|
||||
ODK_ParsedProvisioning* obj) {
|
||||
obj->key_type = (OEMCrypto_PrivateKeyType)Unpack_enum(msg);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
|
||||
}
|
||||
|
||||
/* @ kdo deserialize */
|
||||
|
||||
void Unpack_ODK_PreparedLicenseRequest(ODK_Message* msg,
|
||||
ODK_PreparedLicenseRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
}
|
||||
|
||||
void Unpack_ODK_PreparedRenewalRequest(ODK_Message* msg,
|
||||
ODK_PreparedRenewalRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Unpack_uint64_t(msg, &obj->playback_time);
|
||||
}
|
||||
|
||||
void Unpack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedProvisioningRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Unpack_uint32_t(msg, &obj->device_id_length);
|
||||
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
}
|
||||
|
||||
void Unpack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedRenewedProvisioningRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Unpack_uint32_t(msg, &obj->device_id_length);
|
||||
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
|
||||
Unpack_uint16_t(msg, &obj->renewal_type);
|
||||
Unpack_uint32_t(msg, &obj->renewal_data_length);
|
||||
UnpackArray(msg, &obj->renewal_data[0], obj->renewal_data_length);
|
||||
}
|
||||
|
||||
void Unpack_ODK_PreparedCommonRequest(ODK_Message* msg,
|
||||
ODK_PreparedCommonRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
}
|
||||
/* @@ odk deserialize */
|
||||
|
||||
void Unpack_ODK_LicenseResponse(ODK_Message* msg, ODK_LicenseResponse* obj) {
|
||||
Unpack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||
Unpack_ODK_ParsedLicense(msg, obj->parsed_license);
|
||||
}
|
||||
|
||||
void Unpack_ODK_LicenseResponseV16(ODK_Message* msg,
|
||||
ODK_LicenseResponseV16* obj) {
|
||||
Unpack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||
Unpack_ODK_ParsedLicenseV16(msg, &obj->parsed_license);
|
||||
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||
}
|
||||
|
||||
void Unpack_ODK_RenewalResponse(ODK_Message* msg, ODK_RenewalResponse* obj) {
|
||||
Unpack_ODK_PreparedRenewalRequest(msg, &obj->request);
|
||||
Unpack_uint64_t(msg, &obj->renewal_duration_seconds);
|
||||
}
|
||||
|
||||
void Unpack_ODK_ProvisioningResponse(ODK_Message* msg,
|
||||
ODK_ProvisioningResponse* obj) {
|
||||
Unpack_ODK_PreparedProvisioningRequest(msg, &obj->request);
|
||||
Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning);
|
||||
}
|
||||
61
oemcrypto/odk/src/odk_serialize.h
Normal file
61
oemcrypto/odk/src/odk_serialize.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
/*
|
||||
* This code is auto-generated, do not edit
|
||||
*/
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_
|
||||
|
||||
#include "odk_structs_priv.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* odk pack */
|
||||
void Pack_ODK_PreparedLicenseRequest(ODK_Message* msg,
|
||||
const ODK_PreparedLicenseRequest* obj);
|
||||
void Pack_ODK_PreparedRenewalRequest(ODK_Message* msg,
|
||||
const ODK_PreparedRenewalRequest* obj);
|
||||
void Pack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedProvisioningRequest* obj);
|
||||
void Pack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedRenewedProvisioningRequest* obj);
|
||||
|
||||
/* odk unpack */
|
||||
void Unpack_ODK_CoreMessage(ODK_Message* msg, ODK_CoreMessage* obj);
|
||||
void Unpack_ODK_LicenseResponse(ODK_Message* msg, ODK_LicenseResponse* obj);
|
||||
void Unpack_ODK_LicenseResponseV16(ODK_Message* msg,
|
||||
ODK_LicenseResponseV16* obj);
|
||||
void Unpack_ODK_RenewalResponse(ODK_Message* msg, ODK_RenewalResponse* obj);
|
||||
void Unpack_ODK_ProvisioningResponse(ODK_Message* msg,
|
||||
ODK_ProvisioningResponse* obj);
|
||||
|
||||
/* kdo pack */
|
||||
void Pack_ODK_LicenseResponse(ODK_Message* msg, const ODK_LicenseResponse* obj);
|
||||
void Pack_ODK_LicenseResponseV16(ODK_Message* msg,
|
||||
const ODK_LicenseResponseV16* obj);
|
||||
void Pack_ODK_RenewalResponse(ODK_Message* msg, const ODK_RenewalResponse* obj);
|
||||
void Pack_ODK_ProvisioningResponse(ODK_Message* msg,
|
||||
const ODK_ProvisioningResponse* obj);
|
||||
|
||||
/* kdo unpack */
|
||||
void Unpack_ODK_PreparedLicenseRequest(ODK_Message* msg,
|
||||
ODK_PreparedLicenseRequest* obj);
|
||||
void Unpack_ODK_PreparedRenewalRequest(ODK_Message* msg,
|
||||
ODK_PreparedRenewalRequest* obj);
|
||||
void Unpack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedProvisioningRequest* obj);
|
||||
void Unpack_ODK_PreparedRenewedProvisioningRequest(
|
||||
ODK_Message* msg, ODK_PreparedRenewedProvisioningRequest* obj);
|
||||
|
||||
void Unpack_ODK_PreparedCommonRequest(ODK_Message* msg,
|
||||
ODK_PreparedCommonRequest* obj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_
|
||||
139
oemcrypto/odk/src/odk_structs_priv.h
Normal file
139
oemcrypto/odk/src/odk_structs_priv.h
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_structs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// We use a typedef here so `ODK_CoreMessage` will contain "simple types" which
|
||||
// should work better with the auto code generator.
|
||||
typedef uint32_t ODK_MessageType;
|
||||
|
||||
#define ODK_License_Request_Type ((ODK_MessageType)1u)
|
||||
#define ODK_License_Response_Type ((ODK_MessageType)2u)
|
||||
#define ODK_Renewal_Request_Type ((ODK_MessageType)3u)
|
||||
#define ODK_Renewal_Response_Type ((ODK_MessageType)4u)
|
||||
#define ODK_Provisioning_Request_Type ((ODK_MessageType)5u)
|
||||
#define ODK_Provisioning_Response_Type ((ODK_MessageType)6u)
|
||||
#define ODK_Renewed_Provisioning_Request_Type ((ODK_MessageType)11u)
|
||||
|
||||
// Reserve future message types to support forward compatibility.
|
||||
#define ODK_Release_Request_Type ((ODK_MessageType)7u)
|
||||
#define ODK_Release_Response_Type ((ODK_MessageType)8u)
|
||||
#define ODK_Common_Request_Type ((ODK_MessageType)9u)
|
||||
#define ODK_Common_Response_Type ((ODK_MessageType)10u)
|
||||
|
||||
typedef struct {
|
||||
ODK_MessageType message_type; // Type of core message (defined above)
|
||||
uint32_t message_length; // Length of core message.
|
||||
ODK_NonceValues nonce_values;
|
||||
} ODK_CoreMessage;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
} ODK_PreparedLicenseRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
uint64_t playback_time;
|
||||
} ODK_PreparedRenewalRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
uint32_t device_id_length;
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||
} ODK_PreparedProvisioningRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
uint32_t device_id_length;
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||
uint16_t renewal_type;
|
||||
uint32_t renewal_data_length;
|
||||
uint8_t renewal_data[ODK_KEYBOX_RENEWAL_DATA_SIZE];
|
||||
} ODK_PreparedRenewedProvisioningRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
} ODK_PreparedCommonRequest;
|
||||
|
||||
typedef struct {
|
||||
OEMCrypto_Substring enc_mac_keys_iv;
|
||||
OEMCrypto_Substring enc_mac_keys;
|
||||
OEMCrypto_Substring pst;
|
||||
OEMCrypto_Substring srm_restriction_data;
|
||||
OEMCrypto_LicenseType license_type;
|
||||
bool nonce_required;
|
||||
ODK_TimerLimits timer_limits;
|
||||
uint32_t key_array_length;
|
||||
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
|
||||
} ODK_ParsedLicenseV16;
|
||||
|
||||
typedef struct {
|
||||
ODK_PreparedLicenseRequest request;
|
||||
ODK_ParsedLicense* parsed_license;
|
||||
} ODK_LicenseResponse;
|
||||
|
||||
typedef struct {
|
||||
ODK_PreparedLicenseRequest request;
|
||||
ODK_ParsedLicenseV16 parsed_license;
|
||||
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
|
||||
} ODK_LicenseResponseV16;
|
||||
|
||||
typedef struct {
|
||||
ODK_PreparedRenewalRequest request;
|
||||
uint64_t renewal_duration_seconds;
|
||||
} ODK_RenewalResponse;
|
||||
|
||||
typedef struct {
|
||||
ODK_PreparedProvisioningRequest request;
|
||||
ODK_ParsedProvisioning* parsed_provisioning;
|
||||
} ODK_ProvisioningResponse;
|
||||
|
||||
// These are the sum of sizeof of each individual member of the request structs
|
||||
// without any padding added by the compiler. Make sure they get updated when
|
||||
// request structs change. Refer to test suite OdkSizeTest in
|
||||
// ../test/odk_test.cpp for validations of each of the defined request sizes.
|
||||
#define ODK_LICENSE_REQUEST_SIZE 20u
|
||||
#define ODK_RENEWAL_REQUEST_SIZE 28u
|
||||
#define ODK_PROVISIONING_REQUEST_SIZE 88u
|
||||
#define ODK_RENEWED_PROVISIONING_REQUEST_SIZE 1694u
|
||||
|
||||
// These are the possible timer status values.
|
||||
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0u // Should not happen.
|
||||
// When the structure has been initialized, but no license is loaded.
|
||||
#define ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED 1u
|
||||
// After the license is loaded, before a successful decrypt.
|
||||
#define ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED 2u
|
||||
// After the license is loaded, if a renewal has also been loaded.
|
||||
#define ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED 3u
|
||||
// The first decrypt has occurred and the timer is active.
|
||||
#define ODK_CLOCK_TIMER_STATUS_ACTIVE 4u
|
||||
// The first decrypt has occurred and the timer is unlimited.
|
||||
#define ODK_CLOCK_TIMER_STATUS_UNLIMITED 5u
|
||||
// The timer has transitioned from active to expired.
|
||||
#define ODK_CLOCK_TIMER_STATUS_EXPIRED 6u
|
||||
// The license has been marked as inactive.
|
||||
#define ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE 7u
|
||||
|
||||
// A helper function for computing timer limits when a renewal is loaded.
|
||||
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds,
|
||||
uint64_t new_renewal_duration,
|
||||
uint64_t* timer_value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_
|
||||
503
oemcrypto/odk/src/odk_timer.c
Normal file
503
oemcrypto/odk/src/odk_timer.c
Normal file
@@ -0,0 +1,503 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "odk.h"
|
||||
#include "odk_attributes.h"
|
||||
#include "odk_overflow.h"
|
||||
#include "odk_structs_priv.h"
|
||||
|
||||
/* Private function. Checks to see if the license is active. Returns
|
||||
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
|
||||
* OEMCrypto_SUCCESS if the license is active. Returns
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. */
|
||||
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values) {
|
||||
/* Check some basic errors. */
|
||||
if (clock_values == NULL || timer_limits == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
/* Check if the license has not been loaded yet. */
|
||||
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_UNDEFINED ||
|
||||
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (clock_values->status > kActive) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* Private function. Sets the timer_value to be the min(timer_value, new_value),
|
||||
* with the convention that 0 means infinite. The convention that 0 means
|
||||
* infinite is used for all Widevine license and duration values. */
|
||||
static void ComputeMinimum(uint64_t* timer_value, uint64_t new_value) {
|
||||
if (timer_value == NULL) return;
|
||||
if (new_value > 0) {
|
||||
if (*timer_value == 0 || *timer_value > new_value) {
|
||||
*timer_value = new_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Private function. Check to see if the rental window restricts playback. If
|
||||
* the rental enforcement is hard, or if this is the first playback, then we
|
||||
* verify that system_time_seconds is within the rental window. If the
|
||||
* enforcement is soft and we have already started playback, then there is no
|
||||
* restriction.
|
||||
* Return ODK_TIMER_EXPIRED if out of the window.
|
||||
* Return ODK_TIMER_ACTIVE if within the window, and there is a hard limit.
|
||||
* Return ODK_DISABLE_TIMER if no there should be no limit.
|
||||
* Return other error on error.
|
||||
* Also, if this function does compute a limit, the timer_value is reduced to
|
||||
* obey that limit. If the limit is less restrictive than the current
|
||||
* timer_value, then timer_value is not changed. */
|
||||
static OEMCryptoResult ODK_CheckRentalWindow(
|
||||
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds, uint64_t* timer_value) {
|
||||
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
/* If playback has already started, and rental duration enforcement is soft,
|
||||
* then there is no restriction. */
|
||||
if (clock_values->time_of_first_decrypt > 0 &&
|
||||
timer_limits->soft_enforce_rental_duration) {
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
|
||||
/* rental_clock = time since license signed. */
|
||||
uint64_t rental_clock = 0;
|
||||
if (odk_sub_overflow_u64(system_time_seconds,
|
||||
clock_values->time_of_license_request_signed,
|
||||
&rental_clock)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
/* Check if it is before license is valid. This is an unusual case. First
|
||||
* playback may still work if it occurs after the rental window opens. */
|
||||
if (rental_clock < timer_limits->earliest_playback_start_seconds) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* If the rental duration is 0, there is no limit. */
|
||||
if (timer_limits->rental_duration_seconds == 0) {
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
/* End of rental window, based on rental clock (not system time). */
|
||||
uint64_t end_of_rental_window = 0;
|
||||
if (odk_add_overflow_u64(timer_limits->earliest_playback_start_seconds,
|
||||
timer_limits->rental_duration_seconds,
|
||||
&end_of_rental_window)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (end_of_rental_window <= rental_clock) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* At this point system_time is within the rental window. */
|
||||
if (timer_limits->soft_enforce_rental_duration) {
|
||||
/* For soft enforcement, we allow playback, and do not adjust the timer. */
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
uint64_t time_left = 0;
|
||||
if (odk_sub_overflow_u64(end_of_rental_window, rental_clock, &time_left)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ComputeMinimum(timer_value, time_left);
|
||||
return ODK_SET_TIMER;
|
||||
}
|
||||
|
||||
/* Private function. Check to see if the playback window restricts
|
||||
* playback. This should only be called if playback has started, so that
|
||||
* clock_values->time_of_first_decrypt is nonzero.
|
||||
* Return ODK_TIMER_EXPIRED if out of the window.
|
||||
* Return ODK_SET_TIMER if within the window, and there is a hard limit.
|
||||
* Return ODK_DISABLE_TIMER if no limit.
|
||||
* Return other error on error.
|
||||
* Also, if this function does compute a limit, the timer_value is reduced to
|
||||
* obey that limit. If the limit is less restrictive than the current
|
||||
* timer_value, then timer_value is not changed. */
|
||||
static OEMCryptoResult ODK_CheckPlaybackWindow(
|
||||
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds, uint64_t* timer_value) {
|
||||
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
/* if the playback duration is 0, there is no limit. */
|
||||
if (timer_limits->total_playback_duration_seconds == 0) {
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
uint64_t end_of_playback_window = 0;
|
||||
if (odk_add_overflow_u64(timer_limits->total_playback_duration_seconds,
|
||||
clock_values->time_of_first_decrypt,
|
||||
&end_of_playback_window)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (end_of_playback_window <= system_time_seconds) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* At this point, system_time is within the total playback window. */
|
||||
if (timer_limits->soft_enforce_playback_duration) {
|
||||
/* For soft enforcement, we allow playback, and do not adjust the timer. */
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
uint64_t time_left = 0;
|
||||
if (odk_sub_overflow_u64(end_of_playback_window, system_time_seconds,
|
||||
&time_left)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
ComputeMinimum(timer_value, time_left);
|
||||
return ODK_SET_TIMER;
|
||||
}
|
||||
|
||||
/* Update the timer status. If playback has already started, we use the given
|
||||
* status. However, if playback has not yet started, then we expect a call to
|
||||
* ODK_AttemptFirstPlayback in the future, and we need to signal to it that we
|
||||
* have already computed the timer limit. */
|
||||
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
|
||||
uint32_t new_status) {
|
||||
if (clock_values == NULL) {
|
||||
return; /* should not happen. */
|
||||
}
|
||||
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) {
|
||||
/* Signal that the timer is already set. */
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED;
|
||||
} else {
|
||||
clock_values->timer_status = new_status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Private function, but accessed from odk.c so cannot be static. This checks to
|
||||
* see if a renewal message should restart the playback timer and sets the value
|
||||
* appropriately. */
|
||||
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds,
|
||||
uint64_t new_renewal_duration,
|
||||
uint64_t* timer_value) {
|
||||
if (timer_limits == NULL || clock_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */
|
||||
}
|
||||
/* If this is before the license was signed, something is odd. Return an
|
||||
* error. */
|
||||
if (system_time_seconds < clock_values->time_of_license_request_signed) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
const OEMCryptoResult license_status =
|
||||
ODK_LicenseActive(timer_limits, clock_values);
|
||||
/* If the license is not active, then we cannot renew the license. */
|
||||
if (license_status != OEMCrypto_SUCCESS) {
|
||||
return license_status;
|
||||
}
|
||||
|
||||
/* We start with the new renewal duration as the new timer limit. */
|
||||
uint64_t new_timer_value = new_renewal_duration;
|
||||
|
||||
/* Then we factor in the rental window restrictions. This might decrease
|
||||
* new_timer_value. */
|
||||
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
|
||||
timer_limits, clock_values, system_time_seconds, &new_timer_value);
|
||||
|
||||
/* If the rental status forbids playback, then we're done. */
|
||||
if ((rental_status != ODK_DISABLE_TIMER) &&
|
||||
(rental_status != ODK_SET_TIMER)) {
|
||||
return rental_status;
|
||||
}
|
||||
|
||||
/* If playback has already started and it has hard enforcement, then check
|
||||
* total playback window. */
|
||||
if (clock_values->time_of_first_decrypt > 0 &&
|
||||
!timer_limits->soft_enforce_playback_duration) {
|
||||
/* This might decrease new_timer_value. */
|
||||
const OEMCryptoResult playback_status = ODK_CheckPlaybackWindow(
|
||||
timer_limits, clock_values, system_time_seconds, &new_timer_value);
|
||||
/* If the timer limits forbid playback in the playback window, then we're
|
||||
* done. */
|
||||
if ((playback_status != ODK_DISABLE_TIMER) &&
|
||||
(playback_status != ODK_SET_TIMER)) {
|
||||
return playback_status;
|
||||
}
|
||||
}
|
||||
|
||||
/* If new_timer_value is infinite (represented by 0), then there are no
|
||||
* limits, so we can return now. */
|
||||
if (new_timer_value == 0) {
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
ODK_UpdateTimerStatusForRenewal(clock_values,
|
||||
ODK_CLOCK_TIMER_STATUS_UNLIMITED);
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
|
||||
/* If the caller gave us a pointer to store the new timer value. Fill it. */
|
||||
if (timer_value != NULL) {
|
||||
*timer_value = new_timer_value;
|
||||
}
|
||||
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
|
||||
&clock_values->time_when_timer_expires)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
ODK_UpdateTimerStatusForRenewal(clock_values, ODK_CLOCK_TIMER_STATUS_ACTIVE);
|
||||
return ODK_SET_TIMER;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/************************************************************************/
|
||||
/* Public functions, declared in odk.h. */
|
||||
|
||||
/* This is called when OEMCrypto opens a new session. */
|
||||
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
ODK_NonceValues* nonce_values,
|
||||
uint32_t api_major_version,
|
||||
uint32_t session_id) {
|
||||
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
/* Check that the API version passed in from OEMCrypto matches the version of
|
||||
* this ODK library. */
|
||||
if (api_major_version != ODK_MAJOR_VERSION) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
timer_limits->soft_enforce_rental_duration = false;
|
||||
timer_limits->soft_enforce_playback_duration = false;
|
||||
timer_limits->earliest_playback_start_seconds = 0;
|
||||
timer_limits->rental_duration_seconds = 0;
|
||||
timer_limits->total_playback_duration_seconds = 0;
|
||||
timer_limits->initial_renewal_duration_seconds = 0;
|
||||
|
||||
ODK_InitializeClockValues(clock_values, 0);
|
||||
|
||||
nonce_values->api_major_version = ODK_MAJOR_VERSION;
|
||||
nonce_values->api_minor_version = ODK_MINOR_VERSION;
|
||||
nonce_values->nonce = 0;
|
||||
nonce_values->session_id = session_id;
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called when OEMCrypto generates a new nonce in
|
||||
* OEMCrypto_GenerateNonce. */
|
||||
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
|
||||
uint32_t nonce) {
|
||||
if (nonce_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
/* Setting the nonce should only happen once per session. */
|
||||
if (nonce_values->nonce != 0) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
nonce_values->nonce = nonce;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called when OEMCrypto signs a license. */
|
||||
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds) {
|
||||
if (clock_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
clock_values->time_of_license_request_signed = system_time_seconds;
|
||||
clock_values->time_of_first_decrypt = 0;
|
||||
clock_values->time_of_last_decrypt = 0;
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
|
||||
clock_values->status = kUnused;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called when OEMCrypto reloads a usage entry. */
|
||||
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
||||
uint64_t time_of_license_request_signed,
|
||||
uint64_t time_of_first_decrypt,
|
||||
uint64_t time_of_last_decrypt,
|
||||
enum OEMCrypto_Usage_Entry_Status status,
|
||||
uint64_t system_time_seconds UNUSED) {
|
||||
if (clock_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
clock_values->time_of_license_request_signed = time_of_license_request_signed;
|
||||
clock_values->time_of_first_decrypt = time_of_first_decrypt;
|
||||
clock_values->time_of_last_decrypt = time_of_last_decrypt;
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
|
||||
clock_values->status = status;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called on the first playback for a session. */
|
||||
OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t* timer_value) {
|
||||
if (clock_values == NULL || timer_limits == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
/* All times are relative to when the license was signed. */
|
||||
uint64_t rental_time = 0;
|
||||
if (odk_sub_overflow_u64(system_time_seconds,
|
||||
clock_values->time_of_license_request_signed,
|
||||
&rental_time)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (rental_time < timer_limits->earliest_playback_start_seconds) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* If the license is inactive or not loaded, then playback is not allowed. */
|
||||
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* We start with the initial renewal duration as the timer limit. */
|
||||
uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
|
||||
/* However, if a renewal was loaded before this first playback, use the
|
||||
* previously computed limit. */
|
||||
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED) {
|
||||
if (clock_values->time_when_timer_expires <= system_time_seconds) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires,
|
||||
system_time_seconds, &new_timer_value)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Then we factor in the rental window restrictions. This might decrease
|
||||
* new_timer_value. */
|
||||
status = ODK_CheckRentalWindow(timer_limits, clock_values,
|
||||
system_time_seconds, &new_timer_value);
|
||||
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* If playback has not already started, then this is the first playback. */
|
||||
if (clock_values->time_of_first_decrypt == 0) {
|
||||
clock_values->time_of_first_decrypt = system_time_seconds;
|
||||
clock_values->status = kActive;
|
||||
}
|
||||
|
||||
/* Similar to the rental window, we check the playback window
|
||||
* restrictions. This might decrease new_timer_value. */
|
||||
status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
|
||||
system_time_seconds, &new_timer_value);
|
||||
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* We know we are allowed to decrypt. The rest computes the timer duration. */
|
||||
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||
|
||||
/* If new_timer_value is infinite (represented by 0), then there are no
|
||||
* limits, so we can return now. */
|
||||
if (new_timer_value == 0) {
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_UNLIMITED;
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
/* If the caller gave us a pointer to store the new timer value. Fill it. */
|
||||
if (timer_value) {
|
||||
*timer_value = new_timer_value;
|
||||
}
|
||||
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
|
||||
&clock_values->time_when_timer_expires)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE;
|
||||
return ODK_SET_TIMER;
|
||||
}
|
||||
|
||||
/* This is called regularly during playback if OEMCrypto does not implement its
|
||||
* own timer. */
|
||||
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values) {
|
||||
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
switch (clock_values->timer_status) {
|
||||
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
|
||||
break;
|
||||
case ODK_CLOCK_TIMER_STATUS_ACTIVE:
|
||||
/* Note: we allow playback at the time when the timer expires, but not
|
||||
* after. This is not important for business cases, but it makes it
|
||||
* easier to write tests. */
|
||||
if (clock_values->time_when_timer_expires > 0 &&
|
||||
system_time_seconds > clock_values->time_when_timer_expires) {
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
break;
|
||||
default: /* Expired, error state, or never started. */
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called from OEMCrypto_DeactivateUsageEntry. */
|
||||
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
|
||||
if (clock_values == NULL) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (clock_values->status == kUnused) {
|
||||
clock_values->status = kInactiveUnused;
|
||||
} else if (clock_values->status == kActive) {
|
||||
clock_values->status = kInactiveUsed;
|
||||
}
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called when OEMCrypto loads a legacy v15 license, from
|
||||
* OEMCrypto_LoadKeys. */
|
||||
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
ODK_NonceValues* nonce_values,
|
||||
uint32_t key_duration,
|
||||
uint64_t system_time_seconds) {
|
||||
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
timer_limits->soft_enforce_playback_duration = false;
|
||||
timer_limits->soft_enforce_rental_duration = false;
|
||||
timer_limits->earliest_playback_start_seconds = 0;
|
||||
timer_limits->rental_duration_seconds = 0;
|
||||
timer_limits->total_playback_duration_seconds = 0;
|
||||
timer_limits->initial_renewal_duration_seconds = key_duration;
|
||||
|
||||
nonce_values->api_major_version = 15;
|
||||
nonce_values->api_minor_version = 0;
|
||||
if (key_duration > 0) {
|
||||
clock_values->time_when_timer_expires = system_time_seconds + key_duration;
|
||||
} else {
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
}
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This is called when OEMCrypto loads a legacy license renewal in
|
||||
* OEMCrypto_RefreshKeys. */
|
||||
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
const ODK_NonceValues* nonce_values,
|
||||
uint64_t system_time_seconds,
|
||||
uint32_t new_key_duration,
|
||||
uint64_t* timer_value) {
|
||||
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (nonce_values->api_major_version != 15) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
if (clock_values->status > kActive) {
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
return ODK_ComputeRenewalDuration(timer_limits, clock_values,
|
||||
system_time_seconds, new_key_duration,
|
||||
timer_value);
|
||||
}
|
||||
33
oemcrypto/odk/src/odk_util.c
Normal file
33
oemcrypto/odk/src/odk_util.c
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "odk_util.h"
|
||||
|
||||
int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only valid pointers are allowed. */
|
||||
if (in_a == NULL || in_b == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint8_t* a = (const uint8_t*)in_a;
|
||||
const uint8_t* b = (const uint8_t*)in_b;
|
||||
uint8_t x = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
x |= a[i] ^ b[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
bool ODK_NonceValuesEqualExcludingVersion(const ODK_NonceValues* a,
|
||||
const ODK_NonceValues* b) {
|
||||
if (a == NULL || b == NULL) {
|
||||
return (a == b);
|
||||
}
|
||||
return (a->nonce == b->nonce && a->session_id == b->session_id);
|
||||
}
|
||||
29
oemcrypto/odk/src/odk_util.h
Normal file
29
oemcrypto/odk/src/odk_util.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_UTIL_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "odk_structs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* crypto_memcmp returns zero iff the |len| bytes at |a| and |b| are equal. It
|
||||
* takes an amount of time dependent on |len|, but independent of the contents
|
||||
* of |a| and |b|. Unlike memcmp, it cannot be used to order elements as the
|
||||
* return value when a != b is undefined, other than being non-zero. */
|
||||
int crypto_memcmp(const void* a, const void* b, size_t len);
|
||||
|
||||
bool ODK_NonceValuesEqualExcludingVersion(const ODK_NonceValues* a,
|
||||
const ODK_NonceValues* b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_UTIL_H_
|
||||
200
oemcrypto/odk/src/serialization_base.c
Normal file
200
oemcrypto/odk/src/serialization_base.c
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include "serialization_base.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_message.h"
|
||||
#include "odk_message_priv.h"
|
||||
#include "odk_overflow.h"
|
||||
|
||||
/*
|
||||
* An ODK_Message_Impl pointer must only be obtained by calling GetMessageImpl.
|
||||
* This forces any message to pass the validity check before being operated on,
|
||||
* which means that no function can modify or access the internals of a message
|
||||
* without having it be validated first.
|
||||
*/
|
||||
static ODK_Message_Impl* GetMessageImpl(ODK_Message* message) {
|
||||
if (!ODK_Message_IsValid(message)) return NULL;
|
||||
return (ODK_Message_Impl*)message;
|
||||
}
|
||||
|
||||
static void PackBytes(ODK_Message* message, const uint8_t* ptr, size_t count) {
|
||||
ODK_Message_Impl* message_impl = GetMessageImpl(message);
|
||||
if (!message_impl) return;
|
||||
if (count <= message_impl->capacity - message_impl->size) {
|
||||
memcpy((void*)(message_impl->base + message_impl->size), (const void*)ptr,
|
||||
count);
|
||||
message_impl->size += count;
|
||||
} else {
|
||||
message_impl->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
void Pack_enum(ODK_Message* message, int value) {
|
||||
uint32_t v32 = (uint32_t)value;
|
||||
Pack_uint32_t(message, &v32);
|
||||
}
|
||||
|
||||
void Pack_bool(ODK_Message* message, const bool* value) {
|
||||
assert(value);
|
||||
uint8_t data[4] = {0};
|
||||
data[3] = *value ? 1 : 0;
|
||||
PackBytes(message, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Pack_uint8_t(ODK_Message* message, const uint8_t* value) {
|
||||
assert(value);
|
||||
uint8_t data[1] = {0};
|
||||
data[0] = (uint8_t)(*value >> 0);
|
||||
PackBytes(message, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Pack_uint16_t(ODK_Message* message, const uint16_t* value) {
|
||||
assert(value);
|
||||
uint8_t data[2] = {0};
|
||||
data[0] = (uint8_t)(*value >> 8);
|
||||
data[1] = (uint8_t)(*value >> 0);
|
||||
PackBytes(message, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Pack_uint32_t(ODK_Message* message, const uint32_t* value) {
|
||||
assert(value);
|
||||
uint8_t data[4] = {0};
|
||||
data[0] = (uint8_t)(*value >> 24);
|
||||
data[1] = (uint8_t)(*value >> 16);
|
||||
data[2] = (uint8_t)(*value >> 8);
|
||||
data[3] = (uint8_t)(*value >> 0);
|
||||
PackBytes(message, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Pack_uint64_t(ODK_Message* message, const uint64_t* value) {
|
||||
assert(value);
|
||||
uint32_t hi = (uint32_t)(*value >> 32);
|
||||
uint32_t lo = (uint32_t)(*value);
|
||||
Pack_uint32_t(message, &hi);
|
||||
Pack_uint32_t(message, &lo);
|
||||
}
|
||||
|
||||
void PackArray(ODK_Message* message, const uint8_t* base, size_t size) {
|
||||
PackBytes(message, base, size);
|
||||
}
|
||||
|
||||
void Pack_OEMCrypto_Substring(ODK_Message* message,
|
||||
const OEMCrypto_Substring* obj) {
|
||||
assert(obj);
|
||||
uint32_t offset = (uint32_t)obj->offset;
|
||||
uint32_t length = (uint32_t)obj->length;
|
||||
Pack_uint32_t(message, &offset);
|
||||
Pack_uint32_t(message, &length);
|
||||
}
|
||||
|
||||
static void UnpackBytes(ODK_Message* message, uint8_t* ptr, size_t count) {
|
||||
assert(ptr);
|
||||
ODK_Message_Impl* message_impl = GetMessageImpl(message);
|
||||
if (!message_impl) return;
|
||||
if (count <= message_impl->size - message_impl->read_offset) {
|
||||
memcpy((void*)ptr, (void*)(message_impl->base + message_impl->read_offset),
|
||||
count);
|
||||
message_impl->read_offset += count;
|
||||
} else {
|
||||
message_impl->status = MESSAGE_STATUS_UNDERFLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int Unpack_enum(ODK_Message* message) {
|
||||
uint32_t v32;
|
||||
Unpack_uint32_t(message, &v32);
|
||||
return (int)v32;
|
||||
}
|
||||
|
||||
void Unpack_bool(ODK_Message* message, bool* value) {
|
||||
uint8_t data[4] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
assert(value);
|
||||
*value = (0 != data[3]);
|
||||
}
|
||||
|
||||
void Unpack_uint8_t(ODK_Message* message, uint8_t* value) {
|
||||
assert(value);
|
||||
uint8_t data[1] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
*value = data[0];
|
||||
}
|
||||
|
||||
void Unpack_uint16_t(ODK_Message* message, uint16_t* value) {
|
||||
assert(value);
|
||||
uint8_t data[2] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
*value = data[0];
|
||||
*value = *value << 8 | data[1];
|
||||
}
|
||||
|
||||
void Unpack_uint32_t(ODK_Message* message, uint32_t* value) {
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
if (!message_impl) return;
|
||||
uint8_t data[4] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
assert(value);
|
||||
*value = data[0];
|
||||
*value = *value << 8 | data[1];
|
||||
*value = *value << 8 | data[2];
|
||||
*value = *value << 8 | data[3];
|
||||
}
|
||||
|
||||
void Unpack_uint64_t(ODK_Message* message, uint64_t* value) {
|
||||
uint32_t hi = 0;
|
||||
uint32_t lo = 0;
|
||||
Unpack_uint32_t(message, &hi);
|
||||
Unpack_uint32_t(message, &lo);
|
||||
assert(value);
|
||||
*value = hi;
|
||||
*value = *value << 32 | lo;
|
||||
}
|
||||
|
||||
void Unpack_OEMCrypto_Substring(ODK_Message* message,
|
||||
OEMCrypto_Substring* obj) {
|
||||
uint32_t offset = 0, length = 0;
|
||||
Unpack_uint32_t(message, &offset);
|
||||
Unpack_uint32_t(message, &length);
|
||||
ODK_Message_Impl* message_impl = GetMessageImpl(message);
|
||||
if (!message_impl) return;
|
||||
|
||||
/* Each substring should be contained within the message body, which is in the
|
||||
* total message, just after the core message. The offset of a substring is
|
||||
* relative to the message body. So we need to verify:
|
||||
*
|
||||
* For non-empty substring:
|
||||
* offset + length < message_impl->capacity - message_impl->size or
|
||||
* offset + length + message_impl->size < message_impl->capacity
|
||||
*
|
||||
* For empty substring (length is 0):
|
||||
* offset must be 0
|
||||
*/
|
||||
if (length == 0 && offset != 0) {
|
||||
message_impl->status = MESSAGE_STATUS_UNKNOWN_ERROR;
|
||||
return;
|
||||
}
|
||||
size_t substring_end = 0; /* = offset + length; */
|
||||
size_t end = 0; /* = substring_end + message_impl->size; */
|
||||
if (odk_add_overflow_ux(offset, length, &substring_end) ||
|
||||
odk_add_overflow_ux(substring_end, message_impl->size, &end) ||
|
||||
end > message_impl->capacity) {
|
||||
message_impl->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||
return;
|
||||
}
|
||||
assert(obj);
|
||||
obj->offset = offset;
|
||||
obj->length = length;
|
||||
}
|
||||
|
||||
/* copy out */
|
||||
void UnpackArray(ODK_Message* message, uint8_t* address, size_t size) {
|
||||
UnpackBytes(message, address, size);
|
||||
}
|
||||
42
oemcrypto/odk/src/serialization_base.h
Normal file
42
oemcrypto/odk/src/serialization_base.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||
#define WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_message.h"
|
||||
|
||||
void Pack_enum(ODK_Message* message, int value);
|
||||
void Pack_bool(ODK_Message* message, const bool* value);
|
||||
void Pack_uint8_t(ODK_Message* message, const uint8_t* value);
|
||||
void Pack_uint16_t(ODK_Message* message, const uint16_t* value);
|
||||
void Pack_uint32_t(ODK_Message* message, const uint32_t* value);
|
||||
void Pack_uint64_t(ODK_Message* message, const uint64_t* value);
|
||||
void PackArray(ODK_Message* message, const uint8_t* base, size_t size);
|
||||
void Pack_OEMCrypto_Substring(ODK_Message* message,
|
||||
const OEMCrypto_Substring* obj);
|
||||
|
||||
int Unpack_enum(ODK_Message* message);
|
||||
void Unpack_bool(ODK_Message* message, bool* value);
|
||||
void Unpack_uint8_t(ODK_Message* message, uint8_t* value);
|
||||
void Unpack_uint16_t(ODK_Message* message, uint16_t* value);
|
||||
void Unpack_uint32_t(ODK_Message* message, uint32_t* value);
|
||||
void Unpack_uint64_t(ODK_Message* message, uint64_t* value);
|
||||
void UnpackArray(ODK_Message* message, uint8_t* address,
|
||||
size_t size); /* copy out */
|
||||
void Unpack_OEMCrypto_Substring(ODK_Message* message, OEMCrypto_Substring* obj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_
|
||||
Reference in New Issue
Block a user