Merge latest oemcrypto-v17 change
No-Typo-Check: Not related to this change. Bug: 161477208 Change-Id: I99e4780f6855b7045aa0cd5a49c13d2d0d51ed64
This commit is contained in:
committed by
Fred Gylys-Colwell
parent
c924960962
commit
642965c678
@@ -60,7 +60,7 @@ bool ParseRequest(uint32_t message_type,
|
||||
} 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_major_version < 2) return false;
|
||||
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.
|
||||
|
||||
41
libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp
Normal file
41
libwvdrmengine/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 = 0; // 17.0
|
||||
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
|
||||
@@ -20,18 +20,22 @@ namespace serialize {
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Template for parsing requests
|
||||
* 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
|
||||
* P: auto-generated serializing function for |T|
|
||||
*/
|
||||
template <typename T, typename S, typename P>
|
||||
bool CreateResponse(uint32_t message_type, const S& core_request,
|
||||
std::string* oemcrypto_core_message, T& response,
|
||||
const P& packer) {
|
||||
if (!oemcrypto_core_message) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -43,9 +47,36 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
|
||||
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 > ODK_MAJOR_VERSION) {
|
||||
header->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||
header->nonce_values.api_minor_version = ODK_MINOR_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;
|
||||
@@ -80,33 +111,73 @@ bool CopyDeviceId(const ODK_ProvisioningRequest& src,
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
|
||||
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), {0}};
|
||||
if (core_request_sha256.size() != sizeof(license_response.request_hash))
|
||||
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic)};
|
||||
if (!CreateResponseHeader(features, ODK_License_Response_Type, core_request,
|
||||
license_response)) {
|
||||
return false;
|
||||
memcpy(license_response.request_hash, core_request_sha256.data(),
|
||||
sizeof(license_response.request_hash));
|
||||
}
|
||||
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_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 ODK_RenewalRequest& core_request,
|
||||
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 ODK_ParsedProvisioning& parsed_prov,
|
||||
bool CreateCoreProvisioningResponse(const CoreMessageFeatures& features,
|
||||
const ODK_ParsedProvisioning& parsed_prov,
|
||||
const ODK_ProvisioningRequest& core_request,
|
||||
std::string* oemcrypto_core_message) {
|
||||
ODK_ProvisioningResponse prov_response{
|
||||
@@ -114,6 +185,10 @@ bool CreateCoreProvisioningResponse(const 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);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
namespace oemcrypto_core_message {
|
||||
namespace serialize {
|
||||
namespace {
|
||||
using oemcrypto_core_message::features::CoreMessageFeatures;
|
||||
|
||||
/* @ private functions */
|
||||
|
||||
@@ -41,17 +42,23 @@ OEMCrypto_Substring GetOecSubstring(const std::string& message,
|
||||
}
|
||||
|
||||
OEMCrypto_KeyObject KeyContainerToOecKey(
|
||||
const std::string& proto, const video_widevine::License::KeyContainer& k) {
|
||||
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());
|
||||
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
|
||||
// the padding will always be 16 bytes.
|
||||
const std::string& key_data = k.key();
|
||||
const size_t PKCS5_PADDING_SIZE = 16;
|
||||
obj.key_data = GetOecSubstring(
|
||||
proto, key_data.substr(0, std::max(PKCS5_PADDING_SIZE, key_data.size()) -
|
||||
PKCS5_PADDING_SIZE));
|
||||
|
||||
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());
|
||||
@@ -64,10 +71,12 @@ OEMCrypto_KeyObject KeyContainerToOecKey(
|
||||
|
||||
// @ public create response functions
|
||||
|
||||
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
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)) {
|
||||
@@ -102,7 +111,8 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
return false;
|
||||
}
|
||||
uint32_t& n = parsed_lic.key_array_length;
|
||||
parsed_lic.key_array[n++] = KeyContainerToOecKey(serialized_license, k);
|
||||
parsed_lic.key_array[n++] =
|
||||
KeyContainerToOecKey(serialized_license, k, uses_padding);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -125,12 +135,13 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
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;
|
||||
@@ -146,11 +157,12 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
policy.renewal_delay_seconds() +
|
||||
policy.renewal_recovery_duration_seconds();
|
||||
|
||||
return CreateCoreLicenseResponse(parsed_lic, core_request,
|
||||
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) {
|
||||
@@ -175,7 +187,7 @@ bool CreateCoreProvisioningResponseFromProto(
|
||||
GetOecSubstring(serialized_provisioning_resp, prov.wrapping_key());
|
||||
}
|
||||
|
||||
return CreateCoreProvisioningResponse(parsed_prov, core_request,
|
||||
return CreateCoreProvisioningResponse(features, parsed_prov, core_request,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,64 +91,65 @@ static OEMCryptoResult ODK_PrepareRequest(
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult ODK_ParseResponse(
|
||||
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||
ODK_MessageType message_type, const ODK_NonceValues* nonce_values,
|
||||
void* response_buffer, uint32_t response_buffer_length) {
|
||||
if (message == NULL || response_buffer == NULL ||
|
||||
core_message_length > message_length) {
|
||||
/* 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, and with a
|
||||
* shorter length. The core message is the part we are parsing. */
|
||||
/* 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);
|
||||
|
||||
/* Parse message and unpack it into response buffer. */
|
||||
switch (message_type) {
|
||||
case ODK_License_Response_Type: {
|
||||
if (sizeof(ODK_LicenseResponse) > response_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Unpack_ODK_LicenseResponse(&msg, (ODK_LicenseResponse*)response_buffer);
|
||||
break;
|
||||
}
|
||||
case ODK_Renewal_Response_Type: {
|
||||
if (sizeof(ODK_RenewalResponse) > response_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Unpack_ODK_RenewalResponse(&msg, (ODK_RenewalResponse*)response_buffer);
|
||||
break;
|
||||
}
|
||||
case ODK_Provisioning_Response_Type: {
|
||||
if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
Unpack_ODK_ProvisioningResponse(
|
||||
&msg, (ODK_ProvisioningResponse*)response_buffer);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
ODK_CoreMessage* core_message = (ODK_CoreMessage*)response_buffer;
|
||||
if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK ||
|
||||
message_type != core_message->message_type ||
|
||||
ODK_Message_GetOffset(&msg) != core_message->message_length) {
|
||||
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) {
|
||||
/* always verify nonce_values for Renewal and Provisioning responses */
|
||||
if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -162,9 +163,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedLicenseRequest license_request = {
|
||||
{0, 0, {}},
|
||||
};
|
||||
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));
|
||||
@@ -192,7 +191,7 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
ODK_PreparedRenewalRequest renewal_request = {{0, 0, {}}, 0};
|
||||
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) {
|
||||
@@ -225,11 +224,7 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||
if (core_message_length == NULL || nonce_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
ODK_PreparedProvisioningRequest provisioning_request = {
|
||||
{0, 0, {}},
|
||||
0,
|
||||
{0},
|
||||
};
|
||||
ODK_PreparedProvisioningRequest provisioning_request = {0};
|
||||
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
@@ -248,47 +243,88 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||
OEMCryptoResult ODK_ParseLicense(
|
||||
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||
bool initial_license_load, bool usage_entry_present,
|
||||
const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
|
||||
ODK_ParsedLicense* parsed_license) {
|
||||
if (message == NULL || request_hash == NULL || timer_limits == NULL ||
|
||||
clock_values == NULL || nonce_values == NULL || parsed_license == NULL) {
|
||||
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;
|
||||
}
|
||||
|
||||
ODK_LicenseResponse license_response = {{{0, 0, {}}}, NULL, {0}};
|
||||
license_response.parsed_license = parsed_license;
|
||||
|
||||
const OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, core_message_length, ODK_License_Response_Type,
|
||||
NULL, &license_response, sizeof(ODK_LicenseResponse));
|
||||
|
||||
const OEMCryptoResult err =
|
||||
ODK_ParseCoreHeader(message, message_length, core_message_length,
|
||||
ODK_License_Response_Type, nonce_values);
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* We do not support future API version. Also, this function should not be
|
||||
* used for legacy licenses. */
|
||||
if (license_response.request.core_message.nonce_values.api_major_version >
|
||||
ODK_MAJOR_VERSION ||
|
||||
license_response.request.core_message.nonce_values.api_major_version <
|
||||
ODK_FIRST_VERSION) {
|
||||
return ODK_UNSUPPORTED_API;
|
||||
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 server sent us an older format, record the license's API version. */
|
||||
if (nonce_values->api_major_version >
|
||||
license_response.request.core_message.nonce_values.api_major_version) {
|
||||
nonce_values->api_major_version =
|
||||
license_response.request.core_message.nonce_values.api_major_version;
|
||||
nonce_values->api_minor_version =
|
||||
license_response.request.core_message.nonce_values.api_minor_version;
|
||||
} else if (nonce_values->api_minor_version >
|
||||
license_response.request.core_message.nonce_values
|
||||
.api_minor_version) {
|
||||
nonce_values->api_minor_version =
|
||||
license_response.request.core_message.nonce_values.api_minor_version;
|
||||
}
|
||||
/* 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) ||
|
||||
@@ -315,15 +351,6 @@ OEMCryptoResult ODK_ParseLicense(
|
||||
nonce_values->session_id =
|
||||
license_response.request.core_message.nonce_values.session_id;
|
||||
}
|
||||
/* For v16, in order to be backwards compatible with a v15 license server,
|
||||
* OEMCrypto stores a hash of the core license request and only signs the
|
||||
* message body. Here, when we process the license response, we verify that
|
||||
* the server has the same hash of the core request. */
|
||||
if (initial_license_load && parsed_license->nonce_required &&
|
||||
crypto_memcmp(request_hash, license_response.request_hash,
|
||||
ODK_SHA256_HASH_SIZE)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
*timer_limits = parsed_license->timer_limits;
|
||||
/* And update the clock values state. */
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
|
||||
@@ -342,17 +369,27 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_RenewalResponse renewal_response = {
|
||||
{{0, 0, {}}, 0},
|
||||
0,
|
||||
};
|
||||
const OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, core_message_length, ODK_Renewal_Response_Type,
|
||||
nonce_values, &renewal_response, sizeof(ODK_RenewalResponse));
|
||||
|
||||
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)
|
||||
@@ -381,21 +418,31 @@ OEMCryptoResult ODK_ParseProvisioning(
|
||||
parsed_response == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_ProvisioningResponse provisioning_response = {{{0, 0, {}}, 0, {0}}, NULL};
|
||||
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;
|
||||
}
|
||||
|
||||
const OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, core_message_length,
|
||||
ODK_Provisioning_Response_Type, nonce_values, &provisioning_response,
|
||||
sizeof(ODK_ProvisioningResponse));
|
||||
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
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,
|
||||
@@ -413,3 +460,10 @@ OEMCryptoResult ODK_ParseProvisioning(
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
* source code may only be used and distributed under the Widevine
|
||||
* License Agreement.
|
||||
*/
|
||||
// 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 "odk_message_priv.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
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
* source code may only be used and distributed under the Widevine
|
||||
* License Agreement.
|
||||
*/
|
||||
// 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_INCLUDE_ODK_MESSAGE_PRIV_H_
|
||||
#define WIDEVINE_ODK_INCLUDE_ODK_MESSAGE_PRIV_H_
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_MESSAGE_PRIV_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_MESSAGE_PRIV_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -18,6 +16,8 @@ extern "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
|
||||
@@ -38,4 +38,4 @@ typedef struct {
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WIDEVINE_ODK_INCLUDE_ODK_MESSAGE_PRIV_H_
|
||||
#endif // WIDEVINE_ODK_SRC_ODK_MESSAGE_PRIV_H_
|
||||
|
||||
@@ -58,6 +58,47 @@ static void Pack_ODK_ParsedLicense(ODK_Message* msg,
|
||||
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++) {
|
||||
@@ -99,6 +140,12 @@ 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));
|
||||
}
|
||||
|
||||
@@ -126,7 +173,7 @@ static void Unpack_ODK_NonceValues(ODK_Message* msg, ODK_NonceValues* obj) {
|
||||
Unpack_uint32_t(msg, &obj->session_id);
|
||||
}
|
||||
|
||||
static void Unpack_ODK_CoreMessage(ODK_Message* msg, ODK_CoreMessage* obj) {
|
||||
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);
|
||||
@@ -158,6 +205,64 @@ static void Unpack_ODK_ParsedLicense(ODK_Message* msg, ODK_ParsedLicense* obj) {
|
||||
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);
|
||||
@@ -206,6 +311,12 @@ void Unpack_ODK_PreparedCommonRequest(ODK_Message* msg,
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,18 @@ void Pack_ODK_PreparedProvisioningRequest(
|
||||
ODK_Message* msg, const ODK_PreparedProvisioningRequest* 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);
|
||||
|
||||
@@ -14,24 +14,26 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ODK_License_Request_Type = 1,
|
||||
ODK_License_Response_Type = 2,
|
||||
ODK_Renewal_Request_Type = 3,
|
||||
ODK_Renewal_Response_Type = 4,
|
||||
ODK_Provisioning_Request_Type = 5,
|
||||
ODK_Provisioning_Response_Type = 6,
|
||||
// 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;
|
||||
|
||||
// Reserve future message types to support forward compatibility.
|
||||
ODK_Release_Request_Type = 7,
|
||||
ODK_Release_Response_Type = 8,
|
||||
ODK_Common_Request_Type = 9,
|
||||
ODK_Common_Response_Type = 10,
|
||||
} 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)
|
||||
|
||||
// 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 {
|
||||
uint32_t message_type;
|
||||
uint32_t message_length;
|
||||
ODK_MessageType message_type; // Type of core message (defined above)
|
||||
uint32_t message_length; // Length of core message.
|
||||
ODK_NonceValues nonce_values;
|
||||
} ODK_CoreMessage;
|
||||
|
||||
@@ -54,12 +56,29 @@ 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;
|
||||
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
|
||||
} 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;
|
||||
|
||||
@@ -71,7 +71,7 @@ static OEMCryptoResult ODK_CheckRentalWindow(
|
||||
/* rental_clock = time since license signed. */
|
||||
uint64_t rental_clock = 0;
|
||||
if (odk_sub_overflow_u64(system_time_seconds,
|
||||
clock_values->time_of_license_signed,
|
||||
clock_values->time_of_license_request_signed,
|
||||
&rental_clock)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
||||
}
|
||||
/* If this is before the license was signed, something is odd. Return an
|
||||
* error. */
|
||||
if (system_time_seconds < clock_values->time_of_license_signed) {
|
||||
if (system_time_seconds < clock_values->time_of_license_request_signed) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||
if (clock_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
clock_values->time_of_license_signed = system_time_seconds;
|
||||
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;
|
||||
@@ -308,7 +308,7 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||
|
||||
/* This is called when OEMCrypto reloads a usage entry. */
|
||||
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
||||
uint64_t time_of_license_signed,
|
||||
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,
|
||||
@@ -316,7 +316,7 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
||||
if (clock_values == NULL) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
clock_values->time_of_license_signed = time_of_license_signed;
|
||||
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;
|
||||
@@ -336,7 +336,7 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
||||
/* 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_signed,
|
||||
clock_values->time_of_license_request_signed,
|
||||
&rental_time)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
@@ -24,11 +24,10 @@ int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
|
||||
return x;
|
||||
}
|
||||
|
||||
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b) {
|
||||
bool ODK_NonceValuesEqualExcludingVersion(const ODK_NonceValues* a,
|
||||
const ODK_NonceValues* b) {
|
||||
if (a == NULL || b == NULL) {
|
||||
return (a == b);
|
||||
}
|
||||
return (a->api_major_version == b->api_major_version &&
|
||||
a->api_minor_version == b->api_minor_version &&
|
||||
a->nonce == b->nonce && a->session_id == b->session_id);
|
||||
return (a->nonce == b->nonce && a->session_id == b->session_id);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ extern "C" {
|
||||
* 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_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b);
|
||||
bool ODK_NonceValuesEqualExcludingVersion(const ODK_NonceValues* a,
|
||||
const ODK_NonceValues* b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -49,6 +49,13 @@ void Pack_bool(ODK_Message* message, const bool* value) {
|
||||
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};
|
||||
@@ -114,6 +121,13 @@ void Unpack_bool(ODK_Message* message, bool* 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};
|
||||
@@ -123,7 +137,7 @@ void Unpack_uint16_t(ODK_Message* message, uint16_t* value) {
|
||||
}
|
||||
|
||||
void Unpack_uint32_t(ODK_Message* message, uint32_t* value) {
|
||||
ODK_Message_Impl* message_impl = GetMessageImpl(message);
|
||||
ODK_Message_Impl* message_impl = (ODK_Message_Impl*)message;
|
||||
if (!message_impl) return;
|
||||
uint8_t data[4] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
@@ -155,10 +169,18 @@ void Unpack_OEMCrypto_Substring(ODK_Message* message,
|
||||
/* 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:
|
||||
* 0 < offset and offset + length < message_impl->capacity -
|
||||
* message_impl->size or offset + length + message_impl->size <
|
||||
* message_impl->capacity
|
||||
*
|
||||
* 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) ||
|
||||
|
||||
@@ -17,20 +17,23 @@ extern "C" {
|
||||
|
||||
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* msg, const OEMCrypto_Substring* obj);
|
||||
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* msg, OEMCrypto_Substring* obj);
|
||||
void Unpack_OEMCrypto_Substring(ODK_Message* message, OEMCrypto_Substring* obj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
Reference in New Issue
Block a user