Allow server to send license with larger ODK_MAX_NUM_KEYS

PiperOrigin-RevId: 538676411
Merged from https://widevine-internal-review.googlesource.com/175915

Change-Id: Iadef2115fe3f9001034223e647cbfa6228484281
This commit is contained in:
Vicky Min
2023-06-07 21:22:24 -07:00
committed by Robert Shih
parent 57e997fe19
commit ff80927f90
11 changed files with 205 additions and 37 deletions

View File

@@ -39,7 +39,7 @@ using oemcrypto_core_message::features::CoreMessageFeatures;
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const CoreMessageFeatures& features,
const ODK_ParsedLicense& parsed_lic,
const ODK_Packing_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message);

View File

@@ -19,7 +19,7 @@ extern "C" {
#define ODK_MINOR_VERSION 2
/* ODK Version string. Date changed automatically on each release. */
#define ODK_RELEASE_DATE "ODK v18.2 2023-05-26"
#define ODK_RELEASE_DATE "ODK v18.2 2023-06-08"
/* The lowest version number for an ODK message. */
#define ODK_FIRST_VERSION 16
@@ -247,6 +247,46 @@ typedef struct {
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
} ODK_ParsedLicense;
/**
* The parsed license structure contains information from the license
* message. The function ODK_ParseLicense will fill in the fields of this
* message. All substrings are contained within the message body.
*
* @param enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits.
* @param enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is
* 512 bits.
* @param pst: the Provider Session Token.
* @param srm_restriction_data: optional data specifying the minimum SRM
* version.
* @param license_type: specifies if the license contains content keys or
* entitlement keys.
* @param nonce_required: indicates if the license requires a nonce.
* @param timer_limits: time limits of the for the license.
* @param watermarking: specifies if device supports watermarking.
* @param dtcp2_required: specifies if device supports DTCP.
* @param renewal_delay_base: what time the timer starting is based off of.
* @param key_array_length: number of keys present.
* @param key_array: set of keys to be installed. This is a pointer to an array
* to allow packing a number of keys greater than |ODK_MAX_NUM_KEYS|.
*
* @version
* This struct changed in API version 18.
*/
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 watermarking;
OEMCrypto_DTCP2_CMI_Packet dtcp2_required;
OEMCrypto_TimerDelayBase renewal_delay_base;
uint32_t key_array_length;
OEMCrypto_KeyObject* key_array;
} ODK_Packing_ParsedLicense;
/**
* The parsed provisioning structure contains information from the license
* message. The function ODK_ParseProvisioning will fill in the fields of

View File

@@ -116,19 +116,16 @@ bool CopyDeviceId(const ODK_ProvisioningRequest& src,
} // namespace
bool CreateCoreLicenseResponse(const CoreMessageFeatures& features,
const ODK_ParsedLicense& parsed_lic,
const ODK_Packing_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), {}};
ODK_Packing_LicenseResponse license_response{
{}, const_cast<ODK_Packing_ParsedLicense*>(&parsed_lic), {}};
if (!CreateResponseHeader(features, ODK_License_Response_Type,
&license_response.core_message, core_request)) {
return false;
}
if (ODK_MAX_NUM_KEYS < license_response.parsed_license->key_array_length) {
return false;
}
if (license_response.core_message.nonce_values.api_major_version == 16) {
if (core_request_sha256.size() != sizeof(license_response.request_hash))
return false;

View File

@@ -9,6 +9,7 @@
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "core_message_serialize.h"
#include "license_protocol.pb.h"
@@ -83,7 +84,8 @@ bool CreateCoreLicenseResponseFromProto(const CoreMessageFeatures& features,
return false;
}
ODK_ParsedLicense parsed_lic{};
ODK_Packing_ParsedLicense parsed_lic{};
std::vector<OEMCrypto_KeyObject> key_array;
bool any_content = false;
bool any_entitlement = false;
@@ -110,12 +112,8 @@ bool CreateCoreLicenseResponseFromProto(const CoreMessageFeatures& features,
} else {
any_content = true;
}
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
return false;
}
uint32_t& n = parsed_lic.key_array_length;
parsed_lic.key_array[n++] =
KeyContainerToOecKey(serialized_license, k, uses_padding);
key_array.push_back(
KeyContainerToOecKey(serialized_license, k, uses_padding));
break;
}
default: {
@@ -173,6 +171,9 @@ bool CreateCoreLicenseResponseFromProto(const CoreMessageFeatures& features,
policy.renewal_delay_seconds() +
policy.renewal_recovery_duration_seconds();
parsed_lic.key_array = key_array.data();
parsed_lic.key_array_length = static_cast<uint32_t>(key_array.size());
return CreateCoreLicenseResponse(features, parsed_lic, core_request,
core_request_sha256, oemcrypto_core_message);
}

View File

@@ -47,7 +47,7 @@ static void Pack_ODK_TimerLimits(ODK_Message* msg, ODK_TimerLimits const* obj) {
}
static void Pack_ODK_ParsedLicense(ODK_Message* msg,
ODK_ParsedLicense const* obj,
ODK_Packing_ParsedLicense const* obj,
const ODK_NonceValues* nonce_values) {
/* hand-coded */
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
@@ -172,9 +172,10 @@ void Pack_ODK_PreparedRenewedProvisioningRequest(
/* @@ kdo serialize */
void Pack_ODK_LicenseResponse(ODK_Message* msg,
ODK_LicenseResponse const* obj) {
ODK_Packing_LicenseResponse const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license,
Pack_ODK_ParsedLicense(msg,
(const ODK_Packing_ParsedLicense*)obj->parsed_license,
&obj->core_message.nonce_values);
if ((&obj->core_message.nonce_values)->api_major_version == 16) {
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));

View File

@@ -43,7 +43,8 @@ void Unpack_ODK_Provisioning40Response(ODK_Message* msg,
ODK_Provisioning40Response* obj);
/* kdo pack */
void Pack_ODK_LicenseResponse(ODK_Message* msg, const ODK_LicenseResponse* obj);
void Pack_ODK_LicenseResponse(ODK_Message* msg,
const ODK_Packing_LicenseResponse* obj);
void Pack_ODK_RenewalResponse(ODK_Message* msg, const ODK_RenewalResponse* obj);
void Pack_ODK_ProvisioningResponse(ODK_Message* msg,
const ODK_ProvisioningResponse* obj);

View File

@@ -91,6 +91,12 @@ typedef struct {
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
} ODK_LicenseResponse;
typedef struct {
ODK_CoreMessage core_message;
ODK_Packing_ParsedLicense* parsed_license;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
} ODK_Packing_LicenseResponse;
typedef struct {
ODK_PreparedRenewalRequest request;
uint64_t renewal_duration_seconds;

View File

@@ -4,6 +4,7 @@
#include "fuzzing/odk_fuzz_helper.h"
#include <string>
#include <vector>
#include "core_message_types.h"
#include "odk.h"
@@ -133,8 +134,26 @@ bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
nonce_values.nonce, nonce_values.session_id, counter_info};
std::string core_request_sha_256(
reinterpret_cast<const char*>(args->request_hash), ODK_SHA256_HASH_SIZE);
ODK_Packing_ParsedLicense parsed_license;
parsed_license.enc_mac_keys_iv = parsed_lic.enc_mac_keys_iv;
parsed_license.enc_mac_keys = parsed_lic.enc_mac_keys;
parsed_license.pst = parsed_lic.pst;
parsed_license.srm_restriction_data = parsed_lic.srm_restriction_data;
parsed_license.license_type = parsed_lic.license_type;
parsed_license.nonce_required = parsed_lic.nonce_required;
parsed_license.timer_limits = parsed_lic.timer_limits;
parsed_license.watermarking = parsed_lic.watermarking;
parsed_license.dtcp2_required = parsed_lic.dtcp2_required;
parsed_license.renewal_delay_base = parsed_lic.renewal_delay_base;
parsed_license.key_array_length = parsed_lic.key_array_length;
std::vector<OEMCrypto_KeyObject> key_array;
size_t i;
for (i = 0; i < parsed_lic.key_array_length; i++) {
key_array.push_back(parsed_lic.key_array[i]);
}
parsed_license.key_array = key_array.data();
return serialize::CreateCoreLicenseResponse(
CoreMessageFeatures::kDefaultFeatures, parsed_lic, core_request,
CoreMessageFeatures::kDefaultFeatures, parsed_license, core_request,
core_request_sha_256, oemcrypto_core_message);
}

View File

@@ -978,13 +978,32 @@ TEST_P(OdkVersionTest, LicenseResponseRoundtrip) {
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license), nullptr);
};
ODK_Packing_ParsedLicense parsed_license;
parsed_license.enc_mac_keys_iv = params.parsed_license.enc_mac_keys_iv;
parsed_license.enc_mac_keys = params.parsed_license.enc_mac_keys;
parsed_license.pst = params.parsed_license.pst;
parsed_license.srm_restriction_data =
params.parsed_license.srm_restriction_data;
parsed_license.license_type = params.parsed_license.license_type;
parsed_license.nonce_required = params.parsed_license.nonce_required;
parsed_license.timer_limits = params.parsed_license.timer_limits;
parsed_license.watermarking = params.parsed_license.watermarking;
parsed_license.dtcp2_required = params.parsed_license.dtcp2_required;
parsed_license.renewal_delay_base = params.parsed_license.renewal_delay_base;
parsed_license.key_array_length = params.parsed_license.key_array_length;
std::vector<OEMCrypto_KeyObject> key_array;
for (size_t i = 0; i < params.parsed_license.key_array_length; i++) {
key_array.push_back(params.parsed_license.key_array[i]);
}
parsed_license.key_array = key_array.data();
const std::string request_hash_string(
reinterpret_cast<const char*>(request_hash_read),
sizeof(request_hash_read));
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
std::string* oemcrypto_core_message) {
return CreateCoreLicenseResponse(features_, params.parsed_license,
core_request, request_hash_string,
return CreateCoreLicenseResponse(features_, parsed_license, core_request,
request_hash_string,
oemcrypto_core_message);
};
ValidateResponse<ODK_LicenseRequest>(GetParam(), &(params.core_message),
@@ -992,6 +1011,84 @@ TEST_P(OdkVersionTest, LicenseResponseRoundtrip) {
kdo_prepare_func);
}
// Serialize and de-serialize license response with more keys than
// ODK_MAX_NUM_KEYS.
TEST_P(OdkVersionTest, LicenseResponseRoundtripMoreThanMaxKeys) {
ODK_LicenseResponseParams params;
ODK_SetDefaultLicenseResponseParams(&params,
GetParam().response_major_version);
SetRequestVersion(&params);
// For v17, we do not use the hash to verify the request. However, the server
// needs to be backwards compatible, so it still needs to pass the hash into
// CreateCoreLiceseseResponse below. Save a copy of params.request_hash as it
// will be zero out during the test
uint8_t request_hash_read[ODK_SHA256_HASH_SIZE];
memcpy(request_hash_read, params.request_hash, sizeof(request_hash_read));
uint8_t* buf = nullptr;
uint32_t buf_size = 0;
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
&buf_size);
uint8_t* zero = new uint8_t[buf_size]{};
size_t bytes_read = 0;
// zero-out input
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_IterFields(ODK_READ, zero, buf_size, &bytes_read,
params.extra_fields));
// Parse buf with odk
const OEMCryptoResult parse_result = ODK_ParseLicense(
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
params.usage_entry_present, 0, &(params.timer_limits),
&(params.clock_values), &(params.core_message.nonce_values),
&(params.parsed_license), nullptr);
EXPECT_EQ(OEMCrypto_SUCCESS, parse_result);
size_t size_out = 0;
if (parse_result != OEMCrypto_SUCCESS) {
ODK_IterFields(ODK_FieldMode::ODK_DUMP, buf, buf_size, &size_out,
params.extra_fields);
}
ODK_Packing_ParsedLicense parsed_license;
parsed_license.enc_mac_keys_iv = params.parsed_license.enc_mac_keys_iv;
parsed_license.enc_mac_keys = params.parsed_license.enc_mac_keys;
parsed_license.pst = params.parsed_license.pst;
parsed_license.srm_restriction_data =
params.parsed_license.srm_restriction_data;
parsed_license.license_type = params.parsed_license.license_type;
parsed_license.nonce_required = params.parsed_license.nonce_required;
parsed_license.timer_limits = params.parsed_license.timer_limits;
parsed_license.watermarking = params.parsed_license.watermarking;
parsed_license.dtcp2_required = params.parsed_license.dtcp2_required;
parsed_license.renewal_delay_base = params.parsed_license.renewal_delay_base;
parsed_license.key_array_length = ODK_MAX_NUM_KEYS + 1;
std::vector<OEMCrypto_KeyObject> key_array;
for (size_t i = 0; i < ODK_MAX_NUM_KEYS + 1; i++) {
OEMCrypto_KeyObject key = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
key_array.push_back(key);
}
parsed_license.key_array = key_array.data();
const std::string request_hash_string(
reinterpret_cast<const char*>(request_hash_read),
sizeof(request_hash_read));
// serialize odk output to oemcrypto_core_message
std::string oemcrypto_core_message;
ODK_LicenseRequest core_request = {};
core_request.api_major_version = GetParam().request_major_version;
core_request.api_minor_version = GetParam().request_minor_version;
core_request.nonce = params.core_message.nonce_values.nonce;
core_request.session_id = params.core_message.nonce_values.session_id;
bool result =
CreateCoreLicenseResponse(features_, parsed_license, core_request,
request_hash_string, &oemcrypto_core_message);
EXPECT_FALSE(result);
delete[] buf;
delete[] zero;
}
TEST_P(OdkVersionTest, RenewalResponseRoundtrip) {
ODK_RenewalResponseParams params;
ODK_SetDefaultRenewalResponseParams(&params);

View File

@@ -694,7 +694,7 @@ void LicenseRoundTrip::CreateDefaultResponse() {
FillCoreResponseSubstrings();
}
void LicenseRoundTrip::ConvertDataToValidBools(ODK_ParsedLicense* t) {
void LicenseRoundTrip::ConvertDataToValidBools(ODK_Packing_ParsedLicense* t) {
t->nonce_required = ConvertByteToValidBoolean(&t->nonce_required);
t->timer_limits.soft_enforce_playback_duration = ConvertByteToValidBoolean(
&t->timer_limits.soft_enforce_playback_duration);
@@ -802,27 +802,30 @@ void LicenseRoundTrip::FillCoreResponseSubstrings() {
sizeof(response_data_.srm_restriction_data));
}
core_response_.key_array_length = num_keys_;
key_array_.clear();
for (unsigned int i = 0; i < num_keys_; i++) {
core_response_.key_array[i].key_id = FindSubstring(
response_data_.keys[i].key_id, response_data_.keys[i].key_id_length);
core_response_.key_array[i].key_data_iv = FindSubstring(
response_data_.keys[i].key_iv, sizeof(response_data_.keys[i].key_iv));
core_response_.key_array[i].key_data =
FindSubstring(response_data_.keys[i].key_data,
OEMCrypto_KeyObject obj;
obj.key_id = FindSubstring(response_data_.keys[i].key_id,
response_data_.keys[i].key_id_length);
obj.key_data_iv = FindSubstring(response_data_.keys[i].key_iv,
sizeof(response_data_.keys[i].key_iv));
obj.key_data = FindSubstring(response_data_.keys[i].key_data,
response_data_.keys[i].key_data_length);
if (core_request().api_major_version < kClearControlBlockAPIMajor ||
(core_request().api_major_version == kClearControlBlockAPIMajor &&
core_request().api_minor_version < kClearControlBlockAPIMinor)) {
core_response_.key_array[i].key_control_iv =
obj.key_control_iv =
FindSubstring(response_data_.keys[i].control_iv,
sizeof(response_data_.keys[i].control_iv));
} else {
core_response_.key_array[i].key_control_iv = FindSubstring(nullptr, 0);
obj.key_control_iv = FindSubstring(nullptr, 0);
}
core_response_.key_array[i].key_control =
FindSubstring(&response_data_.keys[i].control,
obj.key_control = FindSubstring(&response_data_.keys[i].control,
sizeof(response_data_.keys[i].control));
key_array_.push_back(obj);
}
core_response_.key_array = key_array_.data();
core_response_.key_array_length = static_cast<uint32_t>(key_array_.size());
}
void LicenseRoundTrip::EncryptResponse(bool force_clear_kcb) {

View File

@@ -373,7 +373,7 @@ class LicenseRoundTrip
: public RoundTrip<
/* CoreRequest */ oemcrypto_core_message::ODK_LicenseRequest,
OEMCrypto_PrepAndSignLicenseRequest,
/* CoreResponse */ ODK_ParsedLicense,
/* CoreResponse */ ODK_Packing_ParsedLicense,
/* ResponseData */ MessageData> {
public:
LicenseRoundTrip(Session* session)
@@ -399,7 +399,7 @@ class LicenseRoundTrip
void InjectFuzzedResponseData(const uint8_t* data, size_t size);
// Used for OEMCrypto Fuzzing: Convert boolean flags in parsed_license to
// valid bytes to avoid errors from msan.
void ConvertDataToValidBools(ODK_ParsedLicense* t);
void ConvertDataToValidBools(ODK_Packing_ParsedLicense* t);
// Create a license with four keys. Each key is responsible for one of generic
// encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key
// is allowed only one type of operation.
@@ -494,6 +494,9 @@ class LicenseRoundTrip
// CreateDefaultResponse.
OEMCrypto_LicenseType license_type_;
uint8_t request_hash_[ODK_SHA256_HASH_SIZE];
// Used to hold and add/update key information to be transferred into the core
// response later on.
std::vector<OEMCrypto_KeyObject> key_array_;
};
class RenewalRoundTrip