Files
oemcrypto/oemcrypto/opk/oemcrypto_ta/oemcrypto.c
Fred Gylys-Colwell 684711a20f Second OPK Partner Beta v16 Release
See https://developers.google.com/widevine/drm/client/opk
for documentation and an integration guide.

See CHANGELOG.md for details about recent changes.
2022-02-25 12:02:41 -08:00

3058 lines
121 KiB
C

/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
source code may only be used and distributed under the Widevine
License Agreement. */
#include "OEMCryptoCENC.h"
#include <inttypes.h>
#include <string.h>
#include "odk.h"
#include "odk_endian.h"
#include "odk_util.h"
#include "oemcrypto_api_macros.h"
#include "oemcrypto_asymmetric_key_table.h"
#include "oemcrypto_check_macros.h"
#include "oemcrypto_compiler_attributes.h"
#include "oemcrypto_key.h"
#include "oemcrypto_key_table.h"
#include "oemcrypto_output.h"
#include "oemcrypto_overflow.h"
#include "oemcrypto_session.h"
#include "oemcrypto_session_key_table.h"
#include "oemcrypto_session_table.h"
#include "oemcrypto_usage_table.h"
#include "wtpi_abort_interface.h"
#include "wtpi_clock_interface_layer1.h"
#include "wtpi_config_interface.h"
#include "wtpi_crypto_and_key_management_interface_layer1.h"
#include "wtpi_crypto_asymmetric_interface.h"
#include "wtpi_initialize_terminate_interface.h"
#include "wtpi_logging_interface.h"
#include "wtpi_root_of_trust_interface_layer1.h"
typedef enum GlobalSystemState {
SYSTEM_NOT_INITIALIZED = (int)0x2ca77206,
SYSTEM_INITIALIZED = (int)0xf57fab49
} GlobalSystemState;
static GlobalSystemState g_opk_system_state = SYSTEM_NOT_INITIALIZED;
static bool IsSubstrInRange(size_t message_length,
OEMCrypto_Substring substring, bool allow_null) {
if (!substring.length) return (substring.offset == 0) && allow_null;
if (substring.offset > message_length) return false;
size_t end_of_substring;
if (OPK_AddOverflowUX(substring.offset, substring.length,
&end_of_substring)) {
return false;
}
if (end_of_substring > message_length) return false;
return true;
}
/* Cleanup functions for various OEMCrypto calls. */
static OEMCryptoResult FreeMacKeys(OEMCryptoSession* session) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF_NULL(session);
OEMCryptoResult result = OPKI_FreeKeyFromTable(&session->mac_key_server);
OEMCryptoResult free_key_result =
OPKI_FreeKeyFromTable(&session->mac_key_client);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
return result;
}
static OEMCryptoResult FreeMacAndEncryptionKeys(OEMCryptoSession* session) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF_NULL(session);
OEMCryptoResult result = FreeMacKeys(session);
OEMCryptoResult free_key_result =
OPKI_FreeKeyFromTable(&session->encryption_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
return result;
}
static OEMCryptoResult FreeContentAndEntitlementKeys(
OEMCryptoSession* session) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF_NULL(session);
OEMCryptoResult result = OEMCrypto_SUCCESS;
OEMCryptoResult free_key_result = result;
for (size_t i = 0; i < session->num_content_keys; i++) {
free_key_result = OPKI_FreeKeyFromTable(&session->content_keys[i]);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
}
for (size_t i = 0; i < session->num_entitlement_keys; i++) {
free_key_result = OPKI_FreeKeyFromTable(&session->entitlement_keys[i]);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
}
session->num_content_keys = 0;
session->num_entitlement_keys = 0;
return result;
}
static bool IsSupportedAsymmetricKeyType(AsymmetricKeyType key_type) {
return key_type == DRM_RSA_PRIVATE_KEY || key_type == DRM_ECC_PRIVATE_KEY;
}
static OEMCryptoResult RewrapDeviceDRMKeyOEMCert(
OEMCryptoSession* session_context, const uint8_t* encrypted_message_key,
size_t encrypted_message_key_length, const uint8_t* enc_drm_key,
size_t enc_drm_key_length, const uint8_t* enc_drm_key_iv,
AsymmetricKeyType drm_key_type, uint8_t* wrapped_drm_key,
size_t wrapped_drm_key_length) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF(WTPI_GetProvisioningMethod() != OEMCrypto_OEMCertificate,
"This function is only valid on Provisioning 3.0 devices");
ABORT_IF_NULL(session_context);
ABORT_IF_NULL(encrypted_message_key);
ABORT_IF_ZERO(encrypted_message_key_length);
ABORT_IF_NULL(enc_drm_key);
ABORT_IF_ZERO(enc_drm_key_length);
ABORT_IF(enc_drm_key_length > PKCS8_DRM_KEY_MAX_SIZE,
"enc_drm_key_length of %zu is too large", enc_drm_key_length);
ABORT_IF_NULL(enc_drm_key_iv);
ABORT_IF(!IsSupportedAsymmetricKeyType(drm_key_type),
"drm_key_type %d is invalid", drm_key_type);
ABORT_IF_NULL(wrapped_drm_key);
ABORT_IF_ZERO(wrapped_drm_key_length);
// TODO(b/180530495): implement this.
OEMCryptoResult result = OEMCrypto_ERROR_NOT_IMPLEMENTED;
/* RSA decryption needs at most RSA_size to decrypt. 3072 is the largest size
OEM RSA keys we can use. */
// uint8_t message_key[KEY_SIZE_3072];
// size_t message_key_length = sizeof(message_key);
// DecryptMessageWithOEMPrivateKey(
// encrypted_message_key, encrypted_message_key_length, message_key,
// &message_key_length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
// if (message_key_length != KEY_SIZE_128) {
// /* Encryption key is expected to be an AES 128-bit key. */
// result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
// goto cleanup;
// }
// result = OPKI_CreateKey(&session_context->encryption_key, ENCRYPTION_KEY,
// KEY_SIZE_128);
// if (result != OEMCrypto_SUCCESS) goto cleanup;
// result = WTPI_CreateKeyHandle(message_key, message_key_length,
// ENCRYPTION_KEY,
// &(session_context->encryption_key->key_handle));
// if (result != OEMCrypto_SUCCESS) goto cleanup;
// result = RewrapDeviceDRMKeyCommon(
// session_context, enc_drm_key, enc_drm_key_length, enc_drm_key_iv,
// drm_key_type, wrapped_drm_key, wrapped_drm_key_length);
cleanup:;
OEMCryptoResult free_key_result =
OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
free_key_result = FreeMacAndEncryptionKeys(session_context);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS) session_context->state = SESSION_INVALID;
return result;
}
// This function contains the RewrapDeviceDRMKey code that is shared between
// keybox and OEM certificate devices.
static OEMCryptoResult RewrapDeviceDRMKeyCommon(OEMCryptoSession* session,
const uint8_t* enc_drm_key,
size_t enc_drm_key_length,
const uint8_t* enc_drm_key_iv,
AsymmetricKeyType drm_key_type,
uint8_t* wrapped_drm_key,
size_t wrapped_drm_key_length) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF_NULL(session);
ABORT_IF_NULL(enc_drm_key);
ABORT_IF_ZERO(enc_drm_key_length);
ABORT_IF(enc_drm_key_length > PKCS8_DRM_KEY_MAX_SIZE,
"enc_drm_key_length of %zu is too large", enc_drm_key_length);
ABORT_IF_NULL(enc_drm_key_iv);
ABORT_IF(!IsSupportedAsymmetricKeyType(drm_key_type),
"drm_key_type %d is invalid", drm_key_type);
if (!OPKI_CheckKey(session->encryption_key, ENCRYPTION_KEY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Decrypt and deserialize the DRM private key. */
uint8_t clear_drm_key[PKCS8_DRM_KEY_MAX_SIZE];
KeySize key_size;
OEMCryptoResult result =
WTPI_K1_GetKeySize(session->encryption_key->key_handle, &key_size);
if (result != OEMCrypto_SUCCESS) return result;
result = WTPI_C1_AESCBCDecrypt(
session->encryption_key->key_handle, (size_t)key_size, enc_drm_key,
enc_drm_key_length, enc_drm_key_iv, clear_drm_key);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to AES CBC decrypt DRM private key with result: %u", result);
return result;
}
/* Adjust for RSA keys with specified schemes */
uint32_t allowed_schemes = 0;
size_t key_offset = 0;
if (drm_key_type == DRM_RSA_PRIVATE_KEY) {
if (enc_drm_key_length >= 8 &&
crypto_memcmp(clear_drm_key, "SIGN", 4) == 0) {
memcpy(&allowed_schemes, clear_drm_key + 4, 4);
allowed_schemes = oemcrypto_be32toh(allowed_schemes);
key_offset = 8;
} else {
allowed_schemes = kSign_RSASSA_PSS;
}
}
WTPI_AsymmetricKey_Handle private_key_handle;
result = WTPI_CreateAsymmetricKeyHandle(clear_drm_key + key_offset,
enc_drm_key_length - key_offset,
drm_key_type, &private_key_handle);
if (result != OEMCrypto_SUCCESS) {
LOGE(
"Failed to create asymmetric key handle for DRM private key with "
"result: %u",
result);
return result;
}
size_t private_key_size;
result = WTPI_GetSignatureSize(private_key_handle, &private_key_size);
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get DRM private key size with result: %u", result);
return result;
}
result =
WTPI_WrapAsymmetricKey(wrapped_drm_key, wrapped_drm_key_length,
drm_key_type, clear_drm_key, enc_drm_key_length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to re-wrap DRM private key with result: %u", result);
return result;
}
/* Check that it's a valid DRM key. */
result = OPKI_LoadDRMKey(session, drm_key_type, wrapped_drm_key,
wrapped_drm_key_length, private_key_size,
allowed_schemes);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to load DRM key with result: %u", result);
}
return result;
}
static OEMCryptoResult RewrapDeviceDRMKeyKeybox(
OEMCryptoSession* session_context, const uint8_t* message,
size_t message_length, const uint8_t* signature, size_t signature_length,
const uint8_t* enc_drm_key, size_t enc_drm_key_length,
const uint8_t* enc_drm_key_iv, AsymmetricKeyType drm_key_type,
uint8_t* wrapped_drm_key, size_t wrapped_drm_key_length) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF(WTPI_GetProvisioningMethod() != OEMCrypto_Keybox,
"This function is only valid on Provisioning 2.0 devices");
ABORT_IF_NULL(session_context);
ABORT_IF_NULL(message);
ABORT_IF_ZERO(message_length);
ABORT_IF_NULL(signature);
ABORT_IF_ZERO(signature_length);
ABORT_IF(signature_length != SHA256_DIGEST_LENGTH,
"signature_length is not the length of a SHA256 digest");
ABORT_IF_NULL(enc_drm_key);
ABORT_IF_ZERO(enc_drm_key_length);
ABORT_IF(enc_drm_key_length > PKCS8_DRM_KEY_MAX_SIZE,
"enc_drm_key_length of %zu is too large", enc_drm_key_length);
ABORT_IF_NULL(enc_drm_key_iv);
ABORT_IF(!IsSupportedAsymmetricKeyType(drm_key_type),
"drm_key_type %d is invalid", drm_key_type);
ABORT_IF_NULL(wrapped_drm_key);
ABORT_IF_ZERO(wrapped_drm_key_length);
/* Use mac_key_server from previous call to GenerateDerivedKeys to verify the
message. */
OEMCryptoResult result = OPKI_VerifySignatureWithMacKeyServer(
session_context, message, message_length, signature);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to verify signature with mac key server, error: %u", result);
goto cleanup;
}
result = RewrapDeviceDRMKeyCommon(
session_context, enc_drm_key, enc_drm_key_length, enc_drm_key_iv,
drm_key_type, wrapped_drm_key, wrapped_drm_key_length);
cleanup:;
OEMCryptoResult free_key_result =
OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
free_key_result = FreeMacAndEncryptionKeys(session_context);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS) session_context->state = SESSION_INVALID;
return result;
}
static OEMCryptoResult GetDeviceID(uint8_t* device_id,
size_t* device_id_length) {
if (device_id_length == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) {
// TODO(b/180530495): Implement this.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (*device_id_length < KEYBOX_DEVICE_ID_SIZE) {
*device_id_length = KEYBOX_DEVICE_ID_SIZE;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (device_id == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*device_id_length = KEYBOX_DEVICE_ID_SIZE;
return WTPI_GetDeviceIDFromKeybox(device_id, *device_id_length);
}
static OEMCryptoResult GetROTSignatureLength(size_t* signature_length) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF_NULL(signature_length);
const OEMCrypto_ProvisioningMethod provisioning_method =
WTPI_GetProvisioningMethod();
if (provisioning_method == OEMCrypto_Keybox) {
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_SUCCESS;
} else if (provisioning_method == OEMCrypto_OEMCertificate) {
// TODO(b/180530495): implement this.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} else {
// TODO(b/180530495): implement this.
/* TODO: Add ECC support. */
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}
OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id,
size_t sandbox_id_length) {
RETURN_INVALID_CONTEXT_IF_NULL(sandbox_id);
RETURN_INVALID_CONTEXT_IF_ZERO(sandbox_id_length);
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_Initialize(void) {
if (g_opk_system_state != SYSTEM_NOT_INITIALIZED) {
LOGE("OEMCrypto_Initialize called when not in uninitialized state.");
LOGE("OEMCrypto will now terminate and re-initialize.");
OEMCrypto_Terminate();
}
OEMCryptoResult result = WTPI_Initialize();
if (result != OEMCrypto_SUCCESS) {
LOGE("OEMCrypto failed to |WTPI_Initialize| with result: %u", result);
result = OEMCrypto_ERROR_INIT_FAILED;
goto cleanup;
}
result = WTPI_InitializeClock();
if (result != OEMCrypto_SUCCESS) {
LOGE("OEMCrypto failed to |WTPI_InitializeClock| with result: %u", result);
result = OEMCrypto_ERROR_INIT_FAILED;
goto cleanup;
}
result = WTPI_InitializeKeybox();
if (result != OEMCrypto_SUCCESS) {
LOGE("Initializing keybox error %u. System not ready for production.",
result);
// This means we don't have a keybox installed. This is fine for a test
// device or for a device that is still in the factory before it has been
// provisioned with a keybox. In that case, we log the error and continue.
// A production system will fail later on when it tries to validate the
// keybox.
result = OEMCrypto_SUCCESS;
}
OPKI_InitializeSessionTable();
OPKI_InitializeKeyTable();
OPKI_InitializeAsymmetricKeyTable();
result = WTPI_K1_InitializeKeyManagement();
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to initialize key management with result: %u", result);
goto cleanup;
}
result = OPKI_InitializeUsageTable();
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to initialize usage table with result: %u", result);
goto cleanup;
}
g_opk_system_state = SYSTEM_INITIALIZED;
result = OEMCrypto_SUCCESS;
cleanup:
if (result != OEMCrypto_SUCCESS) OEMCrypto_Terminate();
return result;
}
OEMCryptoResult OEMCrypto_Terminate(void) {
OEMCryptoResult usage_table_terminate_result = OPKI_TerminateUsageTable();
OEMCryptoResult session_terminate_result = OPKI_TerminateSessionTable();
OEMCryptoResult key_terminate_result = OPKI_TerminateKeyTable();
OEMCryptoResult asymmetric_key_terminate_result =
OPKI_TerminateAsymmetricKeyTable();
OEMCryptoResult keybox_result = WTPI_TerminateKeybox();
OEMCryptoResult clock_result = WTPI_TerminateClock();
OEMCryptoResult key_management_result = WTPI_K1_TerminateKeyManagement();
OEMCryptoResult tee_result = WTPI_Terminate();
g_opk_system_state = SYSTEM_NOT_INITIALIZED;
if (tee_result != OEMCrypto_SUCCESS) {
LOGE("OEMCrypto failed to |Terminate| with result: %u", tee_result);
return tee_result;
}
if (keybox_result != OEMCrypto_SUCCESS) {
return keybox_result;
}
if (clock_result != OEMCrypto_SUCCESS) {
return clock_result;
}
if (key_management_result != OEMCrypto_SUCCESS) {
return key_management_result;
}
if (session_terminate_result != OEMCrypto_SUCCESS) {
return session_terminate_result;
}
if (usage_table_terminate_result != OEMCrypto_SUCCESS) {
return usage_table_terminate_result;
}
if (asymmetric_key_terminate_result != OEMCrypto_SUCCESS) {
return asymmetric_key_terminate_result;
}
return key_terminate_result;
}
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(session);
OEMCryptoResult result = OPKI_GrabSession(session);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to grab and initialize session with result: %u", result);
return result;
}
OEMCryptoSession* session_context = NULL;
result = OPKI_GetSession(*session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
*session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_OPENSESSION);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = OPKI_SetStatePostCall(session_context, API_OPENSESSION);
cleanup:
if (result != OEMCrypto_SUCCESS) {
OEMCryptoResult ignored UNUSED = OPKI_FreeSession(*session);
}
return result;
}
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_CLOSESESSION);
if (result != OEMCrypto_SUCCESS) return result;
return OPKI_FreeSession(session);
}
OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
const uint8_t* mac_key_context,
size_t mac_key_context_length,
const uint8_t* enc_key_context,
size_t enc_key_context_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) {
LOGD("This function is only valid on Provisioning 2.0 devices");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GENERATEDERIVEDKEYS);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(mac_key_context);
RETURN_INVALID_CONTEXT_IF_ZERO(mac_key_context_length);
RETURN_INVALID_CONTEXT_IF_NULL(enc_key_context);
RETURN_INVALID_CONTEXT_IF_ZERO(enc_key_context_length);
WTPI_K1_SymmetricKey_Handle keybox_key = NULL;
result = WTPI_K1_CreateKeyHandleFromKeybox(&keybox_key);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to create key handle from keybox with result: %u", result);
goto cleanup;
}
result = OPKI_DeriveMacAndEncryptionKeys(
session_context, keybox_key, mac_key_context, mac_key_context_length,
enc_key_context, enc_key_context_length);
if (result == OEMCrypto_SUCCESS) {
result = OPKI_SetStatePostCall(session_context, API_GENERATEDERIVEDKEYS);
} else {
LOGE("Failed to derive mac and encryption keys from keybox with result: %u",
result);
}
cleanup : {
OEMCryptoResult free_key_result = WTPI_K1_FreeKeyHandle(keybox_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS) {
FreeMacAndEncryptionKeys(session_context);
session_context->state = SESSION_INVALID;
}
}
return result;
}
OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
OEMCrypto_SESSION session, const uint8_t* enc_session_key,
size_t enc_session_key_length, const uint8_t* mac_key_context,
size_t mac_key_context_length, const uint8_t* enc_key_context,
size_t enc_key_context_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
WTPI_K1_SymmetricKey_Handle session_key_handle = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result =
OPKI_CheckStatePreCall(session_context, API_DERIVEKEYSFROMSESSIONKEY);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(enc_session_key);
RETURN_INVALID_CONTEXT_IF_ZERO(enc_session_key_length);
RETURN_INVALID_CONTEXT_IF_NULL(mac_key_context);
RETURN_INVALID_CONTEXT_IF_ZERO(mac_key_context_length);
RETURN_INVALID_CONTEXT_IF_NULL(enc_key_context);
RETURN_INVALID_CONTEXT_IF_ZERO(enc_key_context_length);
/* Decrypt the session key with the DRM key and then derive keys from it.
RSA decryption needs at most RSA_size to decrypt. 3072 is the largest size
DRM RSA keys we can use. */
uint8_t session_key[KEY_SIZE_3072];
size_t session_key_length = sizeof(session_key);
size_t expected_session_key_length = 0;
result = OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED;
if (session_context->drm_private_key == NULL) goto cleanup;
WTPI_AsymmetricKey_Handle private_key_handle;
uint32_t allowed_schemes;
AsymmetricKey* private_key = session_context->drm_private_key;
result = WTPI_UnwrapIntoAsymmetricKeyHandle(
private_key->wrapped_key, private_key->wrapped_key_length,
private_key->key_type, &private_key_handle, &allowed_schemes);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to unwrap DRM private key into key handle with result: %u",
result);
return result;
}
switch (session_context->drm_private_key->key_type) {
case DRM_RSA_PRIVATE_KEY: {
if (session_context->allowed_schemes != kSign_RSASSA_PSS) {
LOGE("Bad RSA padding scheme: %u", session_context->allowed_schemes);
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
result = WTPI_RSADecrypt(private_key_handle, enc_session_key,
enc_session_key_length, session_key,
&session_key_length);
expected_session_key_length = KEY_SIZE_128;
} break;
case DRM_ECC_PRIVATE_KEY: {
result = WTPI_ECCDeriveSessionKey(private_key_handle, enc_session_key,
enc_session_key_length, session_key,
&session_key_length);
expected_session_key_length = KEY_SIZE_256;
} break;
}
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
if (result != OEMCrypto_SUCCESS) {
LOGE(
"Failed to decrypt session key with the DRM private key with result: "
"%u",
result);
goto cleanup;
}
ABORT_IF_ZERO(expected_session_key_length);
if (session_key_length != expected_session_key_length) {
LOGE("Invalid session key length: %zu, expected = %zu", session_key_length,
expected_session_key_length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
result = WTPI_K1_CreateKeyHandle(session_key, session_key_length,
DERIVING_KEY, &session_key_handle);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to create key handle type DERIVING_KEY with result: %u",
result);
goto cleanup;
}
result = OPKI_DeriveMacAndEncryptionKeys(
session_context, session_key_handle, mac_key_context,
mac_key_context_length, enc_key_context, enc_key_context_length);
if (result != OEMCrypto_SUCCESS) {
LOGE(
"Failed to derive mac and encryption keys from session key with "
"result: %u",
result);
goto cleanup;
}
result = OPKI_SetStatePostCall(session_context, API_DERIVEKEYSFROMSESSIONKEY);
cleanup:;
OEMCryptoResult free_key_result =
OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
free_key_result = WTPI_K1_FreeKeyHandle(session_key_handle);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS) {
FreeMacAndEncryptionKeys(session_context);
session_context->state = SESSION_INVALID;
}
return result;
}
OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
uint32_t* nonce) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(nonce);
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GENERATENONCE);
if (result != OEMCrypto_SUCCESS) return result;
/* Get the current time. */
uint64_t now;
result = WTPI_GetTrustedTime(&now);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get trusted time with result: %u", result);
return result;
}
/* last_nonce_time should only be initialized once. */
static uint64_t last_nonce_time = 0;
static int nonce_count = 0;
const int nonce_flood_count = 200;
if (last_nonce_time == now) {
nonce_count++;
if (nonce_count > nonce_flood_count) {
LOGE("Nonce flood detected: now = %" PRIu64, now);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
} else {
nonce_count = 1;
last_nonce_time = now;
}
/* Generate a new nonce that doesn't collide with other nonces. */
uint32_t nonce_value = 0;
while (nonce_value == 0 || OPKI_NonceCollision(nonce_value)) {
result = WTPI_C1_RandomBytes((uint8_t*)(&nonce_value), sizeof(nonce_value));
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to generate random nonce with result: %u", result);
return result;
}
}
result = OPKI_SetNonce(session_context, nonce_value);
if (result != OEMCrypto_SUCCESS) return result;
*nonce = nonce_value;
return OPKI_SetStatePostCall(session_context, API_GENERATENONCE);
}
OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer UNUSED,
size_t buffer_length UNUSED) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_length, uint8_t* signature, size_t* signature_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context,
API_PREPANDSIGN_PROVISION_REQUEST);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(core_message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature_length);
if (session_context->request_signed) {
LOGE("Attempt to sign provision request after license request");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
size_t required_signature_size;
result = GetROTSignatureLength(&required_signature_size);
if (result != OEMCrypto_SUCCESS) return result;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
size_t device_id_length = ODK_DEVICE_ID_LEN_MAX;
result = GetDeviceID(device_id, &device_id_length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get device id with result: %u", result);
return result;
}
result = ODK_PrepareCoreProvisioningRequest(
message, message_length, core_message_length,
&session_context->nonce_values, device_id, device_id_length);
if (*signature_length < required_signature_size ||
result == OEMCrypto_ERROR_SHORT_BUFFER) {
*signature_length = required_signature_size;
/* The core_message_length has been correctly set by
* ODK_PrepareCoreProvisioningRequest, but the message and signature buffer
* will be initialized and filled in the subsequent call. */
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (result != OEMCrypto_SUCCESS) {
LOGE("ODK error: %u", result);
return result;
}
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
const OEMCrypto_ProvisioningMethod provisioning_method =
WTPI_GetProvisioningMethod();
if (provisioning_method == OEMCrypto_Keybox) {
result = OPKI_GenerateSignatureWithMacKeyClient(
session_context, message, message_length, signature, signature_length);
} else if (provisioning_method == OEMCrypto_OEMCertificate) {
result = OPKI_GenerateCertSignature(session_context, message,
message_length, signature,
signature_length, CERT_SIGNATURE_OEM);
} else {
LOGE("Bad provision method = %#x", WTPI_GetProvisioningMethod());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (result != OEMCrypto_SUCCESS) {
if (result != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGE("Failed to generate signature with result: %u", result);
}
return result;
}
session_context->request_signed = true;
return OPKI_SetStatePostCall(session_context,
API_PREPANDSIGN_PROVISION_REQUEST);
}
OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_length, uint8_t* signature, size_t* signature_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result =
OPKI_CheckStatePreCall(session_context, API_PREPANDSIGN_LICENSE_REQUEST);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(core_message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature_length);
ABORT_IF_NULL(session_context->drm_private_key);
size_t required_signature_size = session_context->drm_private_key->key_size;
result = ODK_PrepareCoreLicenseRequest(message, message_length,
core_message_length,
&session_context->nonce_values);
if (*signature_length < required_signature_size ||
result == OEMCrypto_ERROR_SHORT_BUFFER) {
*signature_length = required_signature_size;
/* The core_message_length has been correctly set by
* ODK_PrepareCoreLicenseRequest, but the message and signature buffer will
* be initialized and filled in the subsequent call. */
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (result != OEMCrypto_SUCCESS) {
LOGE("ODK error: %u", result);
return result;
}
RETURN_INVALID_CONTEXT_IF_NULL(message);
if (message_length < *core_message_length) {
LOGE("message_length of %zu is too small for core message length %zu",
message_length, *core_message_length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (session_context->request_signed) {
LOGE("Attempt to sign two license requests");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* For backwards compatibility, we only sign the message body, and we compute
* a SHA256 of the core message. */
result = WTPI_C1_SHA256(message, *core_message_length,
session_context->license_request_hash);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to compute SHA256 of the core message with result: %u",
result);
return result;
}
const uint8_t* message_body = message + *core_message_length;
const size_t message_body_length = message_length - *core_message_length;
result = OPKI_GenerateCertSignature(session_context, message_body,
message_body_length, signature,
signature_length, CERT_SIGNATURE_DRM);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to generate cert signature with result: %u", result);
return result;
}
uint64_t now;
result = WTPI_GetTrustedTime(&now);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get trusted time with result: %u", result);
return result;
}
result = ODK_InitializeClockValues(&session_context->clock_values, now);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to initialize clock values with result: %u", result);
return result;
}
session_context->request_signed = true;
return OPKI_SetStatePostCall(session_context,
API_PREPANDSIGN_LICENSE_REQUEST);
}
OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_length, uint8_t* signature, size_t* signature_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result =
OPKI_CheckStatePreCall(session_context, API_PREPANDSIGN_RENEWAL_REQUEST);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(core_message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature_length);
/* If we have signed a request, but have not loaded it, something is wrong. On
* the other hand, we can sign a license release using the mac keys from the
* usage table. So it is OK if we have never signed a license request. */
if (session_context->request_signed && !session_context->response_loaded) {
LOGE("Attempt to sign renewal before load");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const size_t required_signature_size = SHA256_DIGEST_LENGTH;
uint64_t now;
result = WTPI_GetTrustedTime(&now);
if (result != OEMCrypto_SUCCESS) return result;
result = ODK_PrepareCoreRenewalRequest(
message, message_length, core_message_length,
&session_context->nonce_values, &session_context->clock_values, now);
if (*signature_length < required_signature_size ||
result == OEMCrypto_ERROR_SHORT_BUFFER) {
*signature_length = required_signature_size;
/* The core_message_length has been correctly set by
* ODK_PrepareCoreRenewalRequest, but the message and signature buffer will
* be initialized and filled in the subsequent call. */
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (result != OEMCrypto_SUCCESS) {
LOGE("ODK error: %u", result);
return result;
}
RETURN_INVALID_CONTEXT_IF_NULL(message);
if (message_length < *core_message_length) {
LOGE("message_length of %zu is too short; expected at least %zu",
message_length, *core_message_length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (session_context->nonce_values.api_major_version < API_MAJOR_VERSION) {
/* If we are talking to an old license server, then we only sign the message
* body. */
const uint8_t* message_body = message + *core_message_length;
const size_t message_body_length = message_length - *core_message_length;
if (OPKI_CheckKey(session_context->mac_key_client, MAC_KEY_CLIENT)) {
/* Sign with mac key client if it is available. */
result = OPKI_GenerateSignatureWithMacKeyClient(
session_context, message_body, message_body_length, signature,
signature_length);
} else {
/* Otherwise, this is a release request. Use the usage entry to sign. */
result = OPKI_SignReleaseRequest(session_context->session_id,
message_body, message_body_length,
signature, signature_length);
}
} else {
/* Sign the entire message. */
if (OPKI_CheckKey(session_context->mac_key_client, MAC_KEY_CLIENT)) {
result = OPKI_GenerateSignatureWithMacKeyClient(session_context, message,
message_length, signature,
signature_length);
} else {
result =
OPKI_SignReleaseRequest(session_context->session_id, message,
message_length, signature, signature_length);
}
}
if (result != OEMCrypto_SUCCESS) {
if (result != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGE(
"Failed to generate signature or sign release request with result: "
"%u",
result);
}
return result;
}
return OPKI_SetStatePostCall(session_context,
API_PREPANDSIGN_RENEWAL_REQUEST);
}
static OEMCryptoResult LoadKeysNoSignature(
OEMCryptoSession* session, const uint8_t* message, size_t message_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"OEMCrypto is not yet initialized");
ABORT_IF_NULL(session);
ABORT_IF_NULL(message);
ABORT_IF_ZERO(message_length);
ABORT_IF_NULL(key_array);
RETURN_INVALID_CONTEXT_IF_NULL(key_array);
RETURN_INVALID_CONTEXT_IF_ZERO(num_keys);
if (session->response_loaded) {
return OEMCrypto_ERROR_LICENSE_RELOAD;
}
if (!IsSubstrInRange(message_length, enc_mac_keys_iv, true) ||
!IsSubstrInRange(message_length, enc_mac_keys, true) ||
!IsSubstrInRange(message_length, pst, true) ||
!IsSubstrInRange(message_length, srm_restriction_data, true)) {
LOGE("Invalid substring range");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for (size_t i = 0; i < num_keys; i++) {
if (!IsSubstrInRange(message_length, key_array[i].key_id, false) ||
!IsSubstrInRange(message_length, key_array[i].key_data, false) ||
!IsSubstrInRange(message_length, key_array[i].key_data_iv, false) ||
!IsSubstrInRange(message_length, key_array[i].key_control, false) ||
!IsSubstrInRange(message_length, key_array[i].key_control_iv, false) ||
key_array[i].key_id.length == 0 ||
key_array[i].key_id.length > KEY_ID_MAX_SIZE ||
key_array[i].key_data_iv.length < KEY_IV_SIZE ||
key_array[i].key_control.length < KEY_CONTROL_SIZE ||
key_array[i].key_control_iv.length < KEY_IV_SIZE) {
LOGE("Invalid substring range for key index: %zu", i);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
/* If enc_mac_keys or enc_mac_keys_iv is present, both must be present and
must be the right sizes. */
if ((enc_mac_keys_iv.length > 0 || enc_mac_keys.length > 0) &&
(enc_mac_keys_iv.length < KEY_IV_SIZE ||
enc_mac_keys.length < 2 * MAC_KEY_SIZE)) {
LOGE("Invalid encrypted MAC key or MAC key IV length");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check to see if the MAC Keys' IV is directly before the MAC Keys, which is
forbidden. */
if (enc_mac_keys.offset >= KEY_IV_SIZE && enc_mac_keys.length > 0 &&
crypto_memcmp(message + enc_mac_keys.offset - KEY_IV_SIZE,
message + enc_mac_keys_iv.offset, KEY_IV_SIZE) == 0) {
LOGE("The bytes before the mac keys shouldn't be the same as the iv");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* If we've already loaded in a license and it isn't the current license type,
fail. */
if (session->state == SESSION_LICENSE_LOADED &&
session->license_type != license_type) {
LOGE(
"License type doesn't match. License type already loaded: %u, license "
"type to be loaded: %u",
session->license_type, license_type);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoResult result = OEMCrypto_SUCCESS;
session->license_type = license_type;
session->refresh_valid =
IsSubstrInRange(message_length, enc_mac_keys, false) &&
IsSubstrInRange(message_length, enc_mac_keys_iv, false);
session->valid_srm_version = false;
if (srm_restriction_data.length != 0) {
static const uint8_t kSrmVerification[8] = {'H', 'D', 'C', 'P',
'D', 'A', 'T', 'A'};
uint32_t minimum_version_network;
if (srm_restriction_data.length <
sizeof(kSrmVerification) + sizeof(minimum_version_network)) {
LOGE("Bad SRM restriction data length: %zu", srm_restriction_data.length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (crypto_memcmp(message + srm_restriction_data.offset, kSrmVerification,
sizeof(kSrmVerification))) {
LOGE("SRM verification failed");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
const uint8_t* const minimum_version_pointer =
message + srm_restriction_data.offset + sizeof(kSrmVerification);
memcpy(&minimum_version_network, minimum_version_pointer,
sizeof(minimum_version_network));
const uint32_t minimum_version = oemcrypto_be32toh(minimum_version_network);
uint32_t current_version = 0;
result = WTPI_GetCurrentSRMVersion(&current_version);
if (result != OEMCrypto_SUCCESS) {
LOGE("Unable to fetch SRM data with error: %u", result);
} else if (current_version < minimum_version) {
LOGE("SRM Version is too small %u, required: %u", current_version,
minimum_version);
} else {
session->valid_srm_version = true;
}
}
/* Decrypt and install keys in key object. Each key will have a key control
block. They will all have the same nonce. */
session->num_content_keys = 0;
session->num_entitlement_keys = 0;
for (size_t i = 0; i < num_keys; i++) {
result = OPKI_InstallKey(
session, message + key_array[i].key_id.offset,
key_array[i].key_id.length, message + key_array[i].key_data.offset,
key_array[i].key_data.length, message + key_array[i].key_data_iv.offset,
message + key_array[i].key_control.offset,
message + key_array[i].key_control_iv.offset);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to install key with result: %u, index = %zu", result, i);
break;
}
if (session->license_type == OEMCrypto_ContentLicense) {
session->num_content_keys++;
} else {
session->num_entitlement_keys++;
}
}
if (result != OEMCrypto_SUCCESS) goto cleanup;
if (session->refresh_valid) {
result = OPKI_UpdateMacKeys(session, message + enc_mac_keys.offset,
message + enc_mac_keys_iv.offset);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to update mac keys with result: %u", result);
goto cleanup;
}
}
/* If we have loaded a usage entry, or we should have loaded an entry, check
* it. */
if (OPKI_GetUsageEntryStatus(session->session_id) != USAGE_ENTRY_NONE ||
pst.length > 0) {
if (pst.length == 0) {
LOGE("Usage entry exists but PST doesn't");
result = OEMCrypto_ERROR_INVALID_CONTEXT;
goto cleanup;
}
switch (OPKI_GetUsageEntryStatus(session->session_id)) {
case USAGE_ENTRY_NONE:
default:
/* PST specified, but no usage entry loaded. */
LOGE("PST specified, but no usage entry loaded");
result = OEMCrypto_ERROR_INVALID_CONTEXT;
break;
case USAGE_ENTRY_NEW:
/* This was the first time loading the license. Copy the pst and mac
* keys. */
result = OPKI_SetUsageEntryPST(session->session_id,
message + pst.offset, pst.length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to set usage entry PST with result: %u", result);
break;
}
if (session->mac_key_server == NULL ||
session->mac_key_client == NULL) {
result = OEMCrypto_ERROR_INVALID_CONTEXT;
break;
}
result = OPKI_SetUsageEntryMacKeys(session->session_id,
session->mac_key_server->key_handle,
session->mac_key_client->key_handle);
break;
case USAGE_ENTRY_DEACTIVATED:
result = OEMCrypto_ERROR_LICENSE_INACTIVE;
break;
case USAGE_ENTRY_LOADED:
/* This was not the first time loading the license. Verify the pst and
* mac keys. */
result = OPKI_VerfiyUsageEntryPST(session->session_id,
message + pst.offset, pst.length);
if (result != OEMCrypto_SUCCESS) {
LOGE(
"Failed to verify usage entry PST with result: %u, session_id = "
"%u",
result, session->session_id);
break;
}
if (session->mac_key_server == NULL ||
session->mac_key_client == NULL) {
result = OEMCrypto_ERROR_INVALID_CONTEXT;
break;
}
result = OPKI_VerifyUsageEntryMacKeys(
session->session_id, session->mac_key_server->key_handle,
session->mac_key_client->key_handle);
if (result != OEMCrypto_SUCCESS) {
LOGE(
"Failed to verify usage entry mac keys with result: %u, "
"session_id = %u",
result, session->session_id);
}
break;
}
}
cleanup:;
OEMCryptoResult free_key_result =
OPKI_FreeKeyFromTable(&session->encryption_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS || !session->refresh_valid) {
/* Mac keys must be freed in every case except when the call is successful
AND we are given new mac keys. In that case, the new keys should be
preserved in the session for future RefreshKeys calls. */
free_key_result = FreeMacKeys(session);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
}
if (result != OEMCrypto_SUCCESS) {
FreeContentAndEntitlementKeys(session);
session->state = SESSION_INVALID;
}
if (result == OEMCrypto_SUCCESS) session->response_loaded = true;
return result;
}
OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADLICENSE);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (signature_length != SHA256_DIGEST_LENGTH) {
LOGE("signature_length is not the length of a SHA256 digest");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
/* Check state before we check signature. State is change in
* LoadKeysNoSignature. */
if (session_context->response_loaded) {
return OEMCrypto_ERROR_LICENSE_RELOAD;
}
ODK_ParsedLicense parsed_response;
const bool initial_license_load =
(OPKI_GetUsageEntryStatus(session_context->session_id) !=
USAGE_ENTRY_LOADED);
const bool usage_entry_present =
(OPKI_GetUsageEntryStatus(session_context->session_id) !=
USAGE_ENTRY_NONE);
result = ODK_ParseLicense(
message, message_length, core_message_length, initial_license_load,
usage_entry_present, session_context->license_request_hash,
&session_context->timer_limits, &session_context->clock_values,
&session_context->nonce_values, &parsed_response);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to parse license with result: %u", result);
return result;
}
if (parsed_response.license_type != OEMCrypto_ContentLicense &&
parsed_response.license_type != OEMCrypto_EntitlementLicense) {
LOGE("Invalid license type: %u", parsed_response.license_type);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
result = OPKI_VerifySignatureWithMacKeyServer(session_context, message,
message_length, signature);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to verify signature with server mac key, error: %u", result);
return result;
}
const uint8_t* message_body = message + core_message_length;
const size_t message_body_length = message_length - core_message_length;
result = LoadKeysNoSignature(
session_context, message_body, message_body_length,
parsed_response.enc_mac_keys_iv, parsed_response.enc_mac_keys,
parsed_response.key_array_length, parsed_response.key_array,
parsed_response.pst, parsed_response.srm_restriction_data,
parsed_response.license_type);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to load keys with result: %u", result);
return result;
}
return OPKI_SetStatePostCall(session_context, API_LOADLICENSE);
}
OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t key_array_length, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADKEYS);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (signature_length != SHA256_DIGEST_LENGTH) {
LOGE("signature_length is not the length of a SHA256 digest");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(key_array);
RETURN_INVALID_CONTEXT_IF_ZERO(key_array_length);
if (license_type != OEMCrypto_ContentLicense &&
license_type != OEMCrypto_EntitlementLicense) {
LOGE("Invalid license type: %u", license_type);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check state before we check signature. State is change in
* LoadKeysNoSignature. */
if (session_context->response_loaded) {
return OEMCrypto_ERROR_LICENSE_RELOAD;
}
result = OPKI_VerifySignatureWithMacKeyServer(session_context, message,
message_length, signature);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to verify signature with server mac key, error: %u", result);
return result;
}
result = LoadKeysNoSignature(
session_context, message, message_length, enc_mac_keys_iv, enc_mac_keys,
key_array_length, key_array, pst, srm_restriction_data, license_type);
if (result != OEMCrypto_SUCCESS) return result;
/* Update clock values using the duration for the loaded key. */
SymmetricKey* key = NULL;
const int kIndex = 0;
switch (session_context->license_type) {
case OEMCrypto_ContentLicense:
if (session_context->num_content_keys == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
key = session_context->content_keys[kIndex];
break;
case OEMCrypto_EntitlementLicense:
if (session_context->num_entitlement_keys == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
key = session_context->entitlement_keys[kIndex];
break;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
const uint32_t duration = key ? key->key_control_block.duration : 0;
uint64_t now;
result = WTPI_GetTrustedTime(&now);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get trusted time with result: %u", result);
return result;
}
result = ODK_InitializeV15Values(
&session_context->timer_limits, &session_context->clock_values,
&session_context->nonce_values, duration, now);
if (result != OEMCrypto_SUCCESS) return result;
return OPKI_SetStatePostCall(session_context, API_LOADKEYS);
}
OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t key_array_length,
const OEMCrypto_EntitledContentKeyObject* key_array) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADENTITLEDCONTENTKEYS);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(key_array);
RETURN_INVALID_CONTEXT_IF_ZERO(key_array_length);
if (session_context->license_type != OEMCrypto_EntitlementLicense ||
session_context->num_entitlement_keys == 0) {
LOGE("Not a entitlement license or no keys found");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for (size_t i = 0; i < key_array_length; i++) {
if (!IsSubstrInRange(message_length, key_array[i].entitlement_key_id,
false) ||
!IsSubstrInRange(message_length, key_array[i].content_key_id, false) ||
!IsSubstrInRange(message_length, key_array[i].content_key_data,
false) ||
!IsSubstrInRange(message_length, key_array[i].content_key_data_iv,
false) ||
key_array[i].entitlement_key_id.length > KEY_ID_MAX_SIZE ||
key_array[i].content_key_id.length > KEY_ID_MAX_SIZE ||
key_array[i].content_key_data.length != KEY_SIZE_128 ||
key_array[i].content_key_data_iv.length != KEY_IV_SIZE) {
LOGE("Invalid substring range for key index: %zu", i);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
result = OEMCrypto_SUCCESS;
for (size_t i = 0; i < key_array_length; i++) {
const OEMCrypto_EntitledContentKeyObject key_object = key_array[i];
SymmetricKey* entitlement_key = OPKI_FindKeyFromTable(
session_context, false, message + key_object.entitlement_key_id.offset,
key_object.entitlement_key_id.length);
if (entitlement_key == NULL) {
result = OEMCrypto_KEY_NOT_ENTITLED;
goto cleanup;
}
uint32_t content_key_index = session_context->num_content_keys;
if (entitlement_key->has_entitled_content_key) {
/* Free previous content key associated with this entitlement key. */
content_key_index = entitlement_key->entitled_content_key_index;
result = OPKI_FreeKeyFromTable(
&session_context->content_keys[content_key_index]);
if (result != OEMCrypto_SUCCESS) goto cleanup;
}
/* If we're not updating an existing content key and we can't add any more,
we fail. */
bool adding_new_content_key =
content_key_index == session_context->num_content_keys;
if (adding_new_content_key &&
session_context->num_content_keys == CONTENT_KEYS_PER_SESSION) {
result = OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
goto cleanup;
}
if (!OPKI_CheckKey(entitlement_key, ENTITLEMENT_KEY)) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
SymmetricKey** content_key_ptr =
&session_context->content_keys[content_key_index];
result = OPKI_CreateKey(content_key_ptr, CONTENT_KEY,
(KeySize)key_object.content_key_data.length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to create CONTENT_KEY with result: %u", result);
goto cleanup;
}
SymmetricKey* content_key = *content_key_ptr;
result = WTPI_K1_AESDecryptAndCreateKeyHandle(
entitlement_key->key_handle,
message + key_object.content_key_data.offset,
key_object.content_key_data.length,
message + key_object.content_key_data_iv.offset, CONTENT_KEY,
&(content_key->key_handle));
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to decrypt and create content key handle with result %u",
result);
goto cleanup;
}
memcpy(content_key->key_id, message + key_object.content_key_id.offset,
key_object.content_key_id.length);
content_key->key_id_size = key_object.content_key_id.length;
content_key->session_key_index = content_key_index;
/* Associate the new content key with its entitlement key. */
content_key->is_entitled_content_key = true;
content_key->entitlement_key_index = entitlement_key->session_key_index;
/* Associate the entitlement key with this new content key. */
entitlement_key->has_entitled_content_key = true;
entitlement_key->entitled_content_key_index = content_key_index;
if (adding_new_content_key) session_context->num_content_keys++;
} // for loop over key_array
result = OPKI_SetStatePostCall(session_context, API_LOADENTITLEDCONTENTKEYS);
cleanup:
if (result != OEMCrypto_SUCCESS) {
FreeContentAndEntitlementKeys(session_context);
session_context->state = SESSION_INVALID;
}
return result;
}
OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADRENEWAL);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (signature_length != SHA256_DIGEST_LENGTH) {
LOGE("signature_length is not the length of a SHA256 digest");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
result = OPKI_VerifySignatureWithMacKeyServer(session_context, message,
message_length, signature);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to verify signature with server mac key, error: %u", result);
return result;
}
uint64_t now;
result = WTPI_GetTrustedTime(&now);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get trusted time with result: %u", result);
return result;
}
uint64_t* timer_value = NULL;
result = ODK_ParseRenewal(message, message_length, core_message_length,
&session_context->nonce_values, now,
&session_context->timer_limits,
&session_context->clock_values, timer_value);
if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED;
if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) {
return OPKI_SetStatePostCall(session_context, API_LOADRENEWAL);
}
return result;
}
OEMCryptoResult OEMCrypto_RefreshKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, size_t key_array_length,
const OEMCrypto_KeyRefreshObject* key_array) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_REFRESHKEYS);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (signature_length != SHA256_DIGEST_LENGTH) {
LOGE("signature_length is not the length of a SHA256 digest");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(key_array);
RETURN_INVALID_CONTEXT_IF_ZERO(key_array_length);
/* We only use the first key object to update the entire license. Since we
* know num_keys > 0 after the last if statement, we can assume index is not
* out of bounds. */
static const size_t kIndex = 0;
if (!IsSubstrInRange(message_length, key_array[kIndex].key_id, true) ||
!IsSubstrInRange(message_length, key_array[kIndex].key_control, false) ||
!IsSubstrInRange(message_length, key_array[kIndex].key_control_iv,
true) ||
key_array[kIndex].key_id.length > KEY_ID_MAX_SIZE ||
key_array[kIndex].key_control.length < KEY_CONTROL_SIZE ||
(key_array[kIndex].key_control_iv.length != 0 &&
key_array[kIndex].key_control_iv.length != KEY_IV_SIZE)) {
LOGE("Invalid substring range for key: %zu", kIndex);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!session_context->refresh_valid) {
LOGE("Invalid key refresh");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
result = OPKI_VerifySignatureWithMacKeyServer(session_context, message,
message_length, signature);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to verify signature with server mac key, error: %u", result);
return result;
}
result = OEMCrypto_SUCCESS;
const uint8_t* key_id = NULL;
const uint8_t* key_control_iv = NULL;
if (key_array[kIndex].key_id.length != 0) {
key_id = message + key_array[kIndex].key_id.offset;
}
if (key_array[kIndex].key_control_iv.length != 0) {
key_control_iv = message + key_array[kIndex].key_control_iv.offset;
}
result = OPKI_RefreshKey(
session_context, key_id, key_array[kIndex].key_id.length,
message + key_array[kIndex].key_control.offset, key_control_iv);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to refresh key with result: %u", result);
return result;
}
return OPKI_SetStatePostCall(session_context, API_REFRESHKEYS);
}
OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session,
const uint8_t* content_key_id,
size_t content_key_id_length,
uint8_t* key_control_block,
size_t* key_control_block_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_QUERYKEYCONTROL);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(key_control_block_length);
if (*key_control_block_length < KEY_CONTROL_SIZE) {
*key_control_block_length = KEY_CONTROL_SIZE;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
RETURN_INVALID_CONTEXT_IF_NULL(content_key_id);
RETURN_INVALID_CONTEXT_IF_ZERO(content_key_id_length);
RETURN_INVALID_CONTEXT_IF_NULL(key_control_block);
if (content_key_id_length > KEY_ID_MAX_SIZE) {
LOGE("content_key_id_length is too large");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*key_control_block_length = KEY_CONTROL_SIZE;
SymmetricKey* content_key = OPKI_FindKeyFromTable(
session_context, true, content_key_id, content_key_id_length);
if (content_key == NULL) return OEMCrypto_ERROR_NO_CONTENT_KEY;
KeyControlBlock control_block;
if (content_key->is_entitled_content_key) {
uint32_t entitlement_key_index = content_key->entitlement_key_index;
if (entitlement_key_index < session_context->num_entitlement_keys &&
OPKI_CheckKey(session_context->entitlement_keys[entitlement_key_index],
ENTITLEMENT_KEY)) {
control_block = session_context->entitlement_keys[entitlement_key_index]
->key_control_block;
} else {
LOGE("Invalid entitlement key, index = %u", entitlement_key_index);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
} else {
control_block = content_key->key_control_block;
}
const uint32_t new_block[4] = {
0, /* Verification optional. */
oemcrypto_htobe32(control_block.duration),
0, /* Nonce optional. */
oemcrypto_htobe32(control_block.control_bits),
};
if (sizeof(new_block) != KEY_CONTROL_SIZE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
memcpy(key_control_block, new_block, KEY_CONTROL_SIZE);
return OPKI_SetStatePostCall(session_context, API_QUERYKEYCONTROL);
}
OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session,
const uint8_t* content_key_id,
size_t content_key_id_length,
OEMCryptoCipherMode cipher_mode) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_SELECTKEY);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(content_key_id);
RETURN_INVALID_CONTEXT_IF_ZERO(content_key_id_length);
if (content_key_id_length > KEY_ID_MAX_SIZE) {
LOGE("content_key_id_length is too large");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (cipher_mode != OEMCrypto_CipherMode_CTR &&
cipher_mode != OEMCrypto_CipherMode_CBC) {
LOGE("Unrecognized cipher_mode %u", (unsigned int)cipher_mode);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (session_context->num_content_keys == 0) {
LOGE("Selected session has no content keys.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
SymmetricKey* content_key = OPKI_FindKeyFromTable(
session_context, true, content_key_id, content_key_id_length);
if (!content_key) return OEMCrypto_ERROR_NO_CONTENT_KEY;
KeyControlBlock key_control_block;
if (content_key->is_entitled_content_key) {
if (session_context->license_type != OEMCrypto_EntitlementLicense ||
session_context->num_entitlement_keys == 0) {
LOGE("Not a entitlement license or no keys found");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SymmetricKey* entitlement_key =
session_context->entitlement_keys[content_key->entitlement_key_index];
if (!OPKI_CheckKey(entitlement_key, ENTITLEMENT_KEY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Key control block for entitled content key is controlled by its
entitlement key. */
key_control_block = entitlement_key->key_control_block;
} else {
if (session_context->license_type != OEMCrypto_ContentLicense) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
key_control_block = content_key->key_control_block;
}
if (WTPI_HasAnalogDisplay() && WTPI_SupportsCGMS_A() &&
(key_control_block.control_bits & CONTROL_OBSERVE_CGMS)) {
result = WTPI_ApplyCGMS(key_control_block.control_bits & CONTROL_CGMS_MASK);
if (result != OEMCrypto_SUCCESS) return result;
}
session_context->current_content_key_index = content_key->session_key_index;
content_key->cipher_mode = cipher_mode;
return OPKI_SetStatePostCall(session_context, API_SELECTKEY);
}
/* This function will only be called if at least some of the data is encrypted,
meaning this function can assume a key is required. */
static OEMCryptoResult DecryptCENCInternal(
OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples,
size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) {
ABORT_IF(g_opk_system_state != SYSTEM_INITIALIZED,
"The system is uninitialized, and this was not caught at a higher "
"level.");
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_DECRYPTCENC);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(samples);
RETURN_INVALID_CONTEXT_IF_ZERO(samples_length);
RETURN_INVALID_CONTEXT_IF_NULL(pattern);
const size_t max_length = WTPI_MaxSampleSize();
/* Iterate through all the samples and validate them before doing any decrypt.
*/
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
const OEMCrypto_SampleDescription* const sample = &(samples[sample_index]);
if (sample->buffers.input_data == NULL ||
sample->buffers.input_data_length == 0) {
LOGE("Sample %zu has invalid input data.", sample_index);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Iterate through all the subsamples and sum their lengths. */
size_t subsample_length_tally = 0;
for (size_t subsample_index = 0;
subsample_index < sample->subsamples_length; ++subsample_index) {
const OEMCrypto_SubSampleDescription* const subsample =
&(sample->subsamples[subsample_index]);
size_t length;
if (OPK_AddOverflowUX(subsample->num_bytes_clear,
subsample->num_bytes_encrypted, &length)) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
if (OPK_AddOverflowUX(subsample_length_tally, length,
&subsample_length_tally)) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
}
if (max_length != 0 && subsample_length_tally > max_length) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
if (subsample_length_tally != sample->buffers.input_data_length) {
LOGE("Sample %zu's length and subsample lengths do not match.",
sample_index);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
result =
OPKI_DecryptSamples(session_context, samples, samples_length, pattern);
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
/* OPKI_DecryptSamples may pass back an OEMCrypto_ERROR_SHORT_BUFFER that
originated in OPK_CheckOutputBounds(). This is not a valid return code
for DecryptCENC(), so we translate it here. */
LOGE("Output buffer size is too small");
return OEMCrypto_ERROR_INVALID_CONTEXT;
} else if (result != OEMCrypto_SUCCESS) {
return result;
}
return OPKI_SetStatePostCall(session_context, API_DECRYPTCENC);
}
OEMCryptoResult OEMCrypto_DecryptCENC(
OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples,
size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(samples);
RETURN_INVALID_CONTEXT_IF_ZERO(samples_length);
RETURN_INVALID_CONTEXT_IF_NULL(pattern);
/* DecryptCENC may be called for calls that could go through CopyBuffer. We
separate those calls out here so that the decrypt code path only has to
handle cases where a key is expected. */
bool has_encrypted_data = false;
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
if (samples[sample_index].subsamples_length != 1 ||
samples[sample_index].subsamples[0].num_bytes_encrypted > 0) {
has_encrypted_data = true;
break;
}
}
if (has_encrypted_data)
return DecryptCENCInternal(session, samples, samples_length, pattern);
const size_t max_length = WTPI_MaxSampleSize();
for (size_t sample_index = 0; sample_index < samples_length; ++sample_index) {
const OEMCrypto_SampleDescription* const sample = &(samples[sample_index]);
ABORT_IF(sample->subsamples_length != 1,
"Sample %zu with multiple subsamples was somehow not treated as "
"encrypted.",
sample_index);
const OEMCrypto_SubSampleDescription* const subsample =
&(sample->subsamples[0]);
if (max_length != 0 && subsample->num_bytes_clear > max_length) {
LOGE("Sample size too large: %zu bytes. Max allowed: %zu",
subsample->num_bytes_clear, max_length);
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
if (subsample->num_bytes_clear != sample->buffers.input_data_length) {
LOGE("Sample %zu's length and subsample length do not match.",
sample_index);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoResult result = OEMCrypto_CopyBuffer(
session, sample->buffers.input_data, sample->buffers.input_data_length,
&sample->buffers.output_descriptor, subsample->subsample_flags);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to copy buffer with result: %u", result);
return result;
}
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult OEMCrypto_CopyBuffer(
OEMCrypto_SESSION session UNUSED, const uint8_t* data_addr,
size_t data_length, const OEMCrypto_DestBufferDesc* dest_buffer_desc,
uint8_t subsample_flags UNUSED) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* We don't need to check the session, CopyBuffer should be allowed for all
session states since it doesn't access any state data. */
RETURN_INVALID_CONTEXT_IF_NULL(data_addr);
RETURN_INVALID_CONTEXT_IF_ZERO(data_length);
RETURN_INVALID_CONTEXT_IF_NULL(dest_buffer_desc);
OEMCryptoResult result;
OPK_OutputBuffer output_buffer;
size_t offset;
result = OPK_ParseDestBufferDesc(dest_buffer_desc, &output_buffer, &offset);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to parse dest buffer description with result: %u", result);
return result;
}
ABORT_IF(!OPK_IsOutputBufferValid(&output_buffer), "Invalid output buffer.");
result = OPK_CheckOutputBounds(&output_buffer, offset, data_length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to check output bounds with result: %u", result);
return result;
}
return WTPI_C1_CopyToOutputBuffer(data_addr, data_length, &output_buffer,
offset);
}
OEMCryptoResult OEMCrypto_Generic_Encrypt(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GENERICENCRYPT);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(in_buffer);
RETURN_INVALID_CONTEXT_IF_NULL(out_buffer);
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length);
RETURN_INVALID_CONTEXT_IF_NULL(iv);
if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
LOGE("Invalid algorithm.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length % AES_BLOCK_SIZE != 0) {
LOGE("buffer_length of %zu is not a multiple of the crypto block length.",
buffer_length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
if (session_context->current_content_key_index >=
session_context->num_content_keys) {
LOGE(
"Content key index out of range: index = %u, number of content keys: "
"%u",
session_context->current_content_key_index,
session_context->num_content_keys);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SymmetricKey* current_content_key =
session_context->content_keys[session_context->current_content_key_index];
if (current_content_key == NULL ||
current_content_key->key_size != KEY_SIZE_128) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = OPKI_CheckCurrentContentKeyUsage(
session_context, CONTROL_ALLOW_ENCRYPT, OPK_CLEAR_INSECURE_OUTPUT_BUFFER);
if (result != OEMCrypto_SUCCESS) return result;
result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = WTPI_C1_AESCBCEncrypt(current_content_key->key_handle, in_buffer,
buffer_length, iv, out_buffer);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to AES CBC encrypt with result: %u", result);
return result;
}
return OPKI_SetStatePostCall(session_context, API_GENERICENCRYPT);
}
OEMCryptoResult OEMCrypto_Generic_Decrypt(
OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length,
const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GENERICDECRYPT);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(in_buffer);
RETURN_INVALID_CONTEXT_IF_NULL(out_buffer);
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length);
RETURN_INVALID_CONTEXT_IF_NULL(iv);
if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
LOGE("Invalid algorithm.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length % AES_BLOCK_SIZE != 0) {
LOGE("buffer_length of %zu is not a multiple of the crypto block length.",
buffer_length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
if (session_context->current_content_key_index >=
session_context->num_content_keys) {
LOGE(
"Content key index out of range: index = %u, number of content keys: "
"%u",
session_context->current_content_key_index,
session_context->num_content_keys);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SymmetricKey* current_content_key =
session_context->content_keys[session_context->current_content_key_index];
if (current_content_key == NULL ||
current_content_key->key_size != KEY_SIZE_128) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = OPKI_CheckCurrentContentKeyUsage(
session_context, CONTROL_ALLOW_DECRYPT, OPK_CLEAR_INSECURE_OUTPUT_BUFFER);
if (result != OEMCrypto_SUCCESS) return result;
result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context);
if (result != OEMCrypto_SUCCESS) return result;
KeySize key_size;
result = WTPI_K1_GetKeySize(current_content_key->key_handle, &key_size);
if (result != OEMCrypto_SUCCESS) return result;
result =
WTPI_C1_AESCBCDecrypt(current_content_key->key_handle, (size_t)key_size,
in_buffer, buffer_length, iv, out_buffer);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to AES CBC decrypt with result: %u", result);
return result;
}
return OPKI_SetStatePostCall(session_context, API_GENERICDECRYPT);
}
OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
uint8_t* signature,
size_t* signature_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GENERICSIGN);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(signature_length);
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
RETURN_INVALID_CONTEXT_IF_NULL(in_buffer);
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (algorithm != OEMCrypto_HMAC_SHA256) {
LOGE("Invalid algorithm.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*signature_length = SHA256_DIGEST_LENGTH;
size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
if (session_context->current_content_key_index >=
session_context->num_content_keys) {
LOGE(
"Content key index out of range: index = %u, number of content keys: "
"%u",
session_context->current_content_key_index,
session_context->num_content_keys);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SymmetricKey* current_content_key =
session_context->content_keys[session_context->current_content_key_index];
/* Only allow 256-bit keys for signing and verifying. */
if (current_content_key == NULL ||
current_content_key->key_size != KEY_SIZE_256) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = OPKI_CheckCurrentContentKeyUsage(session_context, CONTROL_ALLOW_SIGN,
OPK_CLEAR_INSECURE_OUTPUT_BUFFER);
if (result != OEMCrypto_SUCCESS) return result;
result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = WTPI_C1_HMAC_SHA256(current_content_key->key_handle, in_buffer,
buffer_length, signature);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to HMAC-SHA256 with result: %u", result);
return result;
}
return OPKI_SetStatePostCall(session_context, API_GENERICSIGN);
}
OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
const uint8_t* in_buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
size_t signature_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GENERICVERIFY);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(in_buffer);
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
if (signature_length != SHA256_DIGEST_LENGTH) {
LOGE("signature_length is not the length of a SHA256 digest");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
if (algorithm != OEMCrypto_HMAC_SHA256) {
LOGE("Invalid algorithm.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
size_t max_buffer_size = WTPI_MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
if (session_context->current_content_key_index >=
session_context->num_content_keys) {
LOGE(
"Content key index out of range: index = %u, number of content keys: "
"%u",
session_context->current_content_key_index,
session_context->num_content_keys);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SymmetricKey* current_content_key =
session_context->content_keys[session_context->current_content_key_index];
/* Only allow 256-bit keys for signing and verifying. */
if (current_content_key == NULL ||
current_content_key->key_size != KEY_SIZE_256) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = OPKI_CheckCurrentContentKeyUsage(
session_context, CONTROL_ALLOW_VERIFY, OPK_CLEAR_INSECURE_OUTPUT_BUFFER);
if (result != OEMCrypto_SUCCESS) return result;
result = OPKI_UpdatePlaybackTimeAndUsageEntryStatus(session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = WTPI_C1_HMAC_SHA256_Verify(current_content_key->key_handle,
in_buffer, buffer_length, signature);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to HMAC-SHA256 verify with result: %u", result);
return result;
}
return OPKI_SetStatePostCall(session_context, API_GENERICVERIFY);
}
OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert(
const uint8_t* rot UNUSED, size_t rotLength UNUSED,
uint8_t* wrappedRot UNUSED, size_t* wrappedRotLength UNUSED,
const uint8_t* transportKey UNUSED, size_t transportKeyLength UNUSED) {
// TODO(fredgc, ncbray, marcone): One installation option is that there an
// installation tool that uses a system key to unwrap the keybox and then
// re-wraps it using a device key. This wrapped keybox can be stored on the
// file system. I think we don't plan to use this. If we do, then Widevine
// needs to implement this function, and we have to agree on an porting layer
// interface for:
// decrypt w/system key -> encrypt w/device key -> return buffer.
//
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* keybox,
size_t length) {
// TODO(b/180530495): We currently only support keyboxes.
return WTPI_UnwrapValidateAndInstallKeybox(keybox, length);
}
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ProvisioningError;
}
return WTPI_GetProvisioningMethod();
}
OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const OEMCrypto_ProvisioningMethod provisioning_method =
WTPI_GetProvisioningMethod();
if (provisioning_method == OEMCrypto_OEMCertificate) {
// TODO(b/180530495): Implement this.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} else if (provisioning_method == OEMCrypto_Keybox) {
return WTPI_ValidateKeybox();
} else {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// GetDeviceID handles validation of the parameters, so we do not validate
// them here.
return GetDeviceID(deviceID, idLength);
}
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* key_data,
size_t* key_data_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) {
LOGD("This function is only valid on Provisioning 2.0 devices");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
RETURN_INVALID_CONTEXT_IF_NULL(key_data_length);
if (*key_data_length < KEYBOX_KEY_DATA_SIZE) {
*key_data_length = KEYBOX_KEY_DATA_SIZE;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
RETURN_INVALID_CONTEXT_IF_NULL(key_data);
*key_data_length = KEYBOX_KEY_DATA_SIZE;
return WTPI_GetKeyDataFromKeybox(key_data, *key_data_length);
}
OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (WTPI_GetProvisioningMethod() != OEMCrypto_Keybox) {
LOGD("This function is only valid on Provisioning 2.0 devices");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
RETURN_INVALID_CONTEXT_IF_NULL(buffer);
if (length < sizeof(WidevineKeybox)) {
LOGE("length too short");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return WTPI_LoadTestKeybox(buffer, length);
}
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
uint8_t* public_cert UNUSED, size_t* public_cert_length UNUSED) {
// TODO(b/180530495): implement this.
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (WTPI_GetProvisioningMethod() != OEMCrypto_OEMCertificate) {
LOGD("This function is only valid on Provisioning 3.0 devices");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADOEMPRIVATEKEY);
if (result != OEMCrypto_SUCCESS) return result;
/* This API sets the session state to indicate that the OEM private key is
* ready for use. It doesn't actually load it. It is best to load the OEM
* private key in memory only when being used. */
return OPKI_SetStatePostCall(session_context, API_LOADOEMPRIVATEKEY);
}
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(randomData);
RETURN_INVALID_CONTEXT_IF_ZERO(dataLength);
return WTPI_C1_RandomBytes(randomData, dataLength);
}
uint32_t OEMCrypto_APIVersion(void) { return API_MAJOR_VERSION; }
uint32_t OEMCrypto_MinorAPIVersion(void) { return API_MINOR_VERSION; }
const char* OEMCrypto_BuildInformation(void) { return BUILD_INFO(); }
uint8_t OEMCrypto_Security_Patch_Level(void) { return 0; }
const char* OEMCrypto_SecurityLevel(void) {
switch (WTPI_GetSecurityLevel()) {
case OPK_SECURITY_LEVEL_1:
return "L1";
case OPK_SECURITY_LEVEL_3:
return "L3";
}
ABORT("Impossible security level.");
}
OEMCryptoResult OEMCrypto_GetHDCPCapability(
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(current);
RETURN_INVALID_CONTEXT_IF_NULL(maximum);
*current = WTPI_CurrentHDCPCapability();
*maximum = WTPI_MaxHDCPCapability();
return OEMCrypto_SUCCESS;
}
bool OEMCrypto_SupportsUsageTable(void) { return true; }
size_t OEMCrypto_MaximumUsageTableHeaderSize(void) {
return MAX_NUMBER_OF_USAGE_ENTRIES;
}
bool OEMCrypto_IsAntiRollbackHwPresent(void) {
return WTPI_IsAntiRollbackHWPresent();
}
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(count);
uint32_t num_open_sessions = 0;
OEMCryptoResult result = OPKI_NumberOfOpenSessions(&num_open_sessions);
*count = num_open_sessions;
return result;
}
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(max);
*max = OPKI_MaxNumberOfSessions();
return OEMCrypto_SUCCESS;
}
uint32_t OEMCrypto_SupportedCertificates(void) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return 0;
}
return WTPI_SupportedCertificates();
}
bool OEMCrypto_IsSRMUpdateSupported(void) { return false; }
OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version) {
RETURN_INVALID_CONTEXT_IF_NULL(version);
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
uint32_t OEMCrypto_GetAnalogOutputFlags(void) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return 0;
}
uint32_t analog_bits = 0;
if (WTPI_HasAnalogDisplay()) analog_bits |= 0x1;
if (WTPI_CanDisableAnalogDisplay()) analog_bits |= 0x2;
if (WTPI_SupportsCGMS_A()) analog_bits |= 0x4;
return analog_bits;
}
uint32_t OEMCrypto_ResourceRatingTier(void) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return 0;
}
return WTPI_GetResourceRatingTier();
}
OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session,
OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_drm_key,
size_t wrapped_drm_key_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
AsymmetricKeyType drm_key_type;
if (!OPKI_PrivateKeyTypeToAsymmetricKey(key_type, &drm_key_type)) {
LOGE("Invalid key_type: %d", key_type);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADDRMPRIVATEKEY);
if (result != OEMCrypto_SUCCESS) return result;
WTPI_AsymmetricKey_Handle private_key_handle;
uint32_t allowed_schemes;
result = WTPI_UnwrapIntoAsymmetricKeyHandle(
wrapped_drm_key, wrapped_drm_key_length, drm_key_type,
&private_key_handle, &allowed_schemes);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to unwrap DRM private key into key handle with result: %u",
result);
return result;
}
size_t private_key_size;
result = WTPI_GetSignatureSize(private_key_handle, &private_key_size);
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get DRM private key size with result: %u", result);
return result;
}
result = OPKI_LoadDRMKey(session_context, drm_key_type, wrapped_drm_key,
wrapped_drm_key_length, private_key_size,
allowed_schemes);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to load DRM key");
goto cleanup;
}
result = OPKI_SetStatePostCall(session_context, API_LOADDRMPRIVATEKEY);
cleanup:;
OEMCryptoResult free_key_result = FreeMacAndEncryptionKeys(session_context);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS) {
OPKI_FreeAsymmetricKeyFromTable(&session_context->drm_private_key);
session_context->state = SESSION_INVALID;
}
return result;
}
OEMCryptoResult OEMCrypto_LoadTestRSAKey(void) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_GenerateRSASignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length,
RSA_Padding_Scheme padding_scheme) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GENERATERSASIGNATURE);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature_length);
if (padding_scheme == kSign_PKCS1_Block1 && message_length > 83) {
LOGE("message_length is too long for the given padding_scheme.");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
if (session_context->state == SESSION_LOAD_OEM_RSA_KEY &&
padding_scheme == kSign_RSASSA_PSS) {
// SignMessageWithOEMPrivateKey handles signature being NULL, so we
// intentionally do not check it here.
result = OEMCrypto_ERROR_NOT_IMPLEMENTED;
// TODO(b/180530495): implement this.
// SignMessageWithOEMPrivateKey(message, message_length, signature,
// signature_length);
if (result != OEMCrypto_SUCCESS) return result;
} else if (session_context->state == SESSION_LOAD_DRM_RSA_KEY) {
if ((session_context->allowed_schemes & padding_scheme) != padding_scheme) {
LOGE("Invalid padding scheme: %u", session_context->allowed_schemes);
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
WTPI_AsymmetricKey_Handle private_key_handle;
uint32_t allowed_schemes;
AsymmetricKey* private_key = session_context->drm_private_key;
result = WTPI_UnwrapIntoAsymmetricKeyHandle(
private_key->wrapped_key, private_key->wrapped_key_length,
private_key->key_type, &private_key_handle, &allowed_schemes);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to unwrap DRM private key into key handle with result: %u",
result);
return result;
}
// WTPI_RSASign handles signature being NULL, so we intentionally do not
// check it here.
result = WTPI_RSASign(private_key_handle, message, message_length,
signature, signature_length, padding_scheme);
WTPI_FreeAsymmetricKeyHandle(private_key_handle);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to generate RSA signature with result: %u", result);
return result;
}
} else {
LOGE(
"Invalid session state or padding scheme. session state: %#x, padding "
"scheme: %u",
session_context->state, padding_scheme);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OPKI_SetStatePostCall(session_context, API_GENERATERSASIGNATURE);
}
OEMCryptoResult OEMCrypto_LoadProvisioning(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
size_t core_message_length, const uint8_t* signature,
size_t signature_length, uint8_t* wrapped_private_key,
size_t* wrapped_private_key_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADPROVISIONING);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(message);
RETURN_INVALID_CONTEXT_IF_ZERO(message_length);
RETURN_INVALID_CONTEXT_IF_NULL(signature);
RETURN_INVALID_CONTEXT_IF_ZERO(signature_length);
if (signature_length != SHA256_DIGEST_LENGTH) {
LOGE("signature_length is not the length of a SHA256 digest");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length);
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
size_t device_id_length = ODK_DEVICE_ID_LEN_MAX;
result = GetDeviceID(device_id, &device_id_length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get device id with result: %u", result);
return result;
}
ODK_ParsedProvisioning parsed_response;
result = ODK_ParseProvisioning(message, message_length, core_message_length,
&session_context->nonce_values, device_id,
device_id_length, &parsed_response);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to parse provisioning with result: %u", result);
return result;
}
AsymmetricKeyType drm_key_type;
if (!OPKI_PrivateKeyTypeToAsymmetricKey(parsed_response.key_type,
&drm_key_type)) {
LOGE("Unexpected key type: %#x", parsed_response.key_type);
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (parsed_response.enc_private_key.length > PKCS8_DRM_KEY_MAX_SIZE) {
LOGE("Encrypted private key length is too large to handle.");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (parsed_response.enc_private_key_iv.length != KEY_IV_SIZE) {
LOGE("Encrypted private key iv has invalid length: %zu",
parsed_response.enc_private_key_iv.length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
size_t buffer_size = 0;
result = WTPI_GetWrappedAsymmetricKeySize(
parsed_response.enc_private_key.length, drm_key_type, &buffer_size);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get wrapped DRM key size with result: %u", result);
return result;
}
ABORT_IF_ZERO(buffer_size);
if (wrapped_private_key == NULL ||
*wrapped_private_key_length < buffer_size) {
*wrapped_private_key_length = buffer_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
/* Tell caller how much space we used. */
*wrapped_private_key_length = buffer_size;
const uint8_t* message_body = message + core_message_length;
const OEMCrypto_ProvisioningMethod provisioning_method =
WTPI_GetProvisioningMethod();
if (provisioning_method == OEMCrypto_Keybox) {
result = RewrapDeviceDRMKeyKeybox(
session_context, message, message_length, signature, signature_length,
message_body + parsed_response.enc_private_key.offset,
parsed_response.enc_private_key.length,
message_body + parsed_response.enc_private_key_iv.offset, drm_key_type,
wrapped_private_key, *wrapped_private_key_length);
} else if (provisioning_method == OEMCrypto_OEMCertificate) {
result = RewrapDeviceDRMKeyOEMCert(
session_context,
message_body + parsed_response.encrypted_message_key.offset,
parsed_response.encrypted_message_key.length,
message_body + parsed_response.enc_private_key.offset,
parsed_response.enc_private_key.length,
message_body + parsed_response.enc_private_key_iv.offset, drm_key_type,
wrapped_private_key, *wrapped_private_key_length);
} else {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (result != OEMCrypto_SUCCESS) return result;
return OPKI_SetStatePostCall(session_context, API_LOADPROVISIONING);
}
OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer,
size_t* header_buffer_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// OPKI_CreateUsageTableHeader handles validation of the parameters, so we do
// not validate them here.
return OPKI_CreateUsageTableHeader(header_buffer, header_buffer_length);
}
OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* header_buffer,
size_t header_buffer_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// OPKI_LoadUsageTableHeader handles validation of the parameters, so we do
// not validate them here.
return OPKI_LoadUsageTableHeader(header_buffer, header_buffer_length);
}
OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session,
uint32_t* usage_entry_number) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_CREATENEWUSAGEENTRY);
if (result != OEMCrypto_SUCCESS) return result;
// OPKI_CreateNewUsageEntry handles validation of the parameters, so we do not
// validate them here.
result =
OPKI_CreateNewUsageEntry(session_context->session_id, usage_entry_number);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to create new usage entry with result: %u, session_id = %u",
result, session_context->session_id);
return result;
}
return OPKI_SetStatePostCall(session_context, API_CREATENEWUSAGEENTRY);
}
OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session,
uint32_t usage_entry_number,
const uint8_t* buffer,
size_t buffer_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
RETURN_INVALID_CONTEXT_IF_NULL(buffer);
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length);
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_LOADUSAGEENTRY);
if (result != OEMCrypto_SUCCESS) return result;
result = OPKI_LoadUsageEntry(session_context->session_id,
&session_context->clock_values,
usage_entry_number, buffer, buffer_length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to load usage entry with result: %u, session_id = %u", result,
session_context->session_id);
return result;
}
return OPKI_SetStatePostCall(session_context, API_LOADUSAGEENTRY);
}
OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_UPDATEUSAGEENTRY);
if (result != OEMCrypto_SUCCESS) return result;
// OPKI_UpdateUsageEntry handles validation of the parameters, so we do not
// validate them here.
result = OPKI_UpdateUsageEntry(
session_context->session_id, &session_context->clock_values,
header_buffer, header_buffer_length, entry_buffer, entry_buffer_length);
if (result != OEMCrypto_SUCCESS) {
if (result != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGE("Failed to update usage entry with result: %u, session_id = %u",
result, session_context->session_id);
}
return result;
}
return OPKI_SetStatePostCall(session_context, API_UPDATEUSAGEENTRY);
}
OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_DEACTIVATEUSAGEENTRY);
if (result != OEMCrypto_SUCCESS) return result;
// OPKI_DeactivateUsageEntry handles validation of the parameters, so we do
// not validate them here.
result = OPKI_DeactivateUsageEntry(session_context->session_id,
&session_context->clock_values, pst,
pst_length);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to deactivate usage entry with result: %u, session_id = %u",
result, session_context->session_id);
return result;
}
return OPKI_SetStatePostCall(session_context, API_DEACTIVATEUSAGEENTRY);
}
OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
const uint8_t* pst, size_t pst_length,
uint8_t* buffer, size_t* buffer_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_REPORTUSAGE);
if (result != OEMCrypto_SUCCESS) return result;
WTPI_K1_SymmetricKey_Handle mac_key_client =
OPKI_CheckKey(session_context->mac_key_client, MAC_KEY_CLIENT)
? session_context->mac_key_client->key_handle
: NULL;
// OPKI_ReportUsage handles validation of the parameters, so we do not
// validate them here.
result = OPKI_ReportUsage(session_context->session_id, mac_key_client, pst,
pst_length, buffer, buffer_length);
if (result != OEMCrypto_SUCCESS) return result;
return OPKI_SetStatePostCall(session_context, API_REPORTUSAGE);
}
OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session,
uint32_t new_index) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_MOVEENTRY);
if (result != OEMCrypto_SUCCESS) return result;
result = OPKI_MoveEntry(session_context->session_id, new_index);
if (result != OEMCrypto_SUCCESS) {
LOGE(
"Failed to move entry with result: %u, session_id = %u, new_index = %u",
result, session_context->session_id, new_index);
return result;
}
return OPKI_SetStatePostCall(session_context, API_MOVEENTRY);
}
OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count,
uint8_t* header_buffer,
size_t* header_buffer_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// OPKI_ShrinkUsageTableHeader handles validation of the parameters, so we do
// not validate them here.
return OPKI_ShrinkUsageTableHeader(new_entry_count, header_buffer,
header_buffer_length);
}
OEMCryptoResult OEMCrypto_RemoveSRM(void) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
uint32_t OEMCrypto_SupportsDecryptHash(void) {
return OEMCrypto_CRC_Clear_Buffer;
}
OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session,
uint32_t frame_number,
const uint8_t* hash,
size_t hash_length) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_SETDECRYPTHASH);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(hash);
const size_t expected_hash_length = sizeof(session_context->given_hash);
if (hash_length < expected_hash_length) return OEMCrypto_ERROR_SHORT_BUFFER;
if (hash_length > expected_hash_length) {
LOGE("Bad hash_length: %zu, expected: %zu", hash_length,
expected_hash_length);
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
session_context->compute_hash = true;
session_context->current_frame_number = frame_number;
/* This is safe since it's host-to-host. */
memcpy(&session_context->given_hash, hash, hash_length);
return OPKI_SetStatePostCall(session_context, API_SETDECRYPTHASH);
}
OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session,
uint32_t* failed_frame_number) {
if (g_opk_system_state != SYSTEM_INITIALIZED) {
LOGE("OEMCrypto is not yet initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context = NULL;
OEMCryptoResult result = OPKI_GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) {
LOGE("Failed to get session with result: %u, session = %u", result,
session);
return result;
}
ABORT_IF(session_context == NULL,
"OPKI_GetSession() provided invalid output.");
result = OPKI_CheckStatePreCall(session_context, API_GETHASHERRORCODE);
if (result != OEMCrypto_SUCCESS) return result;
RETURN_INVALID_CONTEXT_IF_NULL(failed_frame_number);
if (session_context->hash_error != OEMCrypto_SUCCESS) {
*failed_frame_number = session_context->bad_frame_number;
}
result = OPKI_SetStatePostCall(session_context, API_GETHASHERRORCODE);
if (result != OEMCrypto_SUCCESS) return result;
return session_context->hash_error;
}
OEMCryptoResult OEMCrypto_AllocateSecureBuffer(
OEMCrypto_SESSION session UNUSED, size_t buffer_size,
OEMCrypto_DestBufferDesc* output_descriptor, int* secure_fd) {
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_size);
RETURN_INVALID_CONTEXT_IF_NULL(output_descriptor);
RETURN_INVALID_CONTEXT_IF_NULL(secure_fd);
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_FreeSecureBuffer(
OEMCrypto_SESSION session UNUSED,
OEMCrypto_DestBufferDesc* output_descriptor, int secure_fd UNUSED) {
RETURN_INVALID_CONTEXT_IF_NULL(output_descriptor);
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_GenerateOTARequest(OEMCrypto_SESSION session UNUSED,
uint8_t* buffer UNUSED,
size_t* buffer_length,
uint32_t use_test_key UNUSED) {
RETURN_INVALID_CONTEXT_IF_NULL(buffer_length);
/* Implementing this method is optional. It is only applicable
* a specific set of devices. */
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_ProcessOTAKeybox(OEMCrypto_SESSION session UNUSED,
const uint8_t* buffer,
size_t buffer_length,
uint32_t use_test_key UNUSED) {
RETURN_INVALID_CONTEXT_IF_NULL(buffer);
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_length);
/* Implementing this method is optional. It is only applicable
* a specific set of devices. */
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}