OEMCrypto v16.2
Merge from Widevine repo of http://go/wvgerrit/93404 This is the unit tests, reference code, and documentation for OEMCrypto v16.2. Backwards compatibility should work for a v15 OEMCrypto. Some review comments will be addressed in future CLs. Bug: 141247171 Test: Unit tests Test: Media GTS tests on bonito Change-Id: I9d427c07580e180c0a4cfdc4a68f538d351c0ddd
This commit is contained in:
@@ -19,8 +19,8 @@ namespace oemcrypto_core_message {
|
||||
namespace deserialize {
|
||||
namespace {
|
||||
|
||||
const int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16;
|
||||
const int LATEST_OEMCRYPTO_VERSION = 16;
|
||||
constexpr int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16;
|
||||
constexpr int LATEST_OEMCRYPTO_VERSION = 16;
|
||||
|
||||
/**
|
||||
* Template for parsing requests
|
||||
@@ -53,29 +53,43 @@ bool ParseRequest(uint32_t message_type,
|
||||
}
|
||||
|
||||
const auto& core_message = prepared->core_message;
|
||||
core_request->api_version = core_message.nonce_values.api_version;
|
||||
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 < EARLIEST_OEMCRYPTO_VERSION_WITH_ODK) ||
|
||||
(core_request->api_major_version > LATEST_OEMCRYPTO_VERSION)) {
|
||||
// Non existing and future 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.
|
||||
if (core_request->api_major_version < 2) return false;
|
||||
} else {
|
||||
// Other versions do not (yet) have a restriction on minor number.
|
||||
}
|
||||
return core_message.message_type == message_type &&
|
||||
core_message.message_length == GetOffset(msg) &&
|
||||
core_request->api_version >= EARLIEST_OEMCRYPTO_VERSION_WITH_ODK &&
|
||||
core_request->api_version <= LATEST_OEMCRYPTO_VERSION;
|
||||
core_request->api_major_version >=
|
||||
EARLIEST_OEMCRYPTO_VERSION_WITH_ODK &&
|
||||
core_request->api_major_version <= LATEST_OEMCRYPTO_VERSION;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||
ODK_LicenseRequest* core_license_request) {
|
||||
const auto unpacker = Unpack_ODK_PreparedLicense;
|
||||
ODK_PreparedLicense prepared_license = {};
|
||||
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_RenewalMessage;
|
||||
ODK_RenewalMessage prepared_renewal = {};
|
||||
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;
|
||||
@@ -87,8 +101,8 @@ bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
|
||||
bool CoreProvisioningRequestFromMessage(
|
||||
const std::string& oemcrypto_core_message,
|
||||
ODK_ProvisioningRequest* core_provisioning_request) {
|
||||
const auto unpacker = Unpack_ODK_ProvisioningMessage;
|
||||
ODK_ProvisioningMessage prepared_provision = {};
|
||||
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;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "core_message_serialize.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@@ -38,11 +37,12 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
|
||||
|
||||
auto* header = reinterpret_cast<ODK_CoreMessage*>(&response);
|
||||
header->message_type = message_type;
|
||||
header->nonce_values.api_version = core_request.api_version;
|
||||
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;
|
||||
|
||||
const size_t BUF_CAPACITY = 2048;
|
||||
static constexpr size_t BUF_CAPACITY = 2048;
|
||||
std::vector<uint8_t> buf(BUF_CAPACITY, 0);
|
||||
Message* msg = nullptr;
|
||||
AllocateMessage(&msg, message_block);
|
||||
@@ -63,16 +63,14 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
|
||||
|
||||
bool CopyDeviceId(const ODK_ProvisioningRequest& src,
|
||||
ODK_ProvisioningResponse* dest) {
|
||||
auto& core_provisioning = dest->core_provisioning;
|
||||
auto& request = dest->request;
|
||||
const std::string& device_id = src.device_id;
|
||||
core_provisioning.device_id_length = device_id.size();
|
||||
if (core_provisioning.device_id_length >
|
||||
sizeof(core_provisioning.device_id)) {
|
||||
if (request.device_id_length > sizeof(request.device_id)) {
|
||||
return false;
|
||||
}
|
||||
memset(core_provisioning.device_id, 0, sizeof(core_provisioning.device_id));
|
||||
memcpy(core_provisioning.device_id, device_id.data(),
|
||||
core_provisioning.device_id_length);
|
||||
request.device_id_length = 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;
|
||||
}
|
||||
|
||||
@@ -80,21 +78,28 @@ bool CopyDeviceId(const ODK_ProvisioningRequest& src,
|
||||
|
||||
bool CreateCoreLicenseResponse(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)};
|
||||
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic), {0}};
|
||||
if (core_request_sha256.size() != sizeof(license_response.request_hash))
|
||||
return false;
|
||||
memcpy(license_response.request_hash, core_request_sha256.data(),
|
||||
sizeof(license_response.request_hash));
|
||||
return CreateResponse(ODK_License_Response_Type, core_request,
|
||||
oemcrypto_core_message, license_response,
|
||||
Pack_ODK_LicenseResponse);
|
||||
}
|
||||
|
||||
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
|
||||
uint64_t renewal_duration_seconds,
|
||||
std::string* oemcrypto_core_message) {
|
||||
ODK_RenewalMessage renewal{{}, core_request.playback_time_seconds};
|
||||
renewal.playback_time = core_request.playback_time_seconds;
|
||||
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;
|
||||
return CreateResponse(ODK_Renewal_Response_Type, core_request,
|
||||
oemcrypto_core_message, renewal,
|
||||
Pack_ODK_RenewalMessage);
|
||||
oemcrypto_core_message, renewal_response,
|
||||
Pack_ODK_RenewalResponse);
|
||||
}
|
||||
|
||||
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
|
||||
|
||||
@@ -67,6 +67,7 @@ OEMCrypto_KeyObject KeyContainerToOecKey(
|
||||
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
const ODK_LicenseRequest& core_request,
|
||||
const std::string& core_request_sha256,
|
||||
const bool nonce_required,
|
||||
std::string* oemcrypto_core_message) {
|
||||
video_widevine::License lic;
|
||||
if (!lic.ParseFromString(serialized_license)) {
|
||||
@@ -74,25 +75,33 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
}
|
||||
|
||||
ODK_ParsedLicense parsed_lic{};
|
||||
if (core_request_sha256.size() != ODK_SHA256_HASH_SIZE) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(parsed_lic.request_hash, core_request_sha256.data(),
|
||||
ODK_SHA256_HASH_SIZE);
|
||||
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());
|
||||
// Strip off PKCS#5 padding
|
||||
const size_t MAC_KEY_SIZE = 32;
|
||||
std::string mac_keys(k.key(), 2 * MAC_KEY_SIZE);
|
||||
std::string mac_keys(k.key(), k.key().size());
|
||||
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, mac_keys);
|
||||
break;
|
||||
}
|
||||
case video_widevine::License_KeyContainer::CONTENT: {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case video_widevine::License_KeyContainer::ENTITLEMENT: {
|
||||
any_entitlement = true;
|
||||
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
|
||||
return false;
|
||||
}
|
||||
@@ -105,7 +114,16 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 =
|
||||
@@ -117,22 +135,25 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
||||
GetOecSubstring(serialized_license, lic.srm_requirement());
|
||||
}
|
||||
|
||||
parsed_lic.license_type = lid.type();
|
||||
// todo(robertshih): nonce_required
|
||||
parsed_lic.nonce_required = nonce_required;
|
||||
const auto& policy = lic.policy();
|
||||
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
|
||||
timer_limits.soft_expiry = policy.soft_enforce_playback_duration();
|
||||
// TODO(b/148241181): add field to protobuf.
|
||||
// timer_limits.soft_enforce_rental_duration =
|
||||
// policy.soft_enforce_rental_duration();
|
||||
timer_limits.soft_enforce_rental_duration = true;
|
||||
timer_limits.soft_enforce_playback_duration =
|
||||
policy.soft_enforce_playback_duration();
|
||||
timer_limits.earliest_playback_start_seconds = 0;
|
||||
timer_limits.latest_playback_start_seconds =
|
||||
policy.license_duration_seconds();
|
||||
timer_limits.initial_playback_duration_seconds =
|
||||
timer_limits.rental_duration_seconds = policy.rental_duration_seconds();
|
||||
timer_limits.total_playback_duration_seconds =
|
||||
policy.playback_duration_seconds();
|
||||
timer_limits.renewal_playback_duration_seconds =
|
||||
policy.playback_duration_seconds();
|
||||
timer_limits.license_duration_seconds = policy.license_duration_seconds();
|
||||
timer_limits.initial_renewal_duration_seconds =
|
||||
policy.renewal_delay_seconds() +
|
||||
policy.renewal_recovery_duration_seconds();
|
||||
|
||||
return CreateCoreLicenseResponse(parsed_lic, core_request,
|
||||
oemcrypto_core_message);
|
||||
core_request_sha256, oemcrypto_core_message);
|
||||
}
|
||||
|
||||
bool CreateCoreProvisioningResponseFromProto(
|
||||
@@ -145,7 +166,8 @@ bool CreateCoreProvisioningResponseFromProto(
|
||||
return false;
|
||||
}
|
||||
|
||||
parsed_prov.key_type = 0; // todo(robertshih): ECC or RSA
|
||||
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());
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
#include "odk_util.h"
|
||||
#include "serialization_base.h"
|
||||
|
||||
#define ODK_LICENSE_REQUEST_SIZE 20
|
||||
#define ODK_RENEWAL_REQUEST_SIZE 28
|
||||
#define ODK_PROVISIONING_REQUEST_SIZE 88
|
||||
#define OEC_API_VERSION 16
|
||||
|
||||
/* @ private odk functions */
|
||||
|
||||
@@ -26,8 +26,8 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
|
||||
uint32_t message_type,
|
||||
const ODK_NonceValues* nonce_values,
|
||||
ODK_CoreMessage* core_message) {
|
||||
if (!nonce_values || !core_message_length || !core_message ||
|
||||
*core_message_length > buffer_length) {
|
||||
if (nonce_values == NULL || core_message_length == NULL ||
|
||||
core_message == NULL || *core_message_length > buffer_length) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
@@ -43,17 +43,20 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
|
||||
switch (message_type) {
|
||||
case ODK_License_Request_Type: {
|
||||
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
|
||||
Pack_ODK_PreparedLicense(msg, (ODK_PreparedLicense*)core_message);
|
||||
Pack_ODK_PreparedLicenseRequest(
|
||||
msg, (ODK_PreparedLicenseRequest*)core_message);
|
||||
break;
|
||||
}
|
||||
case ODK_Renewal_Request_Type: {
|
||||
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
|
||||
Pack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
|
||||
Pack_ODK_PreparedRenewalRequest(
|
||||
msg, (ODK_PreparedRenewalRequest*)core_message);
|
||||
break;
|
||||
}
|
||||
case ODK_Provisioning_Request_Type: {
|
||||
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
|
||||
Pack_ODK_ProvisioningMessage(msg, (ODK_ProvisioningMessage*)core_message);
|
||||
Pack_ODK_PreparedProvisioningRequest(
|
||||
msg, (ODK_PreparedProvisioningRequest*)core_message);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -93,7 +96,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
||||
break;
|
||||
}
|
||||
case ODK_Renewal_Response_Type: {
|
||||
Unpack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
|
||||
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)core_message);
|
||||
break;
|
||||
}
|
||||
case ODK_Provisioning_Response_Type: {
|
||||
@@ -114,7 +117,10 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
||||
|
||||
if (nonce_values) {
|
||||
/* always verify nonce_values for Renewal and Provisioning responses */
|
||||
if (nonce_values->api_version != core_message->nonce_values.api_version ||
|
||||
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 ||
|
||||
nonce_values->nonce != core_message->nonce_values.nonce ||
|
||||
nonce_values->session_id != core_message->nonce_values.session_id) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
@@ -131,7 +137,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
||||
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
ODK_PreparedLicense license_request = {
|
||||
ODK_PreparedLicenseRequest license_request = {
|
||||
{0},
|
||||
};
|
||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||
@@ -139,13 +145,17 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||
&license_request.core_message);
|
||||
}
|
||||
|
||||
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
|
||||
uint8_t* message, size_t message_length, size_t* core_message_size,
|
||||
const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values,
|
||||
uint64_t system_time_seconds) {
|
||||
ODK_RenewalMessage renewal_request = {
|
||||
{0},
|
||||
};
|
||||
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 (nonce_values == NULL || clock_values == NULL)
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
ODK_PreparedRenewalRequest renewal_request = {{0}, 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. */
|
||||
@@ -158,7 +168,9 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
}
|
||||
/* Save time for this request so that we can verify the response. */
|
||||
/* 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,
|
||||
@@ -169,7 +181,9 @@ 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) {
|
||||
ODK_ProvisioningMessage provisioning_request = {
|
||||
ODK_PreparedProvisioningRequest provisioning_request = {
|
||||
{0},
|
||||
0,
|
||||
{0},
|
||||
};
|
||||
if (device_id_length > sizeof(provisioning_request.device_id)) {
|
||||
@@ -192,55 +206,61 @@ OEMCryptoResult ODK_ParseLicense(
|
||||
const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
|
||||
ODK_ParsedLicense* parsed_license) {
|
||||
if (!message || !request_hash || !timer_limits || !clock_values ||
|
||||
!nonce_values || !parsed_license) {
|
||||
if (message == NULL || request_hash == NULL || timer_limits == NULL ||
|
||||
clock_values == NULL || nonce_values == NULL || parsed_license == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_LicenseResponse license_response = {{0}, parsed_license};
|
||||
OEMCryptoResult err = ODK_ParseResponse(
|
||||
ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
|
||||
const OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, core_message_length, ODK_License_Response_Type,
|
||||
NULL, &license_response.core_message);
|
||||
NULL, &license_response.request.core_message);
|
||||
|
||||
if (err != OEMCrypto_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function should not be used for legacy licenses. */
|
||||
if (license_response.core_message.nonce_values.api_version !=
|
||||
OEC_API_VERSION) {
|
||||
if (license_response.request.core_message.nonce_values.api_major_version !=
|
||||
ODK_MAJOR_VERSION) {
|
||||
return ODK_UNSUPPORTED_API;
|
||||
}
|
||||
|
||||
/* 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 (parsed_license->nonce_required) {
|
||||
if (initial_license_load) {
|
||||
if (nonce_values->nonce !=
|
||||
license_response.core_message.nonce_values.nonce ||
|
||||
license_response.request.core_message.nonce_values.nonce ||
|
||||
nonce_values->session_id !=
|
||||
license_response.core_message.nonce_values.session_id) {
|
||||
license_response.request.core_message.nonce_values.session_id) {
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
} else { /* !initial_license_load */
|
||||
nonce_values->nonce = license_response.core_message.nonce_values.nonce;
|
||||
nonce_values->nonce =
|
||||
license_response.request.core_message.nonce_values.nonce;
|
||||
nonce_values->session_id =
|
||||
license_response.core_message.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 && memcmp(request_hash, parsed_license->request_hash,
|
||||
ODK_SHA256_HASH_SIZE)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
/* If the license has a provider session token (pst), then OEMCrypto should
|
||||
* have a usage entry loaded. */
|
||||
if (usage_entry_present && parsed_license->pst.length == 0) {
|
||||
if (initial_license_load &&
|
||||
crypto_memcmp(request_hash, license_response.request_hash,
|
||||
ODK_SHA256_HASH_SIZE)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
*timer_limits = parsed_license->timer_limits;
|
||||
return err;
|
||||
/* 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,
|
||||
@@ -250,16 +270,18 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t* timer_value) {
|
||||
if (!message || !nonce_values || !timer_limits || !clock_values) {
|
||||
if (message == NULL || nonce_values == NULL || timer_limits == NULL ||
|
||||
clock_values == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_RenewalMessage renewal_response = {
|
||||
{0},
|
||||
ODK_RenewalResponse renewal_response = {
|
||||
{{0}, 0},
|
||||
0,
|
||||
};
|
||||
OEMCryptoResult err = ODK_ParseResponse(
|
||||
message, message_length, core_message_length, ODK_Renewal_Response_Type,
|
||||
nonce_values, &renewal_response.core_message);
|
||||
nonce_values, &renewal_response.request.core_message);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
@@ -269,69 +291,51 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
||||
* 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.
|
||||
*/
|
||||
if (clock_values->time_of_renewal_request < renewal_response.playback_time) {
|
||||
if (clock_values->time_of_renewal_request <
|
||||
renewal_response.request.playback_time) {
|
||||
return ODK_STALE_RENEWAL;
|
||||
}
|
||||
|
||||
/* The timer value should be set to the renewal duration. */
|
||||
if (timer_value) {
|
||||
*timer_value = timer_limits->renewal_playback_duration_seconds;
|
||||
}
|
||||
|
||||
if (timer_limits->renewal_playback_duration_seconds == 0) {
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_DISABLE_TIMER;
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
if (odk_add_overflow_u64(system_time,
|
||||
timer_limits->renewal_playback_duration_seconds,
|
||||
&clock_values->time_when_timer_expires)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
clock_values->timer_status = ODK_SET_TIMER;
|
||||
return ODK_SET_TIMER;
|
||||
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 || !nonce_values || !device_id || !parsed_response) {
|
||||
if (message == NULL || nonce_values == NULL || device_id == NULL ||
|
||||
parsed_response == NULL) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
ODK_ProvisioningResponse provisioning_response = {{
|
||||
{0},
|
||||
},
|
||||
ODK_ProvisioningResponse provisioning_response = {{{0}, 0, {0}},
|
||||
parsed_response};
|
||||
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
const OEMCryptoResult err =
|
||||
OEMCryptoResult err =
|
||||
ODK_ParseResponse(message, message_length, core_message_length,
|
||||
ODK_Provisioning_Response_Type, nonce_values,
|
||||
&provisioning_response.core_provisioning.core_message);
|
||||
&provisioning_response.request.core_message);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (memcmp(device_id, provisioning_response.core_provisioning.device_id,
|
||||
if (memcmp(device_id, provisioning_response.request.device_id,
|
||||
device_id_length)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||
/* check bytes beyond device_id_length are 0 */
|
||||
if (memcmp(
|
||||
zero,
|
||||
provisioning_response.core_provisioning.device_id + device_id_length,
|
||||
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
|
||||
if (memcmp(zero, provisioning_response.request.device_id + device_id_length,
|
||||
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
|
||||
return ODK_ERROR_CORE_MESSAGE;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#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
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
/* @@ private serialize */
|
||||
|
||||
static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) {
|
||||
Pack_uint32_t(msg, &obj->api_version);
|
||||
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);
|
||||
}
|
||||
@@ -35,12 +36,12 @@ static void Pack_OEMCrypto_KeyObject(Message* msg,
|
||||
}
|
||||
|
||||
static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) {
|
||||
Pack_uint32_t(msg, &obj->soft_expiry);
|
||||
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->latest_playback_start_seconds);
|
||||
Pack_uint64_t(msg, &obj->initial_playback_duration_seconds);
|
||||
Pack_uint64_t(msg, &obj->renewal_playback_duration_seconds);
|
||||
Pack_uint64_t(msg, &obj->license_duration_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(Message* msg, ODK_ParsedLicense const* obj) {
|
||||
@@ -53,10 +54,9 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
|
||||
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->pst);
|
||||
Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||
Pack_uint32_t(msg, &obj->license_type);
|
||||
Pack_uint32_t(msg, &obj->nonce_required);
|
||||
Pack_enum(msg, obj->license_type);
|
||||
Pack_bool(msg, &obj->nonce_required);
|
||||
Pack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||
Pack_uint32_t(msg, &obj->key_array_length);
|
||||
size_t i;
|
||||
for (i = 0; i < (size_t)obj->key_array_length; i++) {
|
||||
@@ -66,7 +66,7 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
|
||||
|
||||
static void Pack_ODK_ParsedProvisioning(Message* msg,
|
||||
ODK_ParsedProvisioning const* obj) {
|
||||
Pack_uint32_t(msg, &obj->key_type);
|
||||
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);
|
||||
@@ -74,17 +74,19 @@ static void Pack_ODK_ParsedProvisioning(Message* msg,
|
||||
|
||||
/* @@ odk serialize */
|
||||
|
||||
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj) {
|
||||
void Pack_ODK_PreparedLicenseRequest(Message* msg,
|
||||
ODK_PreparedLicenseRequest const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
}
|
||||
|
||||
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj) {
|
||||
void Pack_ODK_PreparedRenewalRequest(Message* msg,
|
||||
ODK_PreparedRenewalRequest const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Pack_uint64_t(msg, &obj->playback_time);
|
||||
}
|
||||
|
||||
void Pack_ODK_ProvisioningMessage(Message* msg,
|
||||
ODK_ProvisioningMessage const* obj) {
|
||||
void Pack_ODK_PreparedProvisioningRequest(
|
||||
Message* msg, ODK_PreparedProvisioningRequest const* 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));
|
||||
@@ -93,13 +95,19 @@ void Pack_ODK_ProvisioningMessage(Message* msg,
|
||||
/* @@ kdo serialize */
|
||||
|
||||
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) {
|
||||
Pack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Pack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
|
||||
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||
}
|
||||
|
||||
void Pack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse const* obj) {
|
||||
Pack_ODK_PreparedRenewalRequest(msg, &obj->request);
|
||||
Pack_uint64_t(msg, &obj->renewal_duration_seconds);
|
||||
}
|
||||
|
||||
void Pack_ODK_ProvisioningResponse(Message* msg,
|
||||
ODK_ProvisioningResponse const* obj) {
|
||||
Pack_ODK_ProvisioningMessage(msg, &obj->core_provisioning);
|
||||
Pack_ODK_PreparedProvisioningRequest(msg, &obj->request);
|
||||
Pack_ODK_ParsedProvisioning(
|
||||
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
|
||||
}
|
||||
@@ -109,7 +117,8 @@ void Pack_ODK_ProvisioningResponse(Message* msg,
|
||||
/* @@ private deserialize */
|
||||
|
||||
static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) {
|
||||
Unpack_uint32_t(msg, &obj->api_version);
|
||||
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);
|
||||
}
|
||||
@@ -129,12 +138,12 @@ static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) {
|
||||
}
|
||||
|
||||
static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) {
|
||||
Unpack_uint32_t(msg, &obj->soft_expiry);
|
||||
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->latest_playback_start_seconds);
|
||||
Unpack_uint64_t(msg, &obj->initial_playback_duration_seconds);
|
||||
Unpack_uint64_t(msg, &obj->renewal_playback_duration_seconds);
|
||||
Unpack_uint64_t(msg, &obj->license_duration_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(Message* msg, ODK_ParsedLicense* obj) {
|
||||
@@ -142,10 +151,9 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->pst);
|
||||
Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
|
||||
Unpack_uint32_t(msg, &obj->license_type);
|
||||
Unpack_uint32_t(msg, &obj->nonce_required);
|
||||
obj->license_type = Unpack_enum(msg);
|
||||
Unpack_bool(msg, &obj->nonce_required);
|
||||
Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
|
||||
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||
Unpack_uint32_t(msg, &obj->key_array_length);
|
||||
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
|
||||
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
|
||||
@@ -159,7 +167,7 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
|
||||
|
||||
static void Unpack_ODK_ParsedProvisioning(Message* msg,
|
||||
ODK_ParsedProvisioning* obj) {
|
||||
Unpack_uint32_t(msg, &obj->key_type);
|
||||
obj->key_type = 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);
|
||||
@@ -167,12 +175,19 @@ static void Unpack_ODK_ParsedProvisioning(Message* msg,
|
||||
|
||||
/* @ kdo deserialize */
|
||||
|
||||
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj) {
|
||||
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
|
||||
ODK_PreparedLicenseRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
}
|
||||
|
||||
void Unpack_ODK_ProvisioningMessage(Message* msg,
|
||||
ODK_ProvisioningMessage* obj) {
|
||||
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
|
||||
ODK_PreparedRenewalRequest* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Unpack_uint64_t(msg, &obj->playback_time);
|
||||
}
|
||||
|
||||
void Unpack_ODK_PreparedProvisioningRequest(
|
||||
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));
|
||||
@@ -181,17 +196,18 @@ void Unpack_ODK_ProvisioningMessage(Message* msg,
|
||||
/* @@ odk deserialize */
|
||||
|
||||
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Unpack_ODK_PreparedLicenseRequest(msg, &obj->request);
|
||||
Unpack_ODK_ParsedLicense(msg, obj->parsed_license);
|
||||
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
|
||||
}
|
||||
|
||||
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) {
|
||||
Unpack_ODK_CoreMessage(msg, &obj->core_message);
|
||||
Unpack_uint64_t(msg, &obj->playback_time);
|
||||
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj) {
|
||||
Unpack_ODK_PreparedRenewalRequest(msg, &obj->request);
|
||||
Unpack_uint64_t(msg, &obj->renewal_duration_seconds);
|
||||
}
|
||||
|
||||
void Unpack_ODK_ProvisioningResponse(Message* msg,
|
||||
ODK_ProvisioningResponse* obj) {
|
||||
Unpack_ODK_ProvisioningMessage(msg, &obj->core_provisioning);
|
||||
Unpack_ODK_PreparedProvisioningRequest(msg, &obj->request);
|
||||
Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning);
|
||||
}
|
||||
|
||||
@@ -16,25 +16,32 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* odk pack */
|
||||
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj);
|
||||
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj);
|
||||
void Pack_ODK_ProvisioningMessage(Message* msg,
|
||||
ODK_ProvisioningMessage const* obj);
|
||||
void Pack_ODK_PreparedLicenseRequest(Message* msg,
|
||||
const ODK_PreparedLicenseRequest* obj);
|
||||
void Pack_ODK_PreparedRenewalRequest(Message* msg,
|
||||
const ODK_PreparedRenewalRequest* obj);
|
||||
void Pack_ODK_PreparedProvisioningRequest(
|
||||
Message* msg, const ODK_PreparedProvisioningRequest* obj);
|
||||
|
||||
/* odk unpack */
|
||||
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
|
||||
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj);
|
||||
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj);
|
||||
void Unpack_ODK_ProvisioningResponse(Message* msg,
|
||||
ODK_ProvisioningResponse* obj);
|
||||
|
||||
/* kdo pack */
|
||||
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj);
|
||||
void Pack_ODK_LicenseResponse(Message* msg, const ODK_LicenseResponse* obj);
|
||||
void Pack_ODK_RenewalResponse(Message* msg, const ODK_RenewalResponse* obj);
|
||||
void Pack_ODK_ProvisioningResponse(Message* msg,
|
||||
ODK_ProvisioningResponse const* obj);
|
||||
const ODK_ProvisioningResponse* obj);
|
||||
|
||||
/* kdo unpack */
|
||||
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj);
|
||||
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj);
|
||||
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
|
||||
ODK_PreparedLicenseRequest* obj);
|
||||
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
|
||||
ODK_PreparedRenewalRequest* obj);
|
||||
void Unpack_ODK_PreparedProvisioningRequest(
|
||||
Message* msg, ODK_PreparedProvisioningRequest* obj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "odk_structs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ODK_License_Request_Type = 1,
|
||||
ODK_License_Response_Type = 2,
|
||||
@@ -27,27 +31,61 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
} ODK_PreparedLicense;
|
||||
} ODK_PreparedLicenseRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
uint64_t playback_time;
|
||||
} ODK_RenewalMessage;
|
||||
} ODK_PreparedRenewalRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
uint32_t device_id_length;
|
||||
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||
} ODK_ProvisioningMessage;
|
||||
} ODK_PreparedProvisioningRequest;
|
||||
|
||||
typedef struct {
|
||||
ODK_CoreMessage core_message;
|
||||
ODK_PreparedLicenseRequest request;
|
||||
ODK_ParsedLicense* parsed_license;
|
||||
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
|
||||
} ODK_LicenseResponse;
|
||||
|
||||
typedef struct {
|
||||
ODK_ProvisioningMessage core_provisioning;
|
||||
ODK_PreparedRenewalRequest request;
|
||||
uint64_t renewal_duration_seconds;
|
||||
} ODK_RenewalResponse;
|
||||
|
||||
typedef struct {
|
||||
ODK_PreparedProvisioningRequest request;
|
||||
ODK_ParsedProvisioning* parsed_provisioning;
|
||||
} ODK_ProvisioningResponse;
|
||||
|
||||
/* These are the possible timer status values. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
|
||||
/* When the structure has been initialized, but no license is loaded. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED 1
|
||||
/* After the license is loaded, before a successful decrypt. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED 2
|
||||
/* After the license is loaded, if a renewal has also been loaded. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED 3
|
||||
/* The first decrypt has occurred and the timer is active. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_ACTIVE 4
|
||||
/* The first decrypt has occurred and the timer is unlimited. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_UNLIMITED 5
|
||||
/* The timer has transitioned from active to expired. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_EXPIRED 6
|
||||
/* The license has been marked as inactive. */
|
||||
#define ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE 7
|
||||
|
||||
/* 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_ */
|
||||
|
||||
@@ -6,41 +6,279 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "odk.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. This also updates the
|
||||
* timer_status if appropriate. */
|
||||
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) {
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
||||
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_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_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_version,
|
||||
uint32_t api_major_version,
|
||||
uint32_t session_id) {
|
||||
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
timer_limits->soft_expiry = false;
|
||||
/* 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->latest_playback_start_seconds = 0;
|
||||
timer_limits->initial_playback_duration_seconds = 0;
|
||||
timer_limits->renewal_playback_duration_seconds = 0;
|
||||
timer_limits->license_duration_seconds = 0;
|
||||
timer_limits->rental_duration_seconds = 0;
|
||||
timer_limits->total_playback_duration_seconds = 0;
|
||||
timer_limits->initial_renewal_duration_seconds = 0;
|
||||
|
||||
clock_values->time_of_license_signed = 0;
|
||||
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 = 0;
|
||||
clock_values->status = kUnused;
|
||||
ODK_InitializeClockValues(clock_values, 0);
|
||||
|
||||
nonce_values->api_version = api_version;
|
||||
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;
|
||||
@@ -48,12 +286,12 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||
clock_values->time_of_first_decrypt = 0;
|
||||
clock_values->time_of_last_decrypt = 0;
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
/* TODO(b/142415188): document this. */
|
||||
clock_values->timer_status = 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_signed,
|
||||
uint64_t time_of_first_decrypt,
|
||||
@@ -65,114 +303,84 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
||||
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 = 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. */
|
||||
uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values,
|
||||
uint64_t* timer_value) {
|
||||
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. */
|
||||
const uint64_t rental_time =
|
||||
system_time_seconds - clock_values->time_of_license_signed;
|
||||
uint64_t rental_time = 0;
|
||||
if (odk_sub_overflow_u64(system_time_seconds,
|
||||
clock_values->time_of_license_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 clock status is already marked as inactive, then playback is
|
||||
* not allowed. */
|
||||
/* TODO(b/142415188): add helper function. */
|
||||
if (clock_values->status > kActive) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* If this license is still inactive (never used) then we just look at the
|
||||
* rental window. This is the first playback for the license, not just this
|
||||
* session. */
|
||||
if (clock_values->status == kUnused) {
|
||||
/* If the rental clock has expired, the license has expired. */
|
||||
if (rental_time > timer_limits->latest_playback_start_seconds &&
|
||||
timer_limits->latest_playback_start_seconds > 0) {
|
||||
clock_values->timer_status = 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;
|
||||
}
|
||||
/* The timer should be limited by the playback duration. */
|
||||
uint64_t time_left = timer_limits->initial_playback_duration_seconds;
|
||||
/* If there is a license duration, it also limits the timer. Remeber, a
|
||||
* limit of 0 means no limit, or infinite. */
|
||||
if (timer_limits->license_duration_seconds > 0) {
|
||||
if (timer_limits->license_duration_seconds < rental_time) {
|
||||
/* If the license duration has expired. This is unusual, because this
|
||||
* can only happen if the license duration is less than the rental
|
||||
* window. */
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (timer_limits->license_duration_seconds - rental_time < time_left ||
|
||||
time_left == 0) {
|
||||
time_left = timer_limits->license_duration_seconds - rental_time;
|
||||
}
|
||||
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires,
|
||||
system_time_seconds, &new_timer_value)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
/* This is a new license, and we can start playback. */
|
||||
clock_values->status = kActive;
|
||||
}
|
||||
|
||||
/* 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->time_of_last_decrypt = system_time_seconds;
|
||||
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_DISABLE_TIMER;
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
/* Set timer to limit playback. */
|
||||
if (timer_value) *timer_value = time_left;
|
||||
clock_values->time_when_timer_expires = system_time_seconds + time_left;
|
||||
clock_values->timer_status = ODK_SET_TIMER;
|
||||
return ODK_SET_TIMER;
|
||||
clock_values->status = kActive;
|
||||
}
|
||||
/* Otherwise, this is the second loading of a persistent license. In this
|
||||
* case, we ignore the rental window. */
|
||||
const uint64_t time_since_first_decrypt =
|
||||
system_time_seconds - clock_values->time_of_first_decrypt;
|
||||
uint64_t time_left = 0;
|
||||
/* If there is an initial playback duration, the we use that as a limit.
|
||||
* This ignores any license renewals. If renewals are allowed, then the last
|
||||
* one can be reloaded to reset the timer. */
|
||||
if (timer_limits->initial_playback_duration_seconds > 0) {
|
||||
if (timer_limits->initial_playback_duration_seconds <=
|
||||
time_since_first_decrypt) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
time_left = timer_limits->initial_playback_duration_seconds -
|
||||
time_since_first_decrypt;
|
||||
}
|
||||
/* If there is a license duration, it also limits the timer. */
|
||||
if (timer_limits->license_duration_seconds > 0) {
|
||||
if (timer_limits->license_duration_seconds < rental_time) {
|
||||
/* The license duration has expired. */
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (timer_limits->license_duration_seconds - rental_time < time_left ||
|
||||
time_left == 0) {
|
||||
time_left = timer_limits->license_duration_seconds - rental_time;
|
||||
}
|
||||
}
|
||||
/* We can restart playback for this license. Update last playback time. */
|
||||
|
||||
/* 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 (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
|
||||
|
||||
/* 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_DISABLE_TIMER;
|
||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_UNLIMITED;
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
/* Set timer. */
|
||||
if (timer_value) *timer_value = time_left;
|
||||
clock_values->time_when_timer_expires = system_time_seconds + time_left;
|
||||
clock_values->timer_status = ODK_SET_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;
|
||||
}
|
||||
|
||||
@@ -181,27 +389,29 @@ uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
||||
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
|
||||
const ODK_TimerLimits* timer_limits,
|
||||
ODK_ClockValues* clock_values) {
|
||||
if (clock_values == NULL || timer_limits == NULL)
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (clock_values->timer_status == ODK_TIMER_EXPIRED) {
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* If the clock status is already marked as inactive, then playback is
|
||||
* not allowed. */
|
||||
/* TODO(b/142415188): add helper function. */
|
||||
if (clock_values->status > kActive) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (clock_values->time_when_timer_expires > 0 &&
|
||||
system_time_seconds > clock_values->time_when_timer_expires) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
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) {
|
||||
@@ -209,9 +419,12 @@ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
|
||||
} 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,
|
||||
@@ -219,67 +432,41 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
|
||||
uint64_t system_time_seconds) {
|
||||
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
timer_limits->soft_expiry = false;
|
||||
timer_limits->soft_enforce_playback_duration = false;
|
||||
timer_limits->soft_enforce_rental_duration = false;
|
||||
timer_limits->earliest_playback_start_seconds = 0;
|
||||
timer_limits->latest_playback_start_seconds = 0;
|
||||
timer_limits->initial_playback_duration_seconds = key_duration;
|
||||
timer_limits->renewal_playback_duration_seconds = key_duration;
|
||||
timer_limits->license_duration_seconds = 0;
|
||||
nonce_values->api_version = 15;
|
||||
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_version != 15) return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
if (nonce_values->api_major_version != 15)
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
if (clock_values->status > kActive) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
/* If this is before the license was signed, something is odd. Return an
|
||||
* error. */
|
||||
if (system_time_seconds < clock_values->time_of_license_signed)
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
|
||||
/* All times are relative to when the license was signed. */
|
||||
const uint64_t rental_time =
|
||||
system_time_seconds - clock_values->time_of_license_signed;
|
||||
|
||||
/* The timer should be limited by the renewal playback duration. This is
|
||||
* similar to code in AttemptFirstPlayback, above, except we use the
|
||||
* renewal_playback_duration here, and we do not change clock_values->status.
|
||||
*/
|
||||
uint64_t time_left = timer_limits->renewal_playback_duration_seconds;
|
||||
/* If there is a license duration, it also limits the timer. Remember, a
|
||||
* limit of 0 means no limit, or infinite. */
|
||||
if (timer_limits->license_duration_seconds > 0) {
|
||||
if (timer_limits->license_duration_seconds < rental_time) {
|
||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
||||
return ODK_TIMER_EXPIRED;
|
||||
}
|
||||
if (timer_limits->license_duration_seconds - rental_time < time_left ||
|
||||
time_left == 0) {
|
||||
time_left = timer_limits->license_duration_seconds - rental_time;
|
||||
}
|
||||
}
|
||||
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
|
||||
clock_values->time_when_timer_expires = 0;
|
||||
clock_values->timer_status = ODK_DISABLE_TIMER;
|
||||
return ODK_DISABLE_TIMER;
|
||||
}
|
||||
/* Set timer to limit playback. */
|
||||
if (timer_value) *timer_value = time_left;
|
||||
clock_values->time_when_timer_expires = system_time_seconds + time_left;
|
||||
clock_values->timer_status = ODK_SET_TIMER;
|
||||
return ODK_SET_TIMER;
|
||||
return ODK_ComputeRenewalDuration(timer_limits, clock_values,
|
||||
system_time_seconds, new_key_duration,
|
||||
timer_value);
|
||||
}
|
||||
|
||||
25
libwvdrmengine/oemcrypto/odk/src/odk_util.c
Normal file
25
libwvdrmengine/oemcrypto/odk/src/odk_util.c
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
|
||||
/* source code may only be used and distributed under the Widevine Master */
|
||||
/* License Agreement. */
|
||||
|
||||
#include "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 = in_a;
|
||||
const uint8_t* b = in_b;
|
||||
uint8_t x = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
x |= a[i] ^ b[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
24
libwvdrmengine/oemcrypto/odk/src/odk_util.h
Normal file
24
libwvdrmengine/oemcrypto/odk/src/odk_util.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 Master */
|
||||
/* License Agreement. */
|
||||
|
||||
#ifndef WIDEVINE_ODK_SRC_ODK_UTIL_H_
|
||||
#define WIDEVINE_ODK_SRC_ODK_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* WIDEVINE_ODK_SRC_ODK_UTIL_H_ */
|
||||
@@ -51,6 +51,26 @@ static void PackBytes(Message* message, const uint8_t* ptr, size_t count) {
|
||||
}
|
||||
}
|
||||
|
||||
void Pack_enum(Message* message, int value) {
|
||||
uint32_t v32 = value;
|
||||
Pack_uint32_t(message, &v32);
|
||||
}
|
||||
|
||||
void Pack_bool(Message* message, const bool* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[4] = {0};
|
||||
data[3] = *value ? 1 : 0;
|
||||
PackBytes(message, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Pack_uint16_t(Message* message, const uint16_t* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[2] = {0};
|
||||
data[0] = *value >> 8;
|
||||
data[1] = *value >> 0;
|
||||
PackBytes(message, data, sizeof(data));
|
||||
}
|
||||
|
||||
void Pack_uint32_t(Message* message, const uint32_t* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[4] = {0};
|
||||
@@ -90,6 +110,27 @@ static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) {
|
||||
}
|
||||
}
|
||||
|
||||
int Unpack_enum(Message* message) {
|
||||
uint32_t v32;
|
||||
Unpack_uint32_t(message, &v32);
|
||||
return v32;
|
||||
}
|
||||
|
||||
void Unpack_bool(Message* message, bool* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[4] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
*value = (0 != data[3]);
|
||||
}
|
||||
|
||||
void Unpack_uint16_t(Message* message, uint16_t* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[2] = {0};
|
||||
UnpackBytes(message, data, sizeof(data));
|
||||
*value = data[0];
|
||||
*value = *value << 8 | data[1];
|
||||
}
|
||||
|
||||
void Unpack_uint32_t(Message* message, uint32_t* value) {
|
||||
if (!ValidMessage(message)) return;
|
||||
uint8_t data[4] = {0};
|
||||
@@ -195,7 +236,8 @@ size_t GetSize(Message* message) {
|
||||
void SetSize(Message* message, size_t size) {
|
||||
if (message == NULL) return;
|
||||
if (size > message->capacity) message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||
message->size = size;
|
||||
else
|
||||
message->size = size;
|
||||
}
|
||||
|
||||
MessageStatus GetStatus(Message* message) { return message->status; }
|
||||
|
||||
@@ -34,11 +34,17 @@ typedef struct _Message Message;
|
||||
|
||||
bool ValidMessage(Message* message);
|
||||
|
||||
void Pack_enum(Message* message, int value);
|
||||
void Pack_bool(Message* message, const bool* value);
|
||||
void Pack_uint16_t(Message* message, const uint16_t* value);
|
||||
void Pack_uint32_t(Message* message, const uint32_t* value);
|
||||
void Pack_uint64_t(Message* message, const uint64_t* value);
|
||||
void PackArray(Message* message, const uint8_t* base, size_t size);
|
||||
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
|
||||
|
||||
int Unpack_enum(Message* message);
|
||||
void Unpack_bool(Message* message, bool* value);
|
||||
void Unpack_uint16_t(Message* message, uint16_t* value);
|
||||
void Unpack_uint32_t(Message* message, uint32_t* value);
|
||||
void Unpack_uint64_t(Message* message, uint64_t* value);
|
||||
void UnpackArray(Message* message, uint8_t* address,
|
||||
|
||||
Reference in New Issue
Block a user