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_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
@@ -641,7 +645,7 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
result = OEMCrypto_GetNumberOfOpenSessions(&count);
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();
result = WTPI_SetHDCPLevel(max_hdcp_level);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
@@ -690,7 +694,7 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
OEMCryptoResult hdcp_result = OEMCrypto_GetNumberOfOpenSessions(&count);
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);
if (hdcp_result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
LOGD("WTPI_SetHDCPLevel() not implemented.");
@@ -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 &&

View File

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

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_