Add v16.3 unwrap compatibility
This commit is contained in:
@@ -43,6 +43,10 @@
|
||||
#include "wtpi_provisioning_4_interface.h"
|
||||
#include "wtpi_root_of_trust_interface_layer1.h"
|
||||
|
||||
#ifdef USE_REF_BACK_COMPAT
|
||||
# include "wtpi_ref_compat_interface.h"
|
||||
#endif
|
||||
|
||||
typedef enum GlobalSystemState {
|
||||
SYSTEM_NOT_INITIALIZED = (int)0x2ca77206,
|
||||
SYSTEM_INITIALIZED = (int)0xf57fab49
|
||||
@@ -3050,16 +3054,71 @@ OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session,
|
||||
result = OPKI_CheckStatePreCall(session_context, API_LOADDRMPRIVATEKEY);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
const uint8_t* drm_key = wrapped_drm_key;
|
||||
size_t drm_key_length = wrapped_drm_key_length;
|
||||
WTPI_AsymmetricKey_Handle private_key_handle;
|
||||
uint32_t allowed_schemes;
|
||||
result = WTPI_UnwrapIntoAsymmetricKeyHandle(
|
||||
DEVICE_KEY_WRAP_DRM_CERT, wrapped_drm_key, wrapped_drm_key_length,
|
||||
drm_key_type, &private_key_handle, &allowed_schemes);
|
||||
|
||||
#ifdef USE_REF_BACK_COMPAT
|
||||
uint8_t new_wrapped_drm_key[MAX_WRAPPED_ASYMMETRIC_KEY_SIZE];
|
||||
size_t new_wrapped_drm_key_length = 0;
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGW("Failed to unwrap DRM private key into key handle with result: %u",
|
||||
result);
|
||||
|
||||
// A v16-wrapped RSA key will fail to unwrap with WTPI_Unwrap*
|
||||
// In this case, we need to intercept that failure and try to unwrap using
|
||||
// the "reference" method.
|
||||
uint8_t clear_drm_key[PKCS8_DRM_KEY_MAX_SIZE];
|
||||
size_t clear_drm_key_length = sizeof(clear_drm_key);
|
||||
result = WTPI_RefCompat_UnwrapRSA(wrapped_drm_key, wrapped_drm_key_length,
|
||||
clear_drm_key, &clear_drm_key_length);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("unwrap back compat failed with result %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Rewrap it with current scheme, then continue as usual.
|
||||
// OPKI_LoadDRMKey() requires a wrapped key, so we have to rewrap.
|
||||
result = WTPI_GetWrappedAsymmetricKeySize(
|
||||
clear_drm_key_length, DRM_RSA_PRIVATE_KEY, &new_wrapped_drm_key_length);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
WTPI_SecureZeroMemory(clear_drm_key, clear_drm_key_length);
|
||||
return result;
|
||||
}
|
||||
|
||||
result =
|
||||
WTPI_WrapAsymmetricKey(DEVICE_KEY_WRAP_DRM_CERT, new_wrapped_drm_key,
|
||||
new_wrapped_drm_key_length, drm_key_type,
|
||||
clear_drm_key, clear_drm_key_length);
|
||||
WTPI_SecureZeroMemory(clear_drm_key, clear_drm_key_length);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// This validates the wrapped key and lets us get the signature size.
|
||||
result = WTPI_UnwrapIntoAsymmetricKeyHandle(
|
||||
DEVICE_KEY_WRAP_DRM_CERT, new_wrapped_drm_key,
|
||||
new_wrapped_drm_key_length, drm_key_type, &private_key_handle,
|
||||
&allowed_schemes);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
drm_key = new_wrapped_drm_key;
|
||||
drm_key_length = new_wrapped_drm_key_length;
|
||||
}
|
||||
#else
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Failed to unwrap DRM private key into key handle with result: %u",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t signature_size;
|
||||
result = WTPI_GetSignatureSize(private_key_handle, &signature_size);
|
||||
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
|
||||
@@ -3068,9 +3127,8 @@ OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session,
|
||||
return result;
|
||||
}
|
||||
|
||||
result =
|
||||
OPKI_LoadDRMKey(session_context, drm_key_type, wrapped_drm_key,
|
||||
wrapped_drm_key_length, signature_size, allowed_schemes);
|
||||
result = OPKI_LoadDRMKey(session_context, drm_key_type, drm_key,
|
||||
drm_key_length, signature_size, allowed_schemes);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Failed to load DRM key");
|
||||
goto cleanup;
|
||||
@@ -3338,8 +3396,8 @@ OEMCryptoResult OEMCrypto_LoadProvisioningCast(
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length);
|
||||
|
||||
// If this call is just to find the size of the wrapped key, then we won't
|
||||
// derive new keys because that call will erase the drm private key when it's
|
||||
// finished using it.
|
||||
// derive new keys because that call will erase the drm private key when
|
||||
// it's finished using it.
|
||||
if (wrapped_private_key != NULL && *wrapped_private_key_length > 0) {
|
||||
result = DeriveKeysFromSessionKey(session_context, provision_request,
|
||||
provision_request_length, derivation_key,
|
||||
@@ -3785,10 +3843,11 @@ OEMCryptoResult OEMCrypto_RemoveEntitledKeySession(
|
||||
OEMCryptoResult result =
|
||||
GetSessionContext(key_session, &session_context, &key_session_context);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
// In case that the entitlement session is closed prior to the entitled key
|
||||
// session, the result of OPKI_GetSession() will not be OEMCrypto_SUCCESS,
|
||||
// and that's ok. This entitled key session should already be released when
|
||||
// its entitlement session was closed. Just return success here.
|
||||
// In case that the entitlement session is closed prior to the entitled
|
||||
// key session, the result of OPKI_GetSession() will not be
|
||||
// OEMCrypto_SUCCESS, and that's ok. This entitled key session should
|
||||
// already be released when its entitlement session was closed. Just
|
||||
// return success here.
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
ABORT_IF(session_context == NULL,
|
||||
@@ -4186,8 +4245,8 @@ OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession(
|
||||
}
|
||||
/* Validations to be done before re-associating an entitled key session to a
|
||||
* new entitlement session:
|
||||
* 1. at least one entitled key is supposed to have its entitlement key found
|
||||
* in the new session
|
||||
* 1. at least one entitled key is supposed to have its entitlement key
|
||||
* found in the new session
|
||||
* 2. for any entitled key, if its entitlement key is found in the new
|
||||
* session, then the key control block should remain unchanged in the new
|
||||
* session, compared to the one in the existing entitlement session */
|
||||
@@ -4198,8 +4257,8 @@ OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession(
|
||||
* with. */
|
||||
const EntitlementKeyInfo* key_info =
|
||||
&key_session_context->entitlement_keys[i];
|
||||
/* Finds the entitlement key in the new entitlement session. It is ok if an
|
||||
* entitled key doesn't have an entitlement key in the new entitlement
|
||||
/* Finds the entitlement key in the new entitlement session. It is ok if
|
||||
* an entitled key doesn't have an entitlement key in the new entitlement
|
||||
* session. The entitled key will be ignored in this case. */
|
||||
for (uint32_t k = 0; k < entitlement_session_context->num_entitlement_keys;
|
||||
k++) {
|
||||
@@ -4338,16 +4397,16 @@ OEMCryptoResult OEMCrypto_LoadCasECMKeys(
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Initialized the index to which the entitled content key to be loaded. */
|
||||
/* Initialized the index to which the entitled content key to be loaded.
|
||||
*/
|
||||
uint32_t entitled_content_key_index =
|
||||
key_session->num_entitled_content_keys;
|
||||
/* It prefers to reuse an existing content key index that is entitled by the
|
||||
* same entitlement key if one exists already, but will allocate a new index
|
||||
* if there are none that can be reused.
|
||||
* The block below searches whether there is an existing content key
|
||||
* entitled by the same entitlement key, with the same parity as the key to
|
||||
* be loaded, and will reuse the index to load the new content key if there
|
||||
* is one. */
|
||||
/* It prefers to reuse an existing content key index that is entitled by
|
||||
* the same entitlement key if one exists already, but will allocate a new
|
||||
* index if there are none that can be reused. The block below searches
|
||||
* whether there is an existing content key entitled by the same
|
||||
* entitlement key, with the same parity as the key to be loaded, and will
|
||||
* reuse the index to load the new content key if there is one. */
|
||||
for (uint32_t k = 0; k < key_session->num_entitled_content_keys; k++) {
|
||||
const EntitlementKeyInfo* key_info = &key_session->entitlement_keys[k];
|
||||
if (key_info->entitlement_key_id_size == entitlement_key->key_id_size &&
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "wtpi_device_key_interface.h"
|
||||
#include "wtpi_generation_number_interface.h"
|
||||
#include "wtpi_logging_interface.h"
|
||||
#include "wtpi_ref_compat_interface.h"
|
||||
|
||||
/**
|
||||
The usage table consists of a short array of active entries, and a large
|
||||
@@ -130,43 +131,78 @@ static UsageEntry* FindUsageEntry(OEMCrypto_SESSION session_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This serializes, encrypts, and signs the current header into the given
|
||||
* buffer. It is an error if the buffer is not big enough. The header fields
|
||||
* and the master generation number in the current header will be updated. */
|
||||
NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignHeader(
|
||||
uint8_t* header_buffer, size_t header_buffer_length) {
|
||||
SavedUsageHeader header = {
|
||||
static OEMCryptoResult InitHeader(SavedUsageHeader* header,
|
||||
const UsageTable* usage_table) {
|
||||
if (!header || !usage_table) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
|
||||
*header = (SavedUsageHeader){
|
||||
.common_info =
|
||||
{
|
||||
.file_type = USAGE_TABLE_HEADER,
|
||||
.format_version = CURRENT_FILE_FORMAT_VERSION,
|
||||
},
|
||||
|
||||
.master_generation_number = g_usage_table.master_generation_number,
|
||||
.table_size = g_usage_table.table_size,
|
||||
.master_generation_number = usage_table->master_generation_number,
|
||||
.table_size = usage_table->table_size,
|
||||
};
|
||||
memcpy(header.generation_numbers, g_usage_table.generation_numbers,
|
||||
header.table_size * sizeof(uint64_t));
|
||||
SignedSavedUsageHeader signed_header = {
|
||||
|
||||
memcpy(header->generation_numbers, usage_table->generation_numbers,
|
||||
header->table_size * sizeof(uint64_t));
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult InitSignedHeader(SignedSavedUsageHeader* signed_header) {
|
||||
if (!signed_header) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
|
||||
*signed_header = (SignedSavedUsageHeader){
|
||||
.common_info =
|
||||
{
|
||||
.file_type = SIGNED_USAGE_TABLE_HEADER,
|
||||
.format_version = CURRENT_FILE_FORMAT_VERSION,
|
||||
},
|
||||
.buffer_size = sizeof(signed_header.buffer),
|
||||
.buffer_size = sizeof(signed_header->buffer),
|
||||
.buffer = {0},
|
||||
};
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
static OEMCryptoResult WrapHeader(SavedUsageHeader* header, uint8_t* out_buffer,
|
||||
size_t out_buffer_length) {
|
||||
if (!header) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
|
||||
uint8_t temp_buffer[PADDED_HEADER_BUFFER_SIZE] = {0};
|
||||
OEMCryptoResult result =
|
||||
OPKI_PackUsageHeader(temp_buffer, sizeof(temp_buffer), &header);
|
||||
OPKI_PackUsageHeader(temp_buffer, sizeof(temp_buffer), header);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
SignedSavedUsageHeader signed_header;
|
||||
result = InitSignedHeader(&signed_header);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
result = WTPI_EncryptAndSign(DEVICE_KEY_WRAP_USAGE_TABLE, temp_buffer,
|
||||
sizeof(temp_buffer), signed_header.buffer,
|
||||
&signed_header.buffer_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = OPKI_PackSignedUsageHeader(header_buffer, header_buffer_length,
|
||||
&signed_header);
|
||||
|
||||
result =
|
||||
OPKI_PackSignedUsageHeader(out_buffer, out_buffer_length, &signed_header);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* This serializes, encrypts, and signs the current header into the given
|
||||
* buffer. It is an error if the buffer is not big enough.
|
||||
* Copies the generation numbers from the supplied usage_table parameter into
|
||||
* the serialized header fields.
|
||||
*/
|
||||
NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignHeader(
|
||||
uint8_t* header_buffer, size_t header_buffer_length,
|
||||
const UsageTable* usage_table) {
|
||||
SavedUsageHeader header;
|
||||
OEMCryptoResult result = InitHeader(&header, usage_table);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = WrapHeader(&header, header_buffer, header_buffer_length);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -241,9 +277,48 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader(
|
||||
LOGE("Invalid header buffer size: %zu", header_buffer_length);
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
const uint8_t* buffer = header_buffer;
|
||||
size_t buffer_length = header_buffer_length;
|
||||
SavedCommonInfo common_info;
|
||||
OEMCryptoResult result = OPKI_UnpackSavedCommonInfo(
|
||||
header_buffer, header_buffer_length, &common_info);
|
||||
OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
|
||||
#ifdef USE_REF_BACK_COMPAT
|
||||
// To deal with reference-wrapped v16 usage tables, extract the info and
|
||||
// rewrap. Then continue as usual with the rewrapped blob.
|
||||
|
||||
// Reference usage table doesn't have any kind of common_info struct at the
|
||||
// top. Previously, fail immediatley if the common_info doesn't match
|
||||
// expectations. Now, check if none of the common_info data makes sense, then
|
||||
// try v16.
|
||||
result = OPKI_UnpackSavedCommonInfo(buffer, buffer_length, &common_info);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
bool should_try_ref_compat =
|
||||
(common_info.file_type != SIGNED_USAGE_TABLE_HEADER ||
|
||||
common_info.file_type != SIGNED_USAGE_TABLE_ENTRY ||
|
||||
common_info.file_type != USAGE_TABLE_HEADER ||
|
||||
common_info.file_type != USAGE_TABLE_ENTRY);
|
||||
|
||||
SavedUsageHeader temp_header = {0};
|
||||
uint8_t rewrapped_header[sizeof(SignedSavedUsageHeader)] = {0};
|
||||
size_t rewrapped_header_size = sizeof(rewrapped_header);
|
||||
if (should_try_ref_compat) {
|
||||
// Unwrap into current OPK struct
|
||||
result =
|
||||
WTPI_RefCompat_UnwrapUsageHeader(buffer, buffer_length, &temp_header);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Rewrap struct with current wrapping scheme
|
||||
result = WrapHeader(&temp_header, rewrapped_header, rewrapped_header_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Point variables to use new rewrapped data, start the process again
|
||||
buffer = rewrapped_header;
|
||||
buffer_length = rewrapped_header_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
result = OPKI_UnpackSavedCommonInfo(buffer, buffer_length, &common_info);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (common_info.file_type != SIGNED_USAGE_TABLE_HEADER) {
|
||||
/* We were given the wrong file. */
|
||||
@@ -253,8 +328,7 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader(
|
||||
if (common_info.format_version == LEGACY_FILE_FORMAT_VERSION) {
|
||||
LOGD("Legacy usage header format version %u detected.",
|
||||
LEGACY_FILE_FORMAT_VERSION);
|
||||
return DecryptAndVerifyHeader_Legacy(header_buffer, header_buffer_length,
|
||||
header);
|
||||
return DecryptAndVerifyHeader_Legacy(buffer, buffer_length, header);
|
||||
}
|
||||
if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) {
|
||||
LOGE("Bad signed usage header format version: %u",
|
||||
@@ -262,8 +336,7 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader(
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
SignedSavedUsageHeader signed_header;
|
||||
result = OPKI_UnpackSignedUsageHeader(header_buffer, header_buffer_length,
|
||||
&signed_header);
|
||||
result = OPKI_UnpackSignedUsageHeader(buffer, buffer_length, &signed_header);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (signed_header.buffer_size > sizeof(signed_header.buffer)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -296,8 +369,9 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader(
|
||||
}
|
||||
|
||||
/* This serializes, encrypts, and signs the specified entry into the given
|
||||
* buffer. It is an error if the buffer is not big enough. The header fields in
|
||||
* the entry will be updated, but the generation number will not. */
|
||||
* buffer. It is an error if the buffer is not big enough. The header
|
||||
* fields in the entry will be updated, but the generation number will not.
|
||||
*/
|
||||
NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignEntry(
|
||||
SavedUsageEntry* entry, uint8_t* entry_buffer, size_t entry_buffer_length) {
|
||||
entry->common_info.file_type = USAGE_TABLE_ENTRY;
|
||||
@@ -325,10 +399,10 @@ NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignEntry(
|
||||
return result;
|
||||
}
|
||||
|
||||
/* This decrypts and deserializes a usage table entry from the given buffer in
|
||||
* the legacy format. The signature of the buffer is verified. The generation
|
||||
* number is verified by the calling function. If the buffer is not big enough
|
||||
* the error OEMCrypto_ERROR_SHORT_BUFFER is returned. */
|
||||
/* This decrypts and deserializes a usage table entry from the given buffer
|
||||
* in the legacy format. The signature of the buffer is verified. The
|
||||
* generation number is verified by the calling function. If the buffer is
|
||||
* not big enough the error OEMCrypto_ERROR_SHORT_BUFFER is returned. */
|
||||
NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry_Legacy(
|
||||
const uint8_t* entry_buffer, size_t entry_buffer_length,
|
||||
SavedUsageEntry* entry) {
|
||||
@@ -376,10 +450,10 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry_Legacy(
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/* This decrypts and deserializes a usage table entry from the given buffer. The
|
||||
* signature of the buffer is verified. The generation number is verified by the
|
||||
* calling function. If the buffer is not big enough the error
|
||||
* OEMCrypto_ERROR_SHORT_BUFFER is returned. */
|
||||
/* This decrypts and deserializes a usage table entry from the given buffer.
|
||||
* The signature of the buffer is verified. The generation number is
|
||||
* verified by the calling function. If the buffer is not big enough the
|
||||
* error OEMCrypto_ERROR_SHORT_BUFFER is returned. */
|
||||
NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry(
|
||||
const uint8_t* entry_buffer, size_t entry_buffer_length,
|
||||
SavedUsageEntry* entry) {
|
||||
@@ -390,9 +464,47 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry(
|
||||
LOGE("Invalid entry buffer size: %zu", entry_buffer_length);
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
const uint8_t* buffer = entry_buffer;
|
||||
size_t buffer_length = entry_buffer_length;
|
||||
SavedCommonInfo common_info;
|
||||
OEMCryptoResult result = OPKI_UnpackSavedCommonInfo(
|
||||
entry_buffer, entry_buffer_length, &common_info);
|
||||
OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
|
||||
#ifdef USE_REF_BACK_COMPAT
|
||||
// To deal with reference-wrapped v16 usage tables, extract the info and
|
||||
// rewrap. Then continue as usual with the rewrapped blob.
|
||||
|
||||
// Reference usage table doesn't have any kind of common_info struct at the
|
||||
// top. Previously, fail immediatley if the common_info doesn't match
|
||||
// expectations. Now, check if none of the common_info data makes sense, then
|
||||
// try v16.
|
||||
result = OPKI_UnpackSavedCommonInfo(buffer, buffer_length, &common_info);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
bool should_try_ref_compat =
|
||||
(common_info.file_type != SIGNED_USAGE_TABLE_HEADER ||
|
||||
common_info.file_type != SIGNED_USAGE_TABLE_ENTRY ||
|
||||
common_info.file_type != USAGE_TABLE_HEADER ||
|
||||
common_info.file_type != USAGE_TABLE_ENTRY);
|
||||
|
||||
SavedUsageEntry temp_entry = {0};
|
||||
uint8_t rewrapped_entry[sizeof(SignedSavedUsageEntry)] = {0};
|
||||
size_t rewrapped_entry_size = sizeof(rewrapped_entry);
|
||||
if (should_try_ref_compat) {
|
||||
// Unwrap into current OPK struct
|
||||
result =
|
||||
WTPI_RefCompat_UnwrapUsageEntry(buffer, buffer_length, &temp_entry);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Rewrap struct with current wrapping scheme
|
||||
result =
|
||||
EncryptAndSignEntry(&temp_entry, rewrapped_entry, rewrapped_entry_size);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
|
||||
// Point variables to use new rewrapped data, start the process again
|
||||
buffer = rewrapped_entry;
|
||||
buffer_length = rewrapped_entry_size;
|
||||
}
|
||||
#endif
|
||||
result = OPKI_UnpackSavedCommonInfo(buffer, buffer_length, &common_info);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (common_info.file_type != SIGNED_USAGE_TABLE_ENTRY) {
|
||||
/* We were given the wrong file. */
|
||||
@@ -402,19 +514,18 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry(
|
||||
if (common_info.format_version == LEGACY_FILE_FORMAT_VERSION) {
|
||||
LOGD("Legacy usage entry format version %u detected.",
|
||||
LEGACY_FILE_FORMAT_VERSION);
|
||||
return DecryptAndVerifyEntry_Legacy(entry_buffer, entry_buffer_length,
|
||||
entry);
|
||||
return DecryptAndVerifyEntry_Legacy(buffer, buffer_length, entry);
|
||||
}
|
||||
if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) {
|
||||
LOGE("Bad signed entry format version: %u", common_info.format_version);
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (entry_buffer_length < OPKI_SignedEntrySize()) {
|
||||
if (buffer_length < OPKI_SignedEntrySize()) {
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
SignedSavedUsageEntry signed_entry;
|
||||
result = OPKI_UnpackSignedUsageEntry(entry_buffer, entry_buffer_length,
|
||||
&signed_entry);
|
||||
result =
|
||||
OPKI_UnpackSignedUsageEntry(buffer, buffer_length, &signed_entry);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
if (signed_entry.buffer_size > sizeof(signed_entry.buffer)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -537,7 +648,8 @@ OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer,
|
||||
}
|
||||
*header_buffer_length = size;
|
||||
if (!header_buffer) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
/* Make sure there are no entries that are currently tied to an open session.
|
||||
/* Make sure there are no entries that are currently tied to an open
|
||||
* session.
|
||||
*/
|
||||
for (size_t i = 0; i < MAX_NUMBER_OF_USAGE_ENTRIES; i++) {
|
||||
if (g_usage_table.entries[i]) {
|
||||
@@ -546,8 +658,8 @@ OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
/* Clear the table before we check the state -- we want to have an empty table
|
||||
* after this function whether there was an error or not. */
|
||||
/* Clear the table before we check the state -- we want to have an empty
|
||||
* table after this function whether there was an error or not. */
|
||||
ClearTable();
|
||||
if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) {
|
||||
/* Something went wrong. The system should give up and re-init. */
|
||||
@@ -556,11 +668,12 @@ OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer,
|
||||
} else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
|
||||
/* Creating a new header when one was already active. This is OK but
|
||||
* rare. The system would do this to delete all the entries -- maybe on
|
||||
* reprovisioning, but there are other reasons. However, we want to keep the
|
||||
* same generation number. So we don't try to load it. */
|
||||
* reprovisioning, but there are other reasons. However, we want to keep
|
||||
* the same generation number. So we don't try to load it. */
|
||||
} else if (g_usage_table_state ==
|
||||
USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE) {
|
||||
/* We are creating a brand new table. Try to load the generation number. */
|
||||
/* We are creating a brand new table. Try to load the generation number.
|
||||
*/
|
||||
if (WTPI_LoadGenerationNumber(&g_usage_table.master_generation_number) !=
|
||||
OEMCrypto_SUCCESS) {
|
||||
LOGE("Failed to load generation number");
|
||||
@@ -568,35 +681,38 @@ OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer,
|
||||
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
|
||||
}
|
||||
} else {
|
||||
/* Only other valid state is not initialized, which is not valid for this
|
||||
* function. */
|
||||
/* Only other valid state is not initialized, which is not valid for
|
||||
* this function. */
|
||||
LOGE("Usage table is not initialized");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
g_usage_table_state = USAGE_TABLE_ACTIVE_STATE;
|
||||
g_counter_info.master_generation_number =
|
||||
g_usage_table.master_generation_number;
|
||||
return EncryptAndSignHeader(header_buffer, *header_buffer_length);
|
||||
return EncryptAndSignHeader(header_buffer, *header_buffer_length,
|
||||
&g_usage_table);
|
||||
}
|
||||
|
||||
/* Load a usage table header. */
|
||||
OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
|
||||
size_t buffer_length) {
|
||||
/* Clear the table before we check the state -- we want to have an empty table
|
||||
* before we load a new one, or we want an empty one if there is an error. */
|
||||
/* Clear the table before we check the state -- we want to have an empty
|
||||
* table before we load a new one, or we want an empty one if there is an
|
||||
* error. */
|
||||
ClearTable();
|
||||
if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) {
|
||||
/* Something went wrong. The system should give up and re-init. */
|
||||
LOGE("Usage table is in error state");
|
||||
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
|
||||
} else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
|
||||
/* Loading a header when one was already active is an indication that the
|
||||
* system was going to terminate, but changed its mind - e.g. because
|
||||
* delayed termination was canceled. We keep the existing generation
|
||||
* numbers. */
|
||||
/* Loading a header when one was already active is an indication that
|
||||
* the system was going to terminate, but changed its mind - e.g.
|
||||
* because delayed termination was canceled. We keep the existing
|
||||
* generation numbers. */
|
||||
} else if (g_usage_table_state ==
|
||||
USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE) {
|
||||
/* We are loading a brand new table. Try to load the generation number. */
|
||||
/* We are loading a brand new table. Try to load the generation number.
|
||||
*/
|
||||
if (WTPI_LoadGenerationNumber(&g_usage_table.master_generation_number) !=
|
||||
OEMCrypto_SUCCESS) {
|
||||
LOGE("Failed to load generation number");
|
||||
@@ -604,8 +720,8 @@ OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
|
||||
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
|
||||
}
|
||||
} else {
|
||||
/* Only other valid state is not initialized, which is not valid for this
|
||||
* function. */
|
||||
/* Only other valid state is not initialized, which is not valid for
|
||||
* this function. */
|
||||
LOGE("Usage table is not initialized");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
@@ -621,10 +737,11 @@ OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
|
||||
result = OEMCrypto_WARNING_GENERATION_SKEW;
|
||||
} else if (g_usage_table.master_generation_number !=
|
||||
header.master_generation_number) {
|
||||
/* Skew of more than 1 is an error. Clean the table and return an error. */
|
||||
/* Skew of more than 1 is an error. Clean the table and return an error.
|
||||
*/
|
||||
ClearTable();
|
||||
/* Leave the state as active, so that the generation number is kept. It is
|
||||
* not a security risk to leave an empty usage header in memory. */
|
||||
/* Leave the state as active, so that the generation number is kept. It
|
||||
* is not a security risk to leave an empty usage header in memory. */
|
||||
g_usage_table_state = USAGE_TABLE_ACTIVE_STATE;
|
||||
return OEMCrypto_ERROR_GENERATION_SKEW;
|
||||
}
|
||||
@@ -637,10 +754,10 @@ OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Grabs a free active usage entry off of the free list of active entries. It
|
||||
* updates the free list, the active_entry_map, and the session for a new
|
||||
* entry. It does NOT update the entry's generation number and does NOT update
|
||||
* the usage header. It does sanity checks. */
|
||||
/* Grabs a free active usage entry off of the free list of active entries.
|
||||
* It updates the free list, the active_entry_map, and the session for a new
|
||||
* entry. It does NOT update the entry's generation number and does NOT
|
||||
* update the usage header. It does sanity checks. */
|
||||
NO_IGNORE_RESULT static OEMCryptoResult GrabEntry(OEMCrypto_SESSION session_id,
|
||||
uint32_t usage_entry_number,
|
||||
UsageEntry** entry_ptr) {
|
||||
@@ -671,9 +788,9 @@ NO_IGNORE_RESULT static OEMCryptoResult GrabEntry(OEMCrypto_SESSION session_id,
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
/** Release an active entry and put it back onto the free list. This does not
|
||||
* save any data. It is usually done with the session is closing or when loading
|
||||
* an entry generated an error. */
|
||||
/** Release an active entry and put it back onto the free list. This does
|
||||
* not save any data. It is usually done with the session is closing or when
|
||||
* loading an entry generated an error. */
|
||||
void OPKI_ReleaseEntry(OEMCrypto_SESSION session_id) {
|
||||
if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
|
||||
/* Remove from active entry map. */
|
||||
@@ -686,9 +803,9 @@ void OPKI_ReleaseEntry(OEMCrypto_SESSION session_id) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new usage entry and tie it to |session|. The new entry will have an
|
||||
* entry number at the end of the array of all entries in the header, but it
|
||||
* could be anywhere in the array of active entries. */
|
||||
/* Create a new usage entry and tie it to |session|. The new entry will have
|
||||
* an entry number at the end of the array of all entries in the header, but
|
||||
* it could be anywhere in the array of active entries. */
|
||||
OEMCryptoResult OPKI_CreateNewUsageEntry(OEMCrypto_SESSION session_id,
|
||||
uint32_t* usage_entry_number) {
|
||||
if (!usage_entry_number) {
|
||||
@@ -711,8 +828,8 @@ OEMCryptoResult OPKI_CreateNewUsageEntry(OEMCrypto_SESSION session_id,
|
||||
OEMCryptoResult result = GrabEntry(session_id, new_index, &entry);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
g_usage_table.table_size++;
|
||||
/* Update the generation numbers. Increment the master GN, and then copy to
|
||||
* the entry. Also copy to the header's array of entries. */
|
||||
/* Update the generation numbers. Increment the master GN, and then copy
|
||||
* to the entry. Also copy to the header's array of entries. */
|
||||
g_usage_table.master_generation_number++;
|
||||
entry->data.generation_number = g_usage_table.master_generation_number;
|
||||
g_usage_table.generation_numbers[new_index] =
|
||||
@@ -806,14 +923,16 @@ OEMCryptoResult OPKI_LoadUsageEntry(OEMCrypto_SESSION session_id,
|
||||
OPKI_ReleaseEntry(session_id);
|
||||
return result;
|
||||
}
|
||||
/* check generation number against header's table of generation numbers. */
|
||||
/* check generation number against header's table of generation numbers.
|
||||
*/
|
||||
uint64_t entry_gn = entry->data.generation_number;
|
||||
uint64_t header_gn = g_usage_table.generation_numbers[usage_entry_number];
|
||||
if (entry_gn + 1 == header_gn || entry_gn - 1 == header_gn) {
|
||||
/* Skew of 1 is a warning, but we continue on. */
|
||||
result = OEMCrypto_WARNING_GENERATION_SKEW;
|
||||
} else if (entry_gn != header_gn) {
|
||||
/* Skew of more than 1 is an error. Clean the table and return an error. */
|
||||
/* Skew of more than 1 is an error. Clean the table and return an error.
|
||||
*/
|
||||
OPKI_ReleaseEntry(session_id);
|
||||
return OEMCrypto_ERROR_GENERATION_SKEW;
|
||||
}
|
||||
@@ -855,7 +974,8 @@ OEMCryptoResult OPKI_UpdateUsageEntry(OEMCrypto_SESSION session_id,
|
||||
/* The new generation numbers. */
|
||||
result = RollGenerationNumber(entry);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result = EncryptAndSignHeader(header_buffer, *header_buffer_length);
|
||||
result = EncryptAndSignHeader(header_buffer, *header_buffer_length,
|
||||
&g_usage_table);
|
||||
if (result != OEMCrypto_SUCCESS) return result;
|
||||
result =
|
||||
EncryptAndSignEntry(&entry->data, entry_buffer, *entry_buffer_length);
|
||||
@@ -894,8 +1014,9 @@ OEMCryptoResult OPKI_SetUsageEntryPST(OEMCrypto_SESSION session_id,
|
||||
memcpy(entry->data.pst, pst, pst_length);
|
||||
entry->data.pst_length = pst_length;
|
||||
|
||||
/* The PST is set when the license is loaded. This will be removed in v16 when
|
||||
* we use the time of license signed instead of time of license loaded. */
|
||||
/* The PST is set when the license is loaded. This will be removed in v16
|
||||
* when we use the time of license signed instead of time of license
|
||||
* loaded. */
|
||||
uint64_t now;
|
||||
OEMCryptoResult result = WTPI_GetTrustedTime(&now);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
@@ -1054,8 +1175,8 @@ OEMCryptoResult OPKI_ReportUsage(OEMCrypto_SESSION session_id,
|
||||
}
|
||||
*buffer_length = length_needed;
|
||||
|
||||
// We delay checking these to allow the above length-returning code to work
|
||||
// without passing in these parameters.
|
||||
// We delay checking these to allow the above length-returning code to
|
||||
// work without passing in these parameters.
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(pst);
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(buffer);
|
||||
|
||||
@@ -1241,7 +1362,8 @@ OEMCryptoResult OPKI_ShrinkUsageTableHeader(uint32_t new_entry_count,
|
||||
g_usage_table.generation_numbers[i] = 0;
|
||||
}
|
||||
g_usage_table.table_size = new_entry_count;
|
||||
return EncryptAndSignHeader(header_buffer, *header_buffer_length);
|
||||
return EncryptAndSignHeader(header_buffer, *header_buffer_length,
|
||||
&g_usage_table);
|
||||
}
|
||||
|
||||
NO_IGNORE_RESULT OEMCryptoResult OPKI_UpdateClockValues(
|
||||
|
||||
58
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_ref_compat_interface.h
Normal file
58
oemcrypto/opk/oemcrypto_ta/wtpi/wtpi_ref_compat_interface.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#ifndef OEMCRYPTO_TA_WTPI_REF_COMPAT_INTERFACE_H_
|
||||
#define OEMCRYPTO_TA_WTPI_REF_COMPAT_INTERFACE_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_serialized_usage_table.h"
|
||||
#include "wtpi_crypto_and_key_management_interface_layer1.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup ref_compat
|
||||
*
|
||||
* This interface includes functions for backwards compatibility with 16.X
|
||||
* reference wrapped blobs.
|
||||
*
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unwraps WrappedRSA struct from 16.X reference code. Returns clear RSA key.
|
||||
*
|
||||
*
|
||||
* @param[in] wrapped_data: wrapped key
|
||||
* @param[in] wrapped_data_length: length of wrapped key
|
||||
* @param[out] clear_data: output for clear key
|
||||
* @param[in,out] clear_data_length: size of output buffer. If it is too small
|
||||
* for the output unwrapped data, this will be set to the required size.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS success
|
||||
* @retval OEMCrypto_ERROR_INVALID_CONTEXT NULL pointer inputs
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER clear_data_length is too small
|
||||
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise
|
||||
*/
|
||||
OEMCryptoResult WTPI_RefCompat_UnwrapRSA(const uint8_t* wrapped_data,
|
||||
size_t wrapped_data_length,
|
||||
uint8_t* clear_data,
|
||||
size_t* clear_data_length);
|
||||
|
||||
OEMCryptoResult WTPI_RefCompat_UnwrapUsageHeader(const uint8_t* wrapped_data,
|
||||
size_t wrapped_data_length,
|
||||
SavedUsageHeader* clear);
|
||||
|
||||
OEMCryptoResult WTPI_RefCompat_UnwrapUsageEntry(const uint8_t* wrapped_data,
|
||||
size_t wrapped_data_length,
|
||||
SavedUsageEntry* clear);
|
||||
/// @}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OEMCRYPTO_TA_WTPI_REF_COMPAT_INTERFACE_H_ */
|
||||
342
oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_ref_compat.c
Normal file
342
oemcrypto/opk/oemcrypto_ta/wtpi_reference/wtpi_ref_compat.c
Normal file
@@ -0,0 +1,342 @@
|
||||
/* Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
|
||||
source code may only be used and distributed under the Widevine
|
||||
License Agreement. */
|
||||
|
||||
#include "wtpi_ref_compat_interface.h"
|
||||
|
||||
#include "string.h"
|
||||
|
||||
#include "odk_util.h"
|
||||
#include "oemcrypto_check_macros.h"
|
||||
#include "oemcrypto_key_types.h"
|
||||
#include "oemcrypto_serialized_usage_table.h"
|
||||
#include "wtpi_device_key_access_interface.h"
|
||||
#include "wtpi_device_key_interface.h"
|
||||
#include "wtpi_memory_interface.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t signature[MAC_KEY_SIZE];
|
||||
uint8_t context[MAC_KEY_SIZE]; // to-be-signed data starts here
|
||||
uint8_t iv[KEY_IV_SIZE];
|
||||
uint8_t enc_rsa_key[]; // encrypted data starts here
|
||||
} RefWrappedRSAKey;
|
||||
|
||||
typedef struct {
|
||||
int64_t generation_number;
|
||||
int64_t time_of_license_received;
|
||||
int64_t time_of_first_decrypt;
|
||||
int64_t time_of_last_decrypt;
|
||||
enum OEMCrypto_Usage_Entry_Status status;
|
||||
uint8_t mac_key_server[MAC_KEY_SIZE];
|
||||
uint8_t mac_key_client[MAC_KEY_SIZE];
|
||||
uint32_t index;
|
||||
uint8_t pst[MAX_PST_LENGTH + 1];
|
||||
uint8_t pst_length;
|
||||
} RefStoredUsageEntry;
|
||||
|
||||
#define REF_USAGE_VERIFICATION_MAGIC_LENGTH 8
|
||||
typedef struct {
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
uint8_t iv[SHA256_DIGEST_LENGTH]; // to-be-signed data starts here
|
||||
uint8_t verification[REF_USAGE_VERIFICATION_MAGIC_LENGTH]; // encrypted data
|
||||
// starts here
|
||||
RefStoredUsageEntry data;
|
||||
} RefSignedEntryBlock;
|
||||
|
||||
typedef struct {
|
||||
uint8_t signature[SHA256_DIGEST_LENGTH];
|
||||
uint8_t iv[SHA256_DIGEST_LENGTH]; // to-be-signed data starts here
|
||||
uint8_t verification[REF_USAGE_VERIFICATION_MAGIC_LENGTH]; // encrypted data
|
||||
// starts here
|
||||
int64_t master_generation;
|
||||
uint64_t count;
|
||||
} RefSignedHeaderBlock;
|
||||
|
||||
OEMCryptoResult WTPI_RefCompat_UnwrapRSA(const uint8_t* wrapped_data,
|
||||
size_t wrapped_data_length,
|
||||
uint8_t* clear_data,
|
||||
size_t* clear_data_length) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(wrapped_data);
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(clear_data);
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(clear_data_length);
|
||||
|
||||
if (wrapped_data_length < sizeof(RefWrappedRSAKey))
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
|
||||
if (*clear_data_length < PKCS8_DRM_KEY_MAX_SIZE)
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
|
||||
size_t rsa_key_len = wrapped_data_length - sizeof(RefWrappedRSAKey);
|
||||
if (*clear_data_length < rsa_key_len) {
|
||||
*clear_data_length = rsa_key_len;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
|
||||
// Derive mac/enc keys from device key
|
||||
const uint8_t* device_key = WTPI_GetDeviceKey();
|
||||
KeySize device_key_size = WTPI_GetDeviceKeySize();
|
||||
|
||||
WTPI_K1_SymmetricKey_Handle device_key_handle;
|
||||
OEMCryptoResult result = WTPI_K1_CreateKeyHandle(
|
||||
device_key, device_key_size, DERIVING_KEY, &device_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t context[MAC_KEY_SIZE] = {0};
|
||||
const RefWrappedRSAKey* const wrapped_key =
|
||||
(const RefWrappedRSAKey*)wrapped_data;
|
||||
memcpy(context, wrapped_key->context, sizeof(wrapped_key->context));
|
||||
|
||||
uint8_t empty[1] = {0};
|
||||
uint8_t counter = 1;
|
||||
|
||||
WTPI_K1_SymmetricKey_Handle mac_server_key_handle = NULL;
|
||||
WTPI_K1_SymmetricKey_Handle enc_key_handle = NULL;
|
||||
result = WTPI_K1_DeriveKeyFromKeyHandle(
|
||||
device_key_handle, counter, empty, 0, context, sizeof(context), empty, 0,
|
||||
MAC_KEY_SERVER, MAC_KEY_SIZE, &mac_server_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
result = WTPI_K1_DeriveKeyFromKeyHandle(
|
||||
device_key_handle, counter, empty, 0, context, sizeof(context), empty, 0,
|
||||
ENCRYPTION_KEY, KEY_SIZE_128, &enc_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Use derived mac_key_server to HMAC SHA256 verify
|
||||
// Verify everything after the signature
|
||||
const uint8_t* data_to_verify = &(wrapped_key->context[0]);
|
||||
size_t data_to_verify_length =
|
||||
wrapped_data_length - sizeof(wrapped_key->signature);
|
||||
result =
|
||||
WTPI_C1_HMAC_SHA256_Verify(mac_server_key_handle, data_to_verify,
|
||||
data_to_verify_length, wrapped_key->signature);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Use derived enc_key to AES CBC 128 decrypt
|
||||
size_t data_to_decrypt_length =
|
||||
wrapped_data_length - sizeof(wrapped_key->signature) -
|
||||
sizeof(wrapped_key->context) - sizeof(wrapped_key->iv);
|
||||
result = WTPI_C1_AESCBCDecrypt(
|
||||
enc_key_handle, KEY_SIZE_128, wrapped_key->enc_rsa_key,
|
||||
data_to_decrypt_length, wrapped_key->iv, clear_data);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*clear_data_length = rsa_key_len;
|
||||
|
||||
cleanup:
|
||||
WTPI_K1_FreeKeyHandle(device_key_handle);
|
||||
WTPI_K1_FreeKeyHandle(mac_server_key_handle);
|
||||
WTPI_K1_FreeKeyHandle(enc_key_handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_RefCompat_UnwrapUsageHeader(const uint8_t* wrapped_data,
|
||||
size_t wrapped_data_length,
|
||||
SavedUsageHeader* clear) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(wrapped_data);
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(clear);
|
||||
|
||||
if (wrapped_data_length < sizeof(RefSignedHeaderBlock)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
// RefSignedHeaderBlock has two issues that make things inconvenient
|
||||
// 1. Doesn't include generation number array
|
||||
// 2. Has a uint64_t which makes alignement 8, so we can't directly cast the
|
||||
// uint8_t* pointer to RefSignedHeaderBlock*
|
||||
uint8_t clear_data[sizeof(RefSignedHeaderBlock) +
|
||||
MAX_NUMBER_OF_USAGE_ENTRIES * sizeof(uint64_t)] = {0};
|
||||
RefSignedHeaderBlock* clear_header = (RefSignedHeaderBlock*)clear_data;
|
||||
|
||||
const uint8_t* wrapped_sig = wrapped_data;
|
||||
const uint8_t* wrapped_iv = &wrapped_data[sizeof(clear_header->signature)];
|
||||
const uint8_t* wrapped_enc =
|
||||
&wrapped_data[sizeof(clear_header->signature) + sizeof(clear_header->iv)];
|
||||
|
||||
memcpy(clear_header->signature, wrapped_sig, sizeof(clear_header->signature));
|
||||
memcpy(clear_header->iv, wrapped_iv, sizeof(clear_header->iv));
|
||||
|
||||
// Get device key
|
||||
const uint8_t* device_key = WTPI_GetDeviceKey();
|
||||
KeySize device_key_size = WTPI_GetDeviceKeySize();
|
||||
WTPI_K1_SymmetricKey_Handle device_key_handle;
|
||||
OEMCryptoResult result = WTPI_K1_CreateKeyHandle(
|
||||
device_key, device_key_size, DERIVING_KEY, &device_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// HMAC verify with key. Data starts at buffer->iv (after buffer->signature)
|
||||
// and goes to end. Compare with buffer->signature
|
||||
result = WTPI_C1_HMAC_SHA256_Verify(
|
||||
device_key_handle, wrapped_iv,
|
||||
wrapped_data_length - sizeof(clear_header->signature), wrapped_sig);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Decrypt AES
|
||||
const size_t encrypted_length = wrapped_data_length -
|
||||
sizeof(clear_header->signature) -
|
||||
sizeof(clear_header->iv);
|
||||
result = WTPI_C1_AESCBCDecrypt(device_key_handle, device_key_size,
|
||||
wrapped_enc, encrypted_length, wrapped_iv,
|
||||
clear_header->verification);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Compare verification string
|
||||
const char* kHeaderVerification = "USEHEADR";
|
||||
if (crypto_memcmp(kHeaderVerification, clear_header->verification,
|
||||
sizeof(clear_header->verification)) != 0) {
|
||||
result = OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Move everything to newer "SavedUsageHeader" struct
|
||||
*clear = (SavedUsageHeader){
|
||||
.common_info =
|
||||
{
|
||||
.file_type = USAGE_TABLE_HEADER,
|
||||
.format_version = CURRENT_FILE_FORMAT_VERSION,
|
||||
},
|
||||
|
||||
.master_generation_number = clear_header->master_generation,
|
||||
.table_size =
|
||||
clear_header->count & 0xFFFFFFFF, // intentionally truncating count
|
||||
};
|
||||
|
||||
memcpy(clear->generation_numbers, &clear_data[sizeof(RefSignedHeaderBlock)],
|
||||
clear_header->count * sizeof(uint64_t));
|
||||
|
||||
cleanup:
|
||||
WTPI_K1_FreeKeyHandle(device_key_handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OEMCryptoResult WTPI_RefCompat_UnwrapUsageEntry(const uint8_t* wrapped_data,
|
||||
size_t wrapped_data_length,
|
||||
SavedUsageEntry* clear) {
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(wrapped_data);
|
||||
RETURN_INVALID_CONTEXT_IF_NULL(clear);
|
||||
|
||||
if (wrapped_data_length < sizeof(RefSignedEntryBlock)) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
uint8_t clear_data[sizeof(RefSignedEntryBlock)] = {0};
|
||||
RefSignedEntryBlock* clear_entry = (RefSignedEntryBlock*)clear_data;
|
||||
|
||||
const uint8_t* wrapped_sig = wrapped_data;
|
||||
const uint8_t* wrapped_iv = &wrapped_data[sizeof(clear_entry->signature)];
|
||||
const uint8_t* wrapped_enc =
|
||||
&wrapped_data[sizeof(clear_entry->signature) + sizeof(clear_entry->iv)];
|
||||
|
||||
memcpy(clear_entry->signature, wrapped_sig, sizeof(clear_entry->signature));
|
||||
memcpy(clear_entry->iv, wrapped_iv, sizeof(clear_entry->iv));
|
||||
|
||||
// Get device key
|
||||
const uint8_t* device_key = WTPI_GetDeviceKey();
|
||||
KeySize device_key_size = WTPI_GetDeviceKeySize();
|
||||
WTPI_K1_SymmetricKey_Handle device_key_handle;
|
||||
OEMCryptoResult result = WTPI_K1_CreateKeyHandle(
|
||||
device_key, device_key_size, DERIVING_KEY, &device_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// HMAC verify with key. Data starts at buffer[SHA256_DIGEST_LENGTH] and goes
|
||||
// to end Compare with first SHA256_DIGEST_LENGTH bytes
|
||||
result = WTPI_C1_HMAC_SHA256_Verify(
|
||||
device_key_handle, wrapped_iv,
|
||||
wrapped_data_length - sizeof(clear_entry->signature), wrapped_sig);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Decrypt AES
|
||||
const size_t encrypted_length = wrapped_data_length -
|
||||
sizeof(clear_entry->signature) -
|
||||
sizeof(clear_entry->iv);
|
||||
result = WTPI_C1_AESCBCDecrypt(device_key_handle, device_key_size,
|
||||
wrapped_enc, encrypted_length, wrapped_iv,
|
||||
clear_entry->verification);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Compare verification string
|
||||
const char* kentryVerification = "USEENTRY";
|
||||
if (crypto_memcmp(kentryVerification, clear_entry->verification,
|
||||
sizeof(clear_entry->verification)) != 0) {
|
||||
result = OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Copy values to new struct
|
||||
*clear = (SavedUsageEntry){
|
||||
.common_info =
|
||||
{
|
||||
.file_type = USAGE_TABLE_ENTRY,
|
||||
.format_version = CURRENT_FILE_FORMAT_VERSION,
|
||||
},
|
||||
.index = clear_entry->data.index,
|
||||
.generation_number = clear_entry->data.generation_number,
|
||||
.time_of_license_received = clear_entry->data.time_of_license_received,
|
||||
.time_of_first_decrypt = clear_entry->data.time_of_first_decrypt,
|
||||
.time_of_last_decrypt = clear_entry->data.time_of_last_decrypt,
|
||||
.status = clear_entry->data.status,
|
||||
.pst_length = clear_entry->data.pst_length,
|
||||
};
|
||||
|
||||
memcpy(clear->pst, clear_entry->data.pst, sizeof(clear->pst));
|
||||
|
||||
// MAC keys need to be wrapped for OPK
|
||||
WTPI_K1_SymmetricKey_Handle mac_server_key_handle;
|
||||
result = WTPI_K1_CreateKeyHandle(clear_entry->data.mac_key_server,
|
||||
sizeof(clear_entry->data.mac_key_server),
|
||||
MAC_KEY_SERVER, &mac_server_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
result = WTPI_K1_WrapKey(DEVICE_KEY_WRAP_MAC_KEY, mac_server_key_handle,
|
||||
MAC_KEY_SERVER, clear->mac_key_server,
|
||||
WRAPPED_MAC_KEY_SIZE);
|
||||
WTPI_K1_FreeKeyHandle(mac_server_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Failed to wrap mac key server");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
WTPI_K1_SymmetricKey_Handle mac_client_key_handle;
|
||||
result = WTPI_K1_CreateKeyHandle(clear_entry->data.mac_key_client,
|
||||
sizeof(clear_entry->data.mac_key_client),
|
||||
MAC_KEY_CLIENT, &mac_client_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
result = WTPI_K1_WrapKey(DEVICE_KEY_WRAP_MAC_KEY, mac_client_key_handle,
|
||||
MAC_KEY_CLIENT, clear->mac_key_client,
|
||||
WRAPPED_MAC_KEY_SIZE);
|
||||
WTPI_K1_FreeKeyHandle(mac_client_key_handle);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
LOGE("Failed to wrap mac key client");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
WTPI_K1_FreeKeyHandle(device_key_handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
|
||||
#include "oemcrypto_ref_compat_test.h"
|
||||
|
||||
#include "bcc_validator.h"
|
||||
#include "device_info_validator.h"
|
||||
#include "log.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "platform.h"
|
||||
#include "signed_csr_payload_validator.h"
|
||||
#include "test_sleep.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
TEST_F(OEMCryptoProv2ReferenceWrap, LoadWrappedKey) {
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
|
||||
sts = OEMCrypto_LoadDRMPrivateKey(
|
||||
session_.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key.data(),
|
||||
wrapped_rsa_key.size());
|
||||
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv2ReferenceWrap, LoadWrappedUsageTable) {
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
sts = OEMCrypto_LoadUsageTableHeader(wrapped_usage_header.data(),
|
||||
wrapped_usage_header.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
|
||||
sts = OEMCrypto_LoadUsageEntry(session_.session_id(), 0,
|
||||
wrapped_usage_entry.data(),
|
||||
wrapped_usage_entry.size());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
} // namespace wvoec
|
||||
@@ -0,0 +1,203 @@
|
||||
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
//
|
||||
// Test data generated by v16.3 reference code for wrapped RSA keys and wrapped
|
||||
// usage tables. Used to test optional support in OPK to unwrap legacy formats.
|
||||
//
|
||||
#ifndef CDM_OEMCRYPTO_REFERENCE_COMPATIBILITY_TEST_
|
||||
#define CDM_OEMCRYPTO_REFERENCE_COMPATIBILITY_TEST_
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oec_extra_test_keys.h"
|
||||
#include "oemcrypto_basic_test.h"
|
||||
#include "oemcrypto_license_test.h"
|
||||
#include "oemcrypto_resource_test.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
/* The uncommented test data below was created with certain device qualities
|
||||
from the reference, which should be matched in the OPK in order to pass tests.
|
||||
All of these qualities should be covered in the below comments. Most notably:
|
||||
1. device key is 0xE4FF574C322EF53426212CB3ED37F35E. This comes from the 7912
|
||||
test keybox.
|
||||
2. generation number loaded from file is 0x601839158FACD420
|
||||
*/
|
||||
|
||||
// RSA key
|
||||
// std::vector<uint8_t> master_key =
|
||||
// wvutil::a2b_hex("E4FF574C322EF53426212CB3ED37F35E");
|
||||
// std::vector<uint8_t> mac_key_context = wvutil::a2b_hex(
|
||||
// "2A20B95C28A52646C7192489DC9CF12257E1EB3B929D6B7D699F598BB87C4D4A");
|
||||
// std::vector<uint8_t> enc_key_context = wvutil::a2b_hex(
|
||||
// "2A20B95C28A52646C7192489DC9CF12257E1EB3B929D6B7D699F598BB87C4D4A");
|
||||
// std::vector<uint8_t> mac_key_server = wvutil::a2b_hex(
|
||||
// "6A98FB80AC7A91C7F25D5A38C8748EA85B2E390B2AEA65E5DBC06DFC8402923A");
|
||||
// std::vector<uint8_t> mac_key_client = wvutil::a2b_hex(
|
||||
// "BE5CE5801E1EA7FF235951570603CFFB4C405662EA1D641D959CC6F14431C32F");
|
||||
// std::vector<uint8_t> enc_key =
|
||||
// wvutil::a2b_hex("6A98FB80AC7A91C7F25D5A38C8748EA8");
|
||||
// std::vector<uint8_t> enc_rsa_key_iv =
|
||||
// wvutil::a2b_hex("AE32578914AC408D00AB0156F38214CD");
|
||||
// std::vector<uint8_t> signature = wvutil::a2b_hex(
|
||||
// "5173F3B523AA4DE5E5FE7D26E2913EAB0A312224D0C84B6C903E869764553555");
|
||||
std::vector<uint8_t> clear_rsa_key = wvutil::a2b_hex(
|
||||
"5349474E00000002308204BC020100300D06092A864886F70D01010105000482"
|
||||
"04A6308204A20201000282010100A700366065DCBD545A2A40B4E1159458114F"
|
||||
"9458DDDEA71F3C2CE08809296157675E567EEE278F59349A2AAA9DB44EFAA76A"
|
||||
"D4C97A53C14E9FE334F73DB7C910474F28DA3FCE317BFD0610EBF7BE92F9AFFB"
|
||||
"3E68DAEE1A644CF329F2739E39D8F66FD8B28082718EB5A4F2C23ECD0ACAB604"
|
||||
"CD9A138B54735425548CBE987A67ADDAB34EB3FA82A84A679856575471CD127F"
|
||||
"EDA301C06A8B24039688BE97662ABC53C98306515A88651318E43AED6BF1615B"
|
||||
"4CC81EF4C2AE085E2D5FF8127FA2FCBB211830DAFE40FB01CA2E370ECEDD7687"
|
||||
"82460B3A778FC072072C7F9D1E865BED2729DF039762EF44D35B3DDB9C5E1B7B"
|
||||
"39B40B6D046BBBBB2C5FCFB37A050203010001028201005E796549A57679F905"
|
||||
"450FF403BDA47D29D5DE3363D8B8AC97EB3F5E55E87DF3E73B5C2D546736D61D"
|
||||
"46F5CA2D8B3A7EDC4538797E65715F1C5E79B140CDFEC5E1C16B78044E8E79F9"
|
||||
"0AFC79B15EB360E3687BC6EFCB714CBAA7795C7A81D171E7002113E255690E75"
|
||||
"BE09C34FA9C968220E978D896EF1E8887AD1D9095DD32878250B1C477325CC21"
|
||||
"B6DAC6245AD0371446C79469E4436F47DE00334D8F9572FA68711766121A8727"
|
||||
"F7EF7EE03558F24D6F3501AA96E23D5113869C79D0B7B664E8866550BFCC2753"
|
||||
"1F51D4CABEF5DD7770980FEEA896075F456A7A0D039C4F29F606F35D586C47D0"
|
||||
"96A90317BB4EC921E0ACCD7878B2FE81B25153A61F984502818100CF738CBE6D"
|
||||
"452D0C0B5D5C6C7578CC3548B698F1B964608C43EB85AB04B67D1B717506E2DA"
|
||||
"84682E7F4CE373B4DE514BB651867BD0E64DF3D1CF1AFE7F3A83BAB3E1FF5413"
|
||||
"93D79C2780B71E649EF7322B4629F7F8186CF74ABE4BEE96908FA216226ACC48"
|
||||
"067463437F2722443C2D3B62F11CB427338526604816CBEFF8CD3702818100CE"
|
||||
"15436E4B0FF93F87C3414597B149C2192387E4241C64E528CB431014140E19CB"
|
||||
"BBDBFD119D1768786D6170633AA1B3F3A75B0EFFB76111549199E591322DEB3F"
|
||||
"D83EF7D4CBD2A341C1EEC69213EB7F4258F4D0B2741D8E8746CD14B816ADB5BD"
|
||||
"0D6C955A16BFE953DAFBED835167A955AB54029520A6681753A8EA43E5B0A302"
|
||||
"8180679C32833957FF73B089648BD6F00A2DE2AF301C2A97F3909AAB9B0B1B43"
|
||||
"79A0A73DE7BE8D9CEBDBAD40DDA90080B8E1B3A16C2592E433B2BEEB4D74265F"
|
||||
"37439C6C17760A812082A1482C2D45DC0F624332BBEB5941F9CA58CE4A665354"
|
||||
"C828101E087116D802714158D456CCF5B131A3ED008509BF3595412940198335"
|
||||
"246902818055100BCC3BA9753D16E1AE50766394494CAD10CB47687CF0E5DCB8"
|
||||
"6AAB8EF79F082C1B8AA2B98FCEEC5E61A8CD1C87604AC31A5FDF8726C6CB7C69"
|
||||
"E48B01065922FA344B81873C036D020A77E615D8CFA768266CFA2BD9835A2D0C"
|
||||
"3B701CD448BEA70AD9BEDCC30C2133B366FF1C1BC89676E86F4474BC9B1C7DC8"
|
||||
"AC21A86E370281802C7CAD1E75F6691DE7A6CA747D67C8652866C443A6BD4057"
|
||||
"AEB7652C52F9E4C7817B56A3D20DE83370CF0684B34E4450756196864BB62BAD"
|
||||
"F0AD57D0370D1D3550CB69223929B93AD329230260F7AB3040DA8E4D457026F4"
|
||||
"A20DD0645D473C18F4D4529500AE846B47B23C82D37253DE722CF7C12236D918"
|
||||
"56FE392833E0DB030808080808080808");
|
||||
std::vector<uint8_t> wrapped_rsa_key = wvutil::a2b_hex(
|
||||
"5173F3B523AA4DE5E5FE7D26E2913EAB0A312224D0C84B6C903E869764553555"
|
||||
"2A20B95C28A52646C7192489DC9CF12257E1EB3B929D6B7D699F598BB87C4D4A"
|
||||
"AE32578914AC408D00AB0156F38214CDD920B0EC5083C3916445A6FBC7D74786"
|
||||
"6181E9B06C761D94DC6DD07FE1FA4BB691F71791AF0530A193E312AB89ACB62C"
|
||||
"07312E07326BE72BC9368CDD20929E3D779486602AF40E3749C8C89E06018986"
|
||||
"E4CE4A84907561FFE2D25C20B3E7F7A31DD548D220A17D1BC0F5E92E47DA5E51"
|
||||
"6B3EA63BCCB4764C4B4CE0DD5B712A0D0161B035ADEE0D78A93BDE3B1ADDB2A4"
|
||||
"46A57D40BDEAA9FF5ADBDF40C17FC40A269E3A0EF435A0E0C416DC9D946DDF56"
|
||||
"75197B132BCE0D3D433F1B32F66AC35379861555F733CED6D5BDF145DA7975D4"
|
||||
"AA40DCB44CB6677E1C896AB69EFB6DC43972D9B93E3E9F7DE46A05A4083646C8"
|
||||
"AD228FFC3FE38335F32B6CDA2A99E98B7F66E1E175F4AB2E07C696CA50D40922"
|
||||
"4AD46697D669FD40DFD67C08016CE68FC18BD3E6F23903D7A5B780DC74DC13F3"
|
||||
"AEE716F78A5A2D11546C37680D391D4FB4EB094B1AC70E0950AD65FE7598A622"
|
||||
"F4C50B18B72CC8E26FB3D6BA87C732C5705E102A92D3DFC7F7D57F16448E1EE0"
|
||||
"C696FE8E05239EBBC7C6951374EA04BFB76F49BED4B3A04C38C9C91E2218C7EE"
|
||||
"2D9E1B4ED37A3907395380A8400CDBBDE7E6BFF4ED4F0BED3D839A26707B6CA9"
|
||||
"262F4DE1905B34B50C878EF6860A2E649E2960C0860A0B09E5DC22692EC0A4AA"
|
||||
"0E247A2DB168C1BC979128F29E54F268E0E3CC5A0231838B76A61C97779E5D7A"
|
||||
"2955D33498537878FAC36623A3099EDDB04B0AE748563AB2AC0C0614D776437F"
|
||||
"E9F7C0D149196EF5CB15BE496CE8DC004F0AF79FADB5FB33677CA350A6999367"
|
||||
"535404D9417BED0E794CED1251A9C2F5170DEAF8B0A4EAE8CD5CA3E65E7F5087"
|
||||
"441DFCF48CBC97D6C89A39BFEAC0F45AD8D2977EE04E3B0AC5EF61303361DF05"
|
||||
"E182A0C3BF0B1CB7C7DDA499CE2E10904DEE0BC58F6F8A6D1BEE3A389C5308D2"
|
||||
"0AAFD655A6003914BA5A00ED3BCEC64DE21033BE8E01F0594D1C2899609FDA23"
|
||||
"89DD4F77873C39C9D51C6A8F4967734F68A793EE6F167BCCE66F96473CBDD2E5"
|
||||
"2D0A85124468C260D0F0E1C87C85F32B6443346550DAB72FDCA727BC9C725BA4"
|
||||
"E3A87522C42BE4C2622F90F47A0E344E9C5CE0D9427211834DA4ABDFBDFB0B7C"
|
||||
"8DF7A9DBFB3606DE7053FB28EDC4ED3074F08308F9F77BF983D798E0AFCA2761"
|
||||
"9D12522A1E7346A40A30C0785EE1C271FC40E611FCF6AD633B34F729DFFDB2E0"
|
||||
"ABBF04CC0B5D564351006855BDDCAE20BA4240AF6599949A41F3FF461358D215"
|
||||
"C81CCA82BF89DE581C1F29EA083FE5C8CAEE53237AF872D78086B3219E134369"
|
||||
"372A02EA7640A1DCF7FE82AFE8F43376CA2B3374BA78CBE63CA34D1D5F4EDBEA"
|
||||
"045311A1BD28D10F1346F72DFAF81C33CA6417D666102CE0A9EE0277F077633D"
|
||||
"6A3FE5206E8D4A61869417532C219C4F9C1311FF18708893F365D612AFC3A19A"
|
||||
"98D1A25E058AB03FA6DED49165352E8CE268790A8343CAE9FAE33090078A9475"
|
||||
"7589F369E559A303EE637725DB73FECC38754764BE37018A21A083FB509F4078"
|
||||
"3F3A2F5116343320865BAF15F2020FBDC073BC9A92EAA2A2ECE315F22D3FD15B"
|
||||
"E2303324769ED52511C8DED664C5657A8DAD823DD7EC80B1DF8427AF5D211620"
|
||||
"6D9A31DFEA34A867B81A6BBBBEF04ED9C1B1E9FDDEB5EE6CF42EAAC6794609A9"
|
||||
"BC5AB70CC4891D984DBC7210A1016FBB68B6222213D5F6D011A8309A15A5B04E"
|
||||
"5C0131463D899324091E951252FD6DE232E38816478E227ABFBED403900D18F9");
|
||||
|
||||
// Usage entry
|
||||
// std::vector<uint8_t> key =
|
||||
// wvutil::a2b_hex("E4FF574C322EF53426212CB3ED37F35E"); std::vector<uint8_t>
|
||||
// iv_buffer = wvutil::a2b_hex("B8D3247322351E0A4E06BB5E0BD87151");
|
||||
// std::vector<uint8_t> signature =
|
||||
// wvutil::a2b_hex("333F3815A67D92F91BF8E31479DEC4C19897059C12B6D4F10BA1CA886382DE75");
|
||||
std::vector<uint8_t> clear_usage_entry = wvutil::a2b_hex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"555345454E5452591FD4AC8F1539186007EE7C66000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000");
|
||||
std::vector<uint8_t> wrapped_usage_entry = wvutil::a2b_hex(
|
||||
"333F3815A67D92F91BF8E31479DEC4C19897059C12B6D4F10BA1CA886382DE75"
|
||||
"FA8917E41699C6A7BCD2D2F9F63F85E700000000000000000000000000000000"
|
||||
"EE26BDE360B7C505D65A3B83384B9AACD9F64E6BB05135291AD6D337C2682C34"
|
||||
"F8B5727BF1AB93E3E905D247B9E1717944547D6598723EE3B5BFBC0B0E6F9465"
|
||||
"65FEC2E3869CC5878D9C0F44D72B6E2F967C9AFB4A2AA78D03B90993F7C1F80D"
|
||||
"35A85BF48D625F315FE71270E6E04D586E7954FA6DB4B818962FEECEE317C0EB"
|
||||
"5D29778EB125B2886AE0BDD0049BB5EB5C202C939DA0DF5B65BFEB5CB9690FE6"
|
||||
"C4757A41E51F08CE9839F960B9DC71013C611A46A374A1500B5D02206A183A2A"
|
||||
"0EFE55AB3FE3F5FA353F1E9E75F747A6A81291C71D6F1DEBAB55A76A05564A32"
|
||||
"B28A538872CEC35F8BE21313DE0BCB309EB49B4300CA95249169033AEDA72ED2"
|
||||
"54368DB0F434ACCBC958D06043800F0515CC0C618C5D54FE4804984985189450"
|
||||
"FE9B105FC3605838B624CC5602FB93FC668B34DF3F6D754CC6D57DD08DB3614F"
|
||||
"194A889CD9101A22D001F87D79DF8680F72FFEA213B1927FEC3A1E04C82F4A72"
|
||||
"749718B3423DD3D040AD50D5AFCBCA85B8D3247322351E0A4E06BB5E0BD87151");
|
||||
|
||||
// Usage header
|
||||
// std::vector<uint8_t> key =
|
||||
// wvutil::a2b_hex("E4FF574C322EF53426212CB3ED37F35E"); std::vector<uint8_t>
|
||||
// iv_buffer = wvutil::a2b_hex("CBD0C898BFC17A6D8455F1F3F746F4FE");
|
||||
// std::vector<uint8_t> signature =
|
||||
// wvutil::a2b_hex("DC6E448F7AC3A43F423310FAF0818A3C73BD067B4414707D8E0656F6CCDFF4AC");
|
||||
// std::vector<uint8_t> master_generation_number =
|
||||
// wvutil::a2b_hex("601839158FACD420")
|
||||
std::vector<uint8_t> clear_usage_header = wvutil::a2b_hex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"555345484541445220D4AC8F1539186001000000000000001FD4AC8F15391860");
|
||||
std::vector<uint8_t> wrapped_usage_header = wvutil::a2b_hex(
|
||||
"DC6E448F7AC3A43F423310FAF0818A3C73BD067B4414707D8E0656F6CCDFF4AC"
|
||||
"60E0A2725D56A0C93EE5067F1BDF8EB100000000000000000000000000000000"
|
||||
"C0847CFCB2AE7DAD634578FBF07870F9CBD0C898BFC17A6D8455F1F3F746F4FE");
|
||||
|
||||
class OEMCryptoProv2ReferenceWrap : public OEMCryptoClientTest {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
OEMCryptoClientTest::SetUp();
|
||||
ASSERT_NO_FATAL_FAILURE(session_.open());
|
||||
if (global_features.provisioning_method != OEMCrypto_Keybox) {
|
||||
GTEST_SKIP() << "Test for prov 2 devices only";
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
ASSERT_NO_FATAL_FAILURE(session_.close());
|
||||
OEMCryptoClientTest::TearDown();
|
||||
}
|
||||
|
||||
Session session_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEMCRYPTO_REFERENCE_COMPATIBILITY_TEST_
|
||||
Reference in New Issue
Block a user