Add v16.3 unwrap compatibility

This commit is contained in:
Matt Feddersen
2024-07-25 14:15:55 -07:00
parent 9fe69a896c
commit 482b0923ca
6 changed files with 925 additions and 102 deletions

View File

@@ -43,6 +43,10 @@
#include "wtpi_provisioning_4_interface.h" #include "wtpi_provisioning_4_interface.h"
#include "wtpi_root_of_trust_interface_layer1.h" #include "wtpi_root_of_trust_interface_layer1.h"
#ifdef USE_REF_BACK_COMPAT
# include "wtpi_ref_compat_interface.h"
#endif
typedef enum GlobalSystemState { typedef enum GlobalSystemState {
SYSTEM_NOT_INITIALIZED = (int)0x2ca77206, SYSTEM_NOT_INITIALIZED = (int)0x2ca77206,
SYSTEM_INITIALIZED = (int)0xf57fab49 SYSTEM_INITIALIZED = (int)0xf57fab49
@@ -641,7 +645,7 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
result = OEMCrypto_GetNumberOfOpenSessions(&count); result = OEMCrypto_GetNumberOfOpenSessions(&count);
if (result != OEMCrypto_SUCCESS) goto cleanup; if (result != OEMCrypto_SUCCESS) goto cleanup;
if (count == 1) { // the session was the first session to be opened. if (count == 1) { // the session was the first session to be opened.
OEMCrypto_HDCP_Capability max_hdcp_level = WTPI_MaxHDCPCapability(); OEMCrypto_HDCP_Capability max_hdcp_level = WTPI_MaxHDCPCapability();
result = WTPI_SetHDCPLevel(max_hdcp_level); result = WTPI_SetHDCPLevel(max_hdcp_level);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
@@ -690,7 +694,7 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
OEMCryptoResult hdcp_result = OEMCrypto_GetNumberOfOpenSessions(&count); OEMCryptoResult hdcp_result = OEMCrypto_GetNumberOfOpenSessions(&count);
if (hdcp_result != OEMCrypto_SUCCESS) return result; if (hdcp_result != OEMCrypto_SUCCESS) return result;
if (count == 0) { // the session was the last one opened. if (count == 0) { // the session was the last one opened.
hdcp_result = WTPI_SetHDCPLevel(HDCP_NONE); hdcp_result = WTPI_SetHDCPLevel(HDCP_NONE);
if (hdcp_result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { if (hdcp_result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
LOGD("WTPI_SetHDCPLevel() not implemented."); LOGD("WTPI_SetHDCPLevel() not implemented.");
@@ -3050,16 +3054,71 @@ OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session,
result = OPKI_CheckStatePreCall(session_context, API_LOADDRMPRIVATEKEY); result = OPKI_CheckStatePreCall(session_context, API_LOADDRMPRIVATEKEY);
if (result != OEMCrypto_SUCCESS) return result; 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; WTPI_AsymmetricKey_Handle private_key_handle;
uint32_t allowed_schemes; uint32_t allowed_schemes;
result = WTPI_UnwrapIntoAsymmetricKeyHandle( result = WTPI_UnwrapIntoAsymmetricKeyHandle(
DEVICE_KEY_WRAP_DRM_CERT, wrapped_drm_key, wrapped_drm_key_length, DEVICE_KEY_WRAP_DRM_CERT, wrapped_drm_key, wrapped_drm_key_length,
drm_key_type, &private_key_handle, &allowed_schemes); 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) { if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to unwrap DRM private key into key handle with result: %u", LOGE("Failed to unwrap DRM private key into key handle with result: %u",
result); result);
return result; return result;
} }
#endif
size_t signature_size; size_t signature_size;
result = WTPI_GetSignatureSize(private_key_handle, &signature_size); result = WTPI_GetSignatureSize(private_key_handle, &signature_size);
WTPI_FreeAsymmetricKeyHandle(private_key_handle); WTPI_FreeAsymmetricKeyHandle(private_key_handle);
@@ -3068,9 +3127,8 @@ OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session,
return result; return result;
} }
result = result = OPKI_LoadDRMKey(session_context, drm_key_type, drm_key,
OPKI_LoadDRMKey(session_context, drm_key_type, wrapped_drm_key, drm_key_length, signature_size, allowed_schemes);
wrapped_drm_key_length, signature_size, allowed_schemes);
if (result != OEMCrypto_SUCCESS) { if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to load DRM key"); LOGE("Failed to load DRM key");
goto cleanup; goto cleanup;
@@ -3338,8 +3396,8 @@ OEMCryptoResult OEMCrypto_LoadProvisioningCast(
RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length); 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 // 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 // derive new keys because that call will erase the drm private key when
// finished using it. // it's finished using it.
if (wrapped_private_key != NULL && *wrapped_private_key_length > 0) { if (wrapped_private_key != NULL && *wrapped_private_key_length > 0) {
result = DeriveKeysFromSessionKey(session_context, provision_request, result = DeriveKeysFromSessionKey(session_context, provision_request,
provision_request_length, derivation_key, provision_request_length, derivation_key,
@@ -3785,10 +3843,11 @@ OEMCryptoResult OEMCrypto_RemoveEntitledKeySession(
OEMCryptoResult result = OEMCryptoResult result =
GetSessionContext(key_session, &session_context, &key_session_context); GetSessionContext(key_session, &session_context, &key_session_context);
if (result != OEMCrypto_SUCCESS) { if (result != OEMCrypto_SUCCESS) {
// In case that the entitlement session is closed prior to the entitled key // In case that the entitlement session is closed prior to the entitled
// session, the result of OPKI_GetSession() will not be OEMCrypto_SUCCESS, // key session, the result of OPKI_GetSession() will not be
// and that's ok. This entitled key session should already be released when // OEMCrypto_SUCCESS, and that's ok. This entitled key session should
// its entitlement session was closed. Just return success here. // already be released when its entitlement session was closed. Just
// return success here.
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
ABORT_IF(session_context == NULL, 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 /* Validations to be done before re-associating an entitled key session to a
* new entitlement session: * new entitlement session:
* 1. at least one entitled key is supposed to have its entitlement key found * 1. at least one entitled key is supposed to have its entitlement key
* in the new session * found in the new session
* 2. for any entitled key, if its entitlement key is found in the new * 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, then the key control block should remain unchanged in the new
* session, compared to the one in the existing entitlement session */ * session, compared to the one in the existing entitlement session */
@@ -4198,8 +4257,8 @@ OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession(
* with. */ * with. */
const EntitlementKeyInfo* key_info = const EntitlementKeyInfo* key_info =
&key_session_context->entitlement_keys[i]; &key_session_context->entitlement_keys[i];
/* Finds the entitlement key in the new entitlement session. It is ok if an /* Finds the entitlement key in the new entitlement session. It is ok if
* entitled key doesn't have an entitlement key in the new entitlement * an entitled key doesn't have an entitlement key in the new entitlement
* session. The entitled key will be ignored in this case. */ * session. The entitled key will be ignored in this case. */
for (uint32_t k = 0; k < entitlement_session_context->num_entitlement_keys; for (uint32_t k = 0; k < entitlement_session_context->num_entitlement_keys;
k++) { k++) {
@@ -4338,16 +4397,16 @@ OEMCryptoResult OEMCrypto_LoadCasECMKeys(
goto cleanup; 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 = uint32_t entitled_content_key_index =
key_session->num_entitled_content_keys; key_session->num_entitled_content_keys;
/* It prefers to reuse an existing content key index that is entitled by the /* It prefers to reuse an existing content key index that is entitled by
* same entitlement key if one exists already, but will allocate a new index * the same entitlement key if one exists already, but will allocate a new
* if there are none that can be reused. * index if there are none that can be reused. The block below searches
* The block below searches whether there is an existing content key * whether there is an existing content key entitled by the same
* entitled by the same entitlement key, with the same parity as the key to * entitlement key, with the same parity as the key to be loaded, and will
* be loaded, and will reuse the index to load the new content key if there * reuse the index to load the new content key if there is one. */
* is one. */
for (uint32_t k = 0; k < key_session->num_entitled_content_keys; k++) { for (uint32_t k = 0; k < key_session->num_entitled_content_keys; k++) {
const EntitlementKeyInfo* key_info = &key_session->entitlement_keys[k]; const EntitlementKeyInfo* key_info = &key_session->entitlement_keys[k];
if (key_info->entitlement_key_id_size == entitlement_key->key_id_size && if (key_info->entitlement_key_id_size == entitlement_key->key_id_size &&

View File

@@ -18,6 +18,7 @@
#include "wtpi_device_key_interface.h" #include "wtpi_device_key_interface.h"
#include "wtpi_generation_number_interface.h" #include "wtpi_generation_number_interface.h"
#include "wtpi_logging_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 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; return NULL;
} }
/* This serializes, encrypts, and signs the current header into the given static OEMCryptoResult InitHeader(SavedUsageHeader* header,
* buffer. It is an error if the buffer is not big enough. The header fields const UsageTable* usage_table) {
* and the master generation number in the current header will be updated. */ if (!header || !usage_table) return OEMCrypto_ERROR_INVALID_CONTEXT;
NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignHeader(
uint8_t* header_buffer, size_t header_buffer_length) { *header = (SavedUsageHeader){
SavedUsageHeader header = {
.common_info = .common_info =
{ {
.file_type = USAGE_TABLE_HEADER, .file_type = USAGE_TABLE_HEADER,
.format_version = CURRENT_FILE_FORMAT_VERSION, .format_version = CURRENT_FILE_FORMAT_VERSION,
}, },
.master_generation_number = g_usage_table.master_generation_number, .master_generation_number = usage_table->master_generation_number,
.table_size = g_usage_table.table_size, .table_size = usage_table->table_size,
}; };
memcpy(header.generation_numbers, g_usage_table.generation_numbers,
header.table_size * sizeof(uint64_t)); memcpy(header->generation_numbers, usage_table->generation_numbers,
SignedSavedUsageHeader signed_header = { 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 = .common_info =
{ {
.file_type = SIGNED_USAGE_TABLE_HEADER, .file_type = SIGNED_USAGE_TABLE_HEADER,
.format_version = CURRENT_FILE_FORMAT_VERSION, .format_version = CURRENT_FILE_FORMAT_VERSION,
}, },
.buffer_size = sizeof(signed_header.buffer), .buffer_size = sizeof(signed_header->buffer),
.buffer = {0}, .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}; uint8_t temp_buffer[PADDED_HEADER_BUFFER_SIZE] = {0};
OEMCryptoResult result = 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; if (result != OEMCrypto_SUCCESS) return result;
result = WTPI_EncryptAndSign(DEVICE_KEY_WRAP_USAGE_TABLE, temp_buffer, result = WTPI_EncryptAndSign(DEVICE_KEY_WRAP_USAGE_TABLE, temp_buffer,
sizeof(temp_buffer), signed_header.buffer, sizeof(temp_buffer), signed_header.buffer,
&signed_header.buffer_size); &signed_header.buffer_size);
if (result != OEMCrypto_SUCCESS) return result; 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; return result;
} }
@@ -241,9 +277,48 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader(
LOGE("Invalid header buffer size: %zu", header_buffer_length); LOGE("Invalid header buffer size: %zu", header_buffer_length);
return OEMCrypto_ERROR_SHORT_BUFFER; return OEMCrypto_ERROR_SHORT_BUFFER;
} }
const uint8_t* buffer = header_buffer;
size_t buffer_length = header_buffer_length;
SavedCommonInfo common_info; SavedCommonInfo common_info;
OEMCryptoResult result = OPKI_UnpackSavedCommonInfo( OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
header_buffer, header_buffer_length, &common_info);
#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 (result != OEMCrypto_SUCCESS) return result;
if (common_info.file_type != SIGNED_USAGE_TABLE_HEADER) { if (common_info.file_type != SIGNED_USAGE_TABLE_HEADER) {
/* We were given the wrong file. */ /* 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) { if (common_info.format_version == LEGACY_FILE_FORMAT_VERSION) {
LOGD("Legacy usage header format version %u detected.", LOGD("Legacy usage header format version %u detected.",
LEGACY_FILE_FORMAT_VERSION); LEGACY_FILE_FORMAT_VERSION);
return DecryptAndVerifyHeader_Legacy(header_buffer, header_buffer_length, return DecryptAndVerifyHeader_Legacy(buffer, buffer_length, header);
header);
} }
if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) { if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) {
LOGE("Bad signed usage header format version: %u", LOGE("Bad signed usage header format version: %u",
@@ -262,8 +336,7 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyHeader(
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
} }
SignedSavedUsageHeader signed_header; SignedSavedUsageHeader signed_header;
result = OPKI_UnpackSignedUsageHeader(header_buffer, header_buffer_length, result = OPKI_UnpackSignedUsageHeader(buffer, buffer_length, &signed_header);
&signed_header);
if (result != OEMCrypto_SUCCESS) return result; if (result != OEMCrypto_SUCCESS) return result;
if (signed_header.buffer_size > sizeof(signed_header.buffer)) { if (signed_header.buffer_size > sizeof(signed_header.buffer)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE; 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 /* 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 * buffer. It is an error if the buffer is not big enough. The header
* the entry will be updated, but the generation number will not. */ * fields in the entry will be updated, but the generation number will not.
*/
NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignEntry( NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignEntry(
SavedUsageEntry* entry, uint8_t* entry_buffer, size_t entry_buffer_length) { SavedUsageEntry* entry, uint8_t* entry_buffer, size_t entry_buffer_length) {
entry->common_info.file_type = USAGE_TABLE_ENTRY; entry->common_info.file_type = USAGE_TABLE_ENTRY;
@@ -325,10 +399,10 @@ NO_IGNORE_RESULT static OEMCryptoResult EncryptAndSignEntry(
return result; return result;
} }
/* This decrypts and deserializes a usage table entry from the given buffer in /* This decrypts and deserializes a usage table entry from the given buffer
* the legacy format. The signature of the buffer is verified. The generation * in the legacy format. The signature of the buffer is verified. The
* number is verified by the calling function. If the buffer is not big enough * generation number is verified by the calling function. If the buffer is
* the error OEMCrypto_ERROR_SHORT_BUFFER is returned. */ * not big enough the error OEMCrypto_ERROR_SHORT_BUFFER is returned. */
NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry_Legacy( NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry_Legacy(
const uint8_t* entry_buffer, size_t entry_buffer_length, const uint8_t* entry_buffer, size_t entry_buffer_length,
SavedUsageEntry* entry) { SavedUsageEntry* entry) {
@@ -376,10 +450,10 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry_Legacy(
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/* This decrypts and deserializes a usage table entry from the given buffer. The /* This decrypts and deserializes a usage table entry from the given buffer.
* signature of the buffer is verified. The generation number is verified by the * The signature of the buffer is verified. The generation number is
* calling function. If the buffer is not big enough the error * verified by the calling function. If the buffer is not big enough the
* OEMCrypto_ERROR_SHORT_BUFFER is returned. */ * error OEMCrypto_ERROR_SHORT_BUFFER is returned. */
NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry( NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry(
const uint8_t* entry_buffer, size_t entry_buffer_length, const uint8_t* entry_buffer, size_t entry_buffer_length,
SavedUsageEntry* entry) { SavedUsageEntry* entry) {
@@ -390,9 +464,47 @@ NO_IGNORE_RESULT static OEMCryptoResult DecryptAndVerifyEntry(
LOGE("Invalid entry buffer size: %zu", entry_buffer_length); LOGE("Invalid entry buffer size: %zu", entry_buffer_length);
return OEMCrypto_ERROR_SHORT_BUFFER; return OEMCrypto_ERROR_SHORT_BUFFER;
} }
const uint8_t* buffer = entry_buffer;
size_t buffer_length = entry_buffer_length;
SavedCommonInfo common_info; SavedCommonInfo common_info;
OEMCryptoResult result = OPKI_UnpackSavedCommonInfo( OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
entry_buffer, entry_buffer_length, &common_info);
#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 (result != OEMCrypto_SUCCESS) return result;
if (common_info.file_type != SIGNED_USAGE_TABLE_ENTRY) { if (common_info.file_type != SIGNED_USAGE_TABLE_ENTRY) {
/* We were given the wrong file. */ /* 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) { if (common_info.format_version == LEGACY_FILE_FORMAT_VERSION) {
LOGD("Legacy usage entry format version %u detected.", LOGD("Legacy usage entry format version %u detected.",
LEGACY_FILE_FORMAT_VERSION); LEGACY_FILE_FORMAT_VERSION);
return DecryptAndVerifyEntry_Legacy(entry_buffer, entry_buffer_length, return DecryptAndVerifyEntry_Legacy(buffer, buffer_length, entry);
entry);
} }
if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) { if (common_info.format_version != CURRENT_FILE_FORMAT_VERSION) {
LOGE("Bad signed entry format version: %u", common_info.format_version); LOGE("Bad signed entry format version: %u", common_info.format_version);
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
} }
if (entry_buffer_length < OPKI_SignedEntrySize()) { if (buffer_length < OPKI_SignedEntrySize()) {
return OEMCrypto_ERROR_SHORT_BUFFER; return OEMCrypto_ERROR_SHORT_BUFFER;
} }
SignedSavedUsageEntry signed_entry; SignedSavedUsageEntry signed_entry;
result = OPKI_UnpackSignedUsageEntry(entry_buffer, entry_buffer_length, result =
&signed_entry); OPKI_UnpackSignedUsageEntry(buffer, buffer_length, &signed_entry);
if (result != OEMCrypto_SUCCESS) return result; if (result != OEMCrypto_SUCCESS) return result;
if (signed_entry.buffer_size > sizeof(signed_entry.buffer)) { if (signed_entry.buffer_size > sizeof(signed_entry.buffer)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -537,7 +648,8 @@ OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer,
} }
*header_buffer_length = size; *header_buffer_length = size;
if (!header_buffer) return OEMCrypto_ERROR_INVALID_CONTEXT; 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++) { for (size_t i = 0; i < MAX_NUMBER_OF_USAGE_ENTRIES; i++) {
if (g_usage_table.entries[i]) { if (g_usage_table.entries[i]) {
@@ -546,8 +658,8 @@ OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer,
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
} }
} }
/* Clear the table before we check the state -- we want to have an empty table /* Clear the table before we check the state -- we want to have an empty
* after this function whether there was an error or not. */ * table after this function whether there was an error or not. */
ClearTable(); ClearTable();
if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) { if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) {
/* Something went wrong. The system should give up and re-init. */ /* 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) { } else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
/* Creating a new header when one was already active. This is OK but /* 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 * 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 * reprovisioning, but there are other reasons. However, we want to keep
* same generation number. So we don't try to load it. */ * the same generation number. So we don't try to load it. */
} else if (g_usage_table_state == } else if (g_usage_table_state ==
USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_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) != if (WTPI_LoadGenerationNumber(&g_usage_table.master_generation_number) !=
OEMCrypto_SUCCESS) { OEMCrypto_SUCCESS) {
LOGE("Failed to load generation number"); LOGE("Failed to load generation number");
@@ -568,35 +681,38 @@ OEMCryptoResult OPKI_CreateUsageTableHeader(uint8_t* header_buffer,
return OEMCrypto_ERROR_SYSTEM_INVALIDATED; return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
} }
} else { } else {
/* Only other valid state is not initialized, which is not valid for this /* Only other valid state is not initialized, which is not valid for
* function. */ * this function. */
LOGE("Usage table is not initialized"); LOGE("Usage table is not initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
} }
g_usage_table_state = USAGE_TABLE_ACTIVE_STATE; g_usage_table_state = USAGE_TABLE_ACTIVE_STATE;
g_counter_info.master_generation_number = g_counter_info.master_generation_number =
g_usage_table.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. */ /* Load a usage table header. */
OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer, OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
size_t buffer_length) { size_t buffer_length) {
/* Clear the table before we check the state -- we want to have an empty table /* Clear the table before we check the state -- we want to have an empty
* before we load a new one, or we want an empty one if there is an error. */ * table before we load a new one, or we want an empty one if there is an
* error. */
ClearTable(); ClearTable();
if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) { if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) {
/* Something went wrong. The system should give up and re-init. */ /* Something went wrong. The system should give up and re-init. */
LOGE("Usage table is in error state"); LOGE("Usage table is in error state");
return OEMCrypto_ERROR_SYSTEM_INVALIDATED; return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
} else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) { } else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
/* Loading a header when one was already active is an indication that the /* Loading a header when one was already active is an indication that
* system was going to terminate, but changed its mind - e.g. because * the system was going to terminate, but changed its mind - e.g.
* delayed termination was canceled. We keep the existing generation * because delayed termination was canceled. We keep the existing
* numbers. */ * generation numbers. */
} else if (g_usage_table_state == } else if (g_usage_table_state ==
USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_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) != if (WTPI_LoadGenerationNumber(&g_usage_table.master_generation_number) !=
OEMCrypto_SUCCESS) { OEMCrypto_SUCCESS) {
LOGE("Failed to load generation number"); LOGE("Failed to load generation number");
@@ -604,8 +720,8 @@ OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
return OEMCrypto_ERROR_SYSTEM_INVALIDATED; return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
} }
} else { } else {
/* Only other valid state is not initialized, which is not valid for this /* Only other valid state is not initialized, which is not valid for
* function. */ * this function. */
LOGE("Usage table is not initialized"); LOGE("Usage table is not initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE; return OEMCrypto_ERROR_UNKNOWN_FAILURE;
} }
@@ -621,10 +737,11 @@ OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
result = OEMCrypto_WARNING_GENERATION_SKEW; result = OEMCrypto_WARNING_GENERATION_SKEW;
} else if (g_usage_table.master_generation_number != } else if (g_usage_table.master_generation_number !=
header.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(); ClearTable();
/* Leave the state as active, so that the generation number is kept. It is /* Leave the state as active, so that the generation number is kept. It
* not a security risk to leave an empty usage header in memory. */ * is not a security risk to leave an empty usage header in memory. */
g_usage_table_state = USAGE_TABLE_ACTIVE_STATE; g_usage_table_state = USAGE_TABLE_ACTIVE_STATE;
return OEMCrypto_ERROR_GENERATION_SKEW; return OEMCrypto_ERROR_GENERATION_SKEW;
} }
@@ -637,10 +754,10 @@ OEMCryptoResult OPKI_LoadUsageTableHeader(const uint8_t* buffer,
return result; return result;
} }
/* Grabs a free active usage entry off of the free list of active entries. It /* Grabs a free active usage entry off of the free list of active entries.
* updates the free list, the active_entry_map, and the session for a new * 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 * entry. It does NOT update the entry's generation number and does NOT
* the usage header. It does sanity checks. */ * update the usage header. It does sanity checks. */
NO_IGNORE_RESULT static OEMCryptoResult GrabEntry(OEMCrypto_SESSION session_id, NO_IGNORE_RESULT static OEMCryptoResult GrabEntry(OEMCrypto_SESSION session_id,
uint32_t usage_entry_number, uint32_t usage_entry_number,
UsageEntry** entry_ptr) { UsageEntry** entry_ptr) {
@@ -671,9 +788,9 @@ NO_IGNORE_RESULT static OEMCryptoResult GrabEntry(OEMCrypto_SESSION session_id,
return OEMCrypto_SUCCESS; return OEMCrypto_SUCCESS;
} }
/** Release an active entry and put it back onto the free list. This does not /** Release an active entry and put it back onto the free list. This does
* save any data. It is usually done with the session is closing or when loading * not save any data. It is usually done with the session is closing or when
* an entry generated an error. */ * loading an entry generated an error. */
void OPKI_ReleaseEntry(OEMCrypto_SESSION session_id) { void OPKI_ReleaseEntry(OEMCrypto_SESSION session_id) {
if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) { if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
/* Remove from active entry map. */ /* 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 /* Create a new usage entry and tie it to |session|. The new entry will have
* entry number at the end of the array of all entries in the header, but it * an entry number at the end of the array of all entries in the header, but
* could be anywhere in the array of active entries. */ * it could be anywhere in the array of active entries. */
OEMCryptoResult OPKI_CreateNewUsageEntry(OEMCrypto_SESSION session_id, OEMCryptoResult OPKI_CreateNewUsageEntry(OEMCrypto_SESSION session_id,
uint32_t* usage_entry_number) { uint32_t* usage_entry_number) {
if (!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); OEMCryptoResult result = GrabEntry(session_id, new_index, &entry);
if (result != OEMCrypto_SUCCESS) return result; if (result != OEMCrypto_SUCCESS) return result;
g_usage_table.table_size++; g_usage_table.table_size++;
/* Update the generation numbers. Increment the master GN, and then copy to /* Update the generation numbers. Increment the master GN, and then copy
* the entry. Also copy to the header's array of entries. */ * to the entry. Also copy to the header's array of entries. */
g_usage_table.master_generation_number++; g_usage_table.master_generation_number++;
entry->data.generation_number = g_usage_table.master_generation_number; entry->data.generation_number = g_usage_table.master_generation_number;
g_usage_table.generation_numbers[new_index] = g_usage_table.generation_numbers[new_index] =
@@ -806,14 +923,16 @@ OEMCryptoResult OPKI_LoadUsageEntry(OEMCrypto_SESSION session_id,
OPKI_ReleaseEntry(session_id); OPKI_ReleaseEntry(session_id);
return result; 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 entry_gn = entry->data.generation_number;
uint64_t header_gn = g_usage_table.generation_numbers[usage_entry_number]; uint64_t header_gn = g_usage_table.generation_numbers[usage_entry_number];
if (entry_gn + 1 == header_gn || entry_gn - 1 == header_gn) { if (entry_gn + 1 == header_gn || entry_gn - 1 == header_gn) {
/* Skew of 1 is a warning, but we continue on. */ /* Skew of 1 is a warning, but we continue on. */
result = OEMCrypto_WARNING_GENERATION_SKEW; result = OEMCrypto_WARNING_GENERATION_SKEW;
} else if (entry_gn != header_gn) { } 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); OPKI_ReleaseEntry(session_id);
return OEMCrypto_ERROR_GENERATION_SKEW; return OEMCrypto_ERROR_GENERATION_SKEW;
} }
@@ -855,7 +974,8 @@ OEMCryptoResult OPKI_UpdateUsageEntry(OEMCrypto_SESSION session_id,
/* The new generation numbers. */ /* The new generation numbers. */
result = RollGenerationNumber(entry); result = RollGenerationNumber(entry);
if (result != OEMCrypto_SUCCESS) return result; 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; if (result != OEMCrypto_SUCCESS) return result;
result = result =
EncryptAndSignEntry(&entry->data, entry_buffer, *entry_buffer_length); 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); memcpy(entry->data.pst, pst, pst_length);
entry->data.pst_length = pst_length; entry->data.pst_length = pst_length;
/* The PST is set when the license is loaded. This will be removed in v16 when /* The PST is set when the license is loaded. This will be removed in v16
* we use the time of license signed instead of time of license loaded. */ * when we use the time of license signed instead of time of license
* loaded. */
uint64_t now; uint64_t now;
OEMCryptoResult result = WTPI_GetTrustedTime(&now); OEMCryptoResult result = WTPI_GetTrustedTime(&now);
if (result != OEMCrypto_SUCCESS) { if (result != OEMCrypto_SUCCESS) {
@@ -1054,8 +1175,8 @@ OEMCryptoResult OPKI_ReportUsage(OEMCrypto_SESSION session_id,
} }
*buffer_length = length_needed; *buffer_length = length_needed;
// We delay checking these to allow the above length-returning code to work // We delay checking these to allow the above length-returning code to
// without passing in these parameters. // work without passing in these parameters.
RETURN_INVALID_CONTEXT_IF_NULL(pst); RETURN_INVALID_CONTEXT_IF_NULL(pst);
RETURN_INVALID_CONTEXT_IF_NULL(buffer); 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.generation_numbers[i] = 0;
} }
g_usage_table.table_size = new_entry_count; 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( NO_IGNORE_RESULT OEMCryptoResult OPKI_UpdateClockValues(

View 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_ */

View 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;
}

View File

@@ -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

View File

@@ -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_