Files
odkitee/oemcrypto_ta/oemcrypto.c
2020-07-24 12:03:58 -07:00

2032 lines
80 KiB
C

/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
source code may only be used and distributed under the Widevine Master
License Agreement. */
#include "OEMCryptoCENC.h"
#include "string.h"
#include "assert_interface.h"
#include "clock_interface.h"
#include "initialize_terminate_interface.h"
#include "logging_interface.h"
#include "oemcrypto_api_macros.h"
#include "oemcrypto_config_interface.h"
#include "oemcrypto_endianness.h"
#include "oemcrypto_key.h"
#include "oemcrypto_key_table.h"
#include "oemcrypto_nonce_table.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 "root_of_trust_interface.h"
LogPriority g_cutoff = LOG_ERROR;
typedef enum GlobalSystemState {
SYSTEM_NOT_INITIALIZED = 0xca77206,
SYSTEM_INITIALIZED = 0x57fab49
} GlobalSystemState;
static GlobalSystemState g_odkitee_system_state = SYSTEM_NOT_INITIALIZED;
/* Return uint32 referenced through a potentially unaligned pointer. */
static uint32_t unaligned_dereference_uint32(const void* unaligned_ptr) {
ASSERT(unaligned_ptr != NULL, "unaligned_ptr is NULL");
uint32_t value;
const uint8_t* src = (const uint8_t*)(unaligned_ptr);
uint8_t* dest = (uint8_t*)(&value);
memcpy(dest, src, sizeof(value));
return value;
}
static bool range_check_substr(uint32_t message_length,
OEMCrypto_Substring substring, bool allow_null) {
if (!substring.length) return allow_null;
if (substring.offset > message_length) return false;
size_t end_of_substring;
if (AddOverflowUX(substring.offset, substring.length, &end_of_substring)) {
return false;
}
if (end_of_substring > message_length) return false;
return true;
}
static bool range_check(const uint8_t* message, uint32_t message_length,
const uint8_t* field, uint32_t field_length,
bool allow_null) {
if (field == NULL) return allow_null;
if (field < message) return false;
if (field + field_length > message + message_length) return false;
return true;
}
static OEMCryptoResult set_destination(
const OEMCrypto_DestBufferDesc* out_description, uint32_t data_length,
uint8_t subsample_flags, uint8_t** destination) {
ASSERT(out_description != NULL && data_length != 0 && destination != NULL,
"Parameters are either NULL or 0");
*destination = NULL;
uint32_t dest_length = 0;
switch (out_description->type) {
case OEMCrypto_BufferType_Clear:
*destination = out_description->buffer.clear.address;
dest_length = out_description->buffer.clear.max_length;
break;
case OEMCrypto_BufferType_Secure:
*destination = (uint8_t*)(out_description->buffer.secure.handle) +
out_description->buffer.secure.offset;
dest_length = out_description->buffer.secure.max_length -
out_description->buffer.secure.offset;
break;
case OEMCrypto_BufferType_Direct:
/* TODO(b/145244584): Add support for direct buffers. */
return OEMCrypto_ERROR_INVALID_CONTEXT;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t max_allowed = MaxOutputSizeForDecrypt();
if (max_allowed != 0 &&
(max_allowed < dest_length || max_allowed < data_length)) {
return OEMCrypto_ERROR_OUTPUT_TOO_LARGE;
}
if (out_description->type != OEMCrypto_BufferType_Direct &&
dest_length < data_length) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (*destination == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return OEMCrypto_SUCCESS;
}
/* Cleanup functions for various OEMCrypto calls. */
static OEMCryptoResult FreeMacKeys(OEMCryptoSession* session) {
ASSERT(session != NULL, "session is NULL");
OEMCryptoResult result = FreeKey(&session->mac_key_server);
OEMCryptoResult free_key_result = FreeKey(&session->mac_key_client);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
return result;
}
static OEMCryptoResult FreeMacAndEncryptionKeys(OEMCryptoSession* session) {
ASSERT(session != NULL, "session is NULL");
OEMCryptoResult result = FreeMacKeys(session);
OEMCryptoResult free_key_result = FreeKey(&session->encryption_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
return result;
}
static OEMCryptoResult FreeContentAndEntitlementKeys(
OEMCryptoSession* session) {
ASSERT(session != NULL, "session is NULL");
OEMCryptoResult result = OEMCrypto_SUCCESS;
OEMCryptoResult free_key_result = result;
for (uint32_t i = 0; i < session->num_content_keys; i++) {
free_key_result = FreeKey(&session->content_keys[i]);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
}
for (uint32_t i = 0; i < session->num_entitlement_keys; i++) {
free_key_result = FreeKey(&session->entitlement_keys[i]);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
}
session->num_content_keys = 0;
session->num_entitlement_keys = 0;
return result;
}
OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id,
size_t sandbox_id_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_Initialize(void) {
#ifdef OEMCRYPTO_TA_TEST_ONLY
g_cutoff = LOG_DEBUG;
#else
g_cutoff = LOG_ERROR;
#endif
if (g_odkitee_system_state != SYSTEM_NOT_INITIALIZED) {
LOGE("OEMCrypto Initialize called when not in uninitialized state");
OEMCrypto_Terminate();
}
int tee_result = Initialize();
if (tee_result != 0) {
LOGE("OEMCrypto failed to |Initialize| with result: %d", tee_result);
return OEMCrypto_ERROR_INIT_FAILED;
}
OEMCryptoResult result = InitializeSessionTable();
if (result != OEMCrypto_SUCCESS) {
OEMCrypto_Terminate();
return result;
}
result = InitializeKeyTable();
if (result != OEMCrypto_SUCCESS) {
OEMCrypto_Terminate();
return result;
}
result = InitializeUsageTable();
if (result != OEMCrypto_SUCCESS) {
OEMCrypto_Terminate();
return result;
}
g_odkitee_system_state = SYSTEM_INITIALIZED;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult OEMCrypto_Terminate(void) {
OEMCryptoResult usage_table_terminate_result = TerminateUsageTable();
OEMCryptoResult session_terminate_result = TerminateSessionTable();
OEMCryptoResult key_terminate_result = TerminateKeyTable();
int tee_result = Terminate();
g_odkitee_system_state = SYSTEM_NOT_INITIALIZED;
if (tee_result != 0) {
LOGE("OEMCrypto failed to |Terminate| with result: %d", tee_result);
return OEMCrypto_ERROR_TERMINATE_FAILED;
}
if (session_terminate_result != OEMCrypto_SUCCESS) {
return session_terminate_result;
}
if (usage_table_terminate_result != OEMCrypto_SUCCESS) {
return usage_table_terminate_result;
}
return key_terminate_result;
}
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = GrabSession(session);
if (result != OEMCrypto_SUCCESS) return result;
OEMCryptoSession* session_context;
result = GetSession(*session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_OPENSESSION);
if (result != OEMCrypto_SUCCESS) {
FreeSession(*session);
return result;
}
result = SetStatePostCall(session_context, API_OPENSESSION);
if (result != OEMCrypto_SUCCESS) FreeSession(*session);
return result;
}
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_CLOSESESSION);
if (result != OEMCrypto_SUCCESS) return result;
return FreeSession(session);
}
OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
const uint8_t* mac_key_context,
uint32_t mac_key_context_length,
const uint8_t* enc_key_context,
uint32_t enc_key_context_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() != OEMCrypto_Keybox) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERATEDERIVEDKEYS);
if (result != OEMCrypto_SUCCESS) return result;
if (mac_key_context == NULL || mac_key_context_length == 0 ||
enc_key_context == NULL || enc_key_context_length == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
result = DeriveMacAndEncryptionKeysFromKeybox(
session_context, mac_key_context, mac_key_context_length, enc_key_context,
enc_key_context_length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = SetStatePostCall(session_context, API_GENERATEDERIVEDKEYS);
cleanup : {
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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_DERIVEKEYSFROMSESSIONKEY);
if (result != OEMCrypto_SUCCESS) return result;
if (enc_session_key == NULL || enc_session_key_length == 0 ||
mac_key_context == NULL || mac_key_context_length == 0 ||
enc_key_context == NULL || enc_key_context_length == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (session_context->drm_private_key == NULL) {
result = OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED;
goto cleanup;
}
if (session_context->drm_private_key->allowed_schemes != kSign_RSASSA_PSS) {
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
/* 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];
uint32_t session_key_length = sizeof(session_key);
session_context->drm_private_key->key_operation = DRM_RSA_PRIVATE_KEY_DECRYPT;
if (!CheckKey(session_context->drm_private_key, DRM_RSA_PRIVATE_KEY,
DRM_RSA_PRIVATE_KEY_DECRYPT)) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
result =
RSADecrypt(session_context->drm_private_key->key_handle, enc_session_key,
enc_session_key_length, session_key, &session_key_length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
/* Session key can only be 16 bytes. */
if (session_key_length != KEY_SIZE_128) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
result =
CreateKey(&session_context->session_key, session_key, session_key_length,
SESSION_KEY, SESSION_KEY_DERIVE, session_key_length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
if (!CheckKey(session_context->session_key, SESSION_KEY,
SESSION_KEY_DERIVE)) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
result = DeriveMacAndEncryptionKeysFromCryptoKey(
session_context, session_context->session_key, mac_key_context,
mac_key_context_length, enc_key_context, enc_key_context_length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = SetStatePostCall(session_context, API_DERIVEKEYSFROMSESSIONKEY);
cleanup : {
OEMCryptoResult free_key_result = FreeKey(&session_context->drm_private_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
free_key_result = FreeKey(&session_context->session_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_GenerateNonce(OEMCrypto_SESSION session,
uint32_t* nonce) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERATENONCE);
if (result != OEMCrypto_SUCCESS) return result;
/* Get the current time. */
uint64_t now;
OEMCrypto_Clock_Security_Level clock_type;
result = GetSystemTime(&now, &clock_type);
if (result != OEMCrypto_SUCCESS) return result;
/* last_nonce_time should only be initialized once. */
static int64_t last_nonce_time = 0;
static int nonce_count = 0;
const int nonce_flood_count = 20;
if (last_nonce_time == now) {
nonce_count++;
if (nonce_count > nonce_flood_count) {
LOGD("Nonce flood detected: now = %lld", 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;
uint8_t* nonce_string = (uint8_t*)(&nonce_value);
while (nonce_value == 0 ||
NonceCollision(&session_context->nonce_table, nonce_value)) {
result = RandomBytes(nonce_string, sizeof(nonce_value));
if (result != OEMCrypto_SUCCESS) return result;
}
AddNonce(&session_context->nonce_table, nonce_value);
*nonce = nonce_value;
return SetStatePostCall(session_context, API_GENERATENONCE);
}
OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERATESIGNATURE);
if (result != OEMCrypto_SUCCESS) return result;
if (signature_length == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (message == NULL || message_length == 0 || signature == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*signature_length = SHA256_DIGEST_LENGTH;
if (CheckKey(session_context->mac_key_client, MAC_KEY_CLIENT,
MAC_KEY_CLIENT_SIGN)) {
result = HMAC_SHA256(session_context->mac_key_client->key_handle, message,
message_length, signature);
} else {
/* Otherwise, use the usage entry to sign a release. */
result = SignReleaseRequest(session_context, message, message_length,
signature, signature_length);
}
if (result != OEMCrypto_SUCCESS) return result;
return SetStatePostCall(session_context, API_GENERATESIGNATURE);
}
OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_LOADKEYS);
if (result != OEMCrypto_SUCCESS) return result;
if (message == NULL || message_length == 0 || signature == NULL ||
signature_length == 0 || key_array_length == 0 || key_array == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (signature_length != SHA256_DIGEST_LENGTH) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
if (license_type != OEMCrypto_ContentLicense &&
license_type != OEMCrypto_EntitlementLicense) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!range_check_substr(message_length, enc_mac_keys_iv, true) ||
!range_check_substr(message_length, enc_mac_keys, true) ||
!range_check_substr(message_length, pst, true) ||
!range_check_substr(message_length, srm_restriction_data, true)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for (uint32_t i = 0; i < key_array_length; i++) {
if (!range_check_substr(message_length, key_array[i].key_id, false) ||
!range_check_substr(message_length, key_array[i].key_data, false) ||
!range_check_substr(message_length, key_array[i].key_data_iv, false) ||
!range_check_substr(message_length, key_array[i].key_control, false) ||
!range_check_substr(message_length, key_array[i].key_control_iv,
false) ||
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) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
/* If set, enc_mac_keys and enc_mac_keys_iv must be the right sizes. */
if ((enc_mac_keys_iv.length > 0 && enc_mac_keys_iv.length != KEY_IV_SIZE) ||
(enc_mac_keys.length > 0 && enc_mac_keys.length != 2 * MAC_KEY_SIZE)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check to see if iv is not right before mac keys and that the bytes before
the mac keys aren't the same as the iv. */
if (enc_mac_keys.offset >= KEY_IV_SIZE && enc_mac_keys.length > 0) {
if (enc_mac_keys_iv.offset + KEY_IV_SIZE == enc_mac_keys.offset) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
} else if (enc_mac_keys.offset >= KEY_IV_SIZE) {
/* Compare bytes before enc_mac_keys only if offset is large enough. */
if (memcmp(message + enc_mac_keys.offset - KEY_IV_SIZE,
message + enc_mac_keys_iv.offset, KEY_IV_SIZE) == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
}
/* If we've already loaded in a license and it isn't the current license type,
fail. */
if (session_context->state == SESSION_KEYS_LOADED &&
session_context->license_type != license_type) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
result = VerifySignatureWithMacKeyServer(session_context, message,
message_length, signature);
if (result != OEMCrypto_SUCCESS) return result;
session_context->license_type = license_type;
session_context->refresh_valid =
range_check_substr(message_length, enc_mac_keys, false) &&
range_check_substr(message_length, enc_mac_keys_iv, false);
session_context->valid_srm_version = false;
if (srm_restriction_data.length != 0) {
const char srm_verification[] = "HDCPDATA";
if (memcmp(message + srm_restriction_data.offset, srm_verification,
sizeof(srm_verification))) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t minimum_version = NetworkToHostU32(
*(const uint32_t*)(message + srm_restriction_data.offset +
sizeof(srm_verification)));
uint32_t current_version = 0;
result = GetCurrentSRMVersion(&current_version);
if (result != OEMCrypto_SUCCESS) {
LOGE("Unable to fetch SRM data with error: %d", result);
} else if (current_version < minimum_version) {
LOGE("SRM Version is too small %d, required: %d", current_version,
minimum_version);
} else {
session_context->valid_srm_version = true;
}
}
result = StartTimer(session_context);
if (result != OEMCrypto_SUCCESS) goto cleanup;
/* Decrypt and install keys in key object. Each key will have a key control
block. They will all have the same nonce. */
session_context->num_content_keys = 0;
session_context->num_entitlement_keys = 0;
for (uint32_t i = 0; i < key_array_length; i++) {
result = InstallKey(
session_context, 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) break;
if (session_context->license_type == OEMCrypto_ContentLicense) {
session_context->num_content_keys++;
} else {
session_context->num_entitlement_keys++;
}
}
FlushNonces(&session_context->nonce_table);
if (result != OEMCrypto_SUCCESS) goto cleanup;
if (session_context->refresh_valid) {
result = UpdateMacKeys(session_context, message + enc_mac_keys.offset,
message + enc_mac_keys_iv.offset);
if (result != OEMCrypto_SUCCESS) goto cleanup;
}
/* If we have loaded a usage entry, or we should have loaded an entry, check
* it. */
if (session_context->usage_entry_status != SESSION_HAS_NO_ENTRY ||
pst.length > 0) {
if (pst.length == 0) {
result = OEMCrypto_ERROR_INVALID_CONTEXT;
goto cleanup;
}
switch (session_context->usage_entry_status) {
case SESSION_HAS_NO_ENTRY:
default:
/* PST specified, but no usage entry loaded. */
result = OEMCrypto_ERROR_INVALID_CONTEXT;
goto cleanup;
break;
case SESSION_HAS_NEW_ENTRY:
/* This was the first time loading the license. Copy the pst and mac
* keys. */
result =
SetUsageEntryPST(session_context, message + pst.offset, pst.length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = SetUsageEntryMacKeys(session_context);
if (result != OEMCrypto_SUCCESS) goto cleanup;
break;
case SESSION_HAS_DEACTIVATED_ENTRY:
/* TODO(b/154764983): fix this. */
/* This was not the first time loading the license, and it was
* deactivated. This will be treated differently in future API versions,
* but for v15, we fall through to the next case statement and allow the
* license to be loaded. */
case SESSION_HAS_LOADED_ENTRY:
/* This was not the first time loading the license. Verify the pst and
* mac keys. */
result = VerfiyUsageEntryPST(session_context, message + pst.offset,
pst.length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = VerifysageEntryMacKeys(session_context);
if (result != OEMCrypto_SUCCESS) goto cleanup;
break;
}
}
result = SetStatePostCall(session_context, API_LOADKEYS);
cleanup : {
OEMCryptoResult free_key_result = FreeKey(&session_context->encryption_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS || !session_context->refresh_valid) {
/* See b/144122560. TODO: reword this once bug is resolved.
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_context);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
}
if (result != OEMCrypto_SUCCESS) {
FreeContentAndEntitlementKeys(session_context);
session_context->state = SESSION_INVALID;
}
}
return result;
}
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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_LOADENTITLEDCONTENTKEYS);
if (result != OEMCrypto_SUCCESS) return result;
if (message == NULL || message_length == 0 || key_array_length == 0 ||
key_array == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (session_context->license_type != OEMCrypto_EntitlementLicense ||
session_context->num_entitlement_keys == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for (uint32_t i = 0; i < key_array_length; i++) {
if (!range_check_substr(message_length, key_array[i].entitlement_key_id,
false) ||
!range_check_substr(message_length, key_array[i].content_key_id,
false) ||
!range_check_substr(message_length, key_array[i].content_key_data,
false) ||
!range_check_substr(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) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
result = OEMCrypto_SUCCESS;
for (uint32_t i = 0; i < key_array_length; i++) {
const OEMCrypto_EntitledContentKeyObject key_object = key_array[i];
CryptoKey* entitlement_key = FindKeyFromTable(
session_context, false, message + key_object.entitlement_key_id.offset,
key_object.entitlement_key_id.length);
if (entitlement_key == NULL) return OEMCrypto_KEY_NOT_ENTITLED;
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 = FreeKey(&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) {
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
if (!CheckKey(entitlement_key, ENTITLEMENT_KEY, ENTITLEMENT_KEY_DECRYPT)) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
uint8_t raw_content_key[KEY_SIZE_128];
result = AESCBCDecrypt(entitlement_key->key_handle, KEY_SIZE_256,
message + key_object.content_key_data.offset,
key_object.content_key_data.length,
message + key_object.content_key_data_iv.offset,
raw_content_key);
if (result != OEMCrypto_SUCCESS) goto cleanup;
CryptoKey** content_key_ptr =
&session_context->content_keys[content_key_index];
result = CreateKey(content_key_ptr, raw_content_key,
key_object.content_key_data.length, CONTENT_KEY,
CONTENT_KEY_DECRYPT, key_object.content_key_data.length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
CryptoKey* content_key = *content_key_ptr;
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;
/* 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++;
}
result = SetStatePostCall(session_context, API_LOADENTITLEDCONTENTKEYS);
cleanup:
if (result != OEMCrypto_SUCCESS) {
FreeContentAndEntitlementKeys(session_context);
session_context->state = SESSION_INVALID;
}
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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_REFRESHKEYS);
if (result != OEMCrypto_SUCCESS) return result;
if (message == NULL || message_length == 0 || signature == NULL ||
signature_length == 0 || key_array_length == 0 || key_array == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
for (uint32_t i = 0; i < key_array_length; i++) {
if (!range_check_substr(message_length, key_array[i].key_id, true) ||
!range_check_substr(message_length, key_array[i].key_control, false) ||
!range_check_substr(message_length, key_array[i].key_control_iv,
true) ||
key_array[i].key_id.length > KEY_ID_MAX_SIZE ||
key_array[i].key_control.length < KEY_CONTROL_SIZE ||
(key_array[i].key_control_iv.length != 0 &&
key_array[i].key_control_iv.length != KEY_IV_SIZE)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
}
if (!session_context->refresh_valid) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (signature_length != SHA256_DIGEST_LENGTH) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
result = VerifySignatureWithMacKeyServer(session_context, message,
message_length, signature);
if (result != OEMCrypto_SUCCESS) return result;
result = OEMCrypto_SUCCESS;
for (uint32_t i = 0; i < key_array_length; i++) {
uint8_t* key_id = NULL;
uint8_t* key_control_iv = NULL;
if (key_array[i].key_id.length != 0) {
key_id = (uint8_t*)message + key_array[i].key_id.offset;
}
if (key_array[i].key_control_iv.length != 0) {
key_control_iv = (uint8_t*)message + key_array[i].key_control_iv.offset;
}
result =
RefreshKey(session_context, key_id, key_array[i].key_id.length,
message + key_array[i].key_control.offset, key_control_iv);
if (result != OEMCrypto_SUCCESS) break;
}
FlushNonces(&session_context->nonce_table);
if (result != OEMCrypto_SUCCESS) return result;
result = StartTimer(session_context);
if (result != OEMCrypto_SUCCESS) return result;
return 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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_QUERYKEYCONTROL);
if (result != OEMCrypto_SUCCESS) return result;
if (key_control_block_length == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (*key_control_block_length < KEY_CONTROL_SIZE) {
*key_control_block_length = KEY_CONTROL_SIZE;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (content_key_id == NULL || content_key_id_length == 0 ||
key_control_block == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*key_control_block_length = KEY_CONTROL_SIZE;
CryptoKey* content_key = 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 &&
CheckKey(session_context->entitlement_keys[entitlement_key_index],
ENTITLEMENT_KEY, ENTITLEMENT_KEY_DECRYPT)) {
control_block = session_context->entitlement_keys[entitlement_key_index]
->key_control_block;
} else {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
} else {
control_block = content_key->key_control_block;
}
uint32_t* block = (uint32_t*)key_control_block;
block[0] = 0; /* Verification optional. */
block[1] = HostToNetworkU32(control_block.duration);
block[2] = 0; /* Nonce optional. */
block[3] = HostToNetworkU32(control_block.control_bits);
return 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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_SELECTKEY);
if (result != OEMCrypto_SUCCESS) return result;
if (content_key_id == NULL || content_key_id_length == 0 ||
content_key_id_length > KEY_ID_MAX_SIZE ||
(cipher_mode != OEMCrypto_CipherMode_CTR &&
cipher_mode != OEMCrypto_CipherMode_CBC) ||
session_context->num_content_keys == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
CryptoKey* content_key = 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) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
CryptoKey* entitlement_key =
session_context->entitlement_keys[content_key->entitlement_key_index];
if (!CheckKey(entitlement_key, ENTITLEMENT_KEY, ENTITLEMENT_KEY_DECRYPT)) {
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;
}
uint32_t duration = key_control_block.duration;
if (duration > 0) {
uint64_t time_elapsed = 0;
result = CurrentTimer(session_context, &time_elapsed);
if (result != OEMCrypto_SUCCESS) return result;
time_elapsed = time_elapsed / 1000; /* Duration is in seconds. */
if (duration < time_elapsed) return OEMCrypto_ERROR_KEY_EXPIRED;
}
if (key_control_block.control_bits & CONTROL_OBSERVE_CGMS) {
result = 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 SetStatePostCall(session_context, API_SELECTKEY);
}
OEMCryptoResult OEMCrypto_DecryptCENC(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv, size_t block_offset,
OEMCrypto_DestBufferDesc* out_buffer,
const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_DECRYPTCENC);
if (result != OEMCrypto_SUCCESS &&
(session_context->state == SESSION_INVALID || is_encrypted)) {
/* If the session is not invalid and the data is not encrypted, we're
allowed to copy data. */
return result;
}
if (data_addr == NULL || data_length == 0 || iv == NULL ||
out_buffer == NULL || pattern == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t max_buffer_size = MaxBufferSizeForDecrypt();
if (max_buffer_size != 0 && data_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
uint8_t* destination;
result =
set_destination(out_buffer, data_length, subsample_flags, &destination);
if (result != OEMCrypto_SUCCESS) return result;
if (!is_encrypted) {
memmove(destination, data_addr, data_length);
} else {
/* Use 0 as use_type since there is no key control bit indicating whether
this key can be used as for DecryptCENC. */
result = CheckCurrentContentKeyUsage(session_context, 0, out_buffer->type);
if (result != OEMCrypto_SUCCESS) return result;
if (session_context
->content_keys[session_context->current_content_key_index]
->cipher_mode == OEMCrypto_CipherMode_CBC) {
if (block_offset != 0) return OEMCrypto_ERROR_INVALID_CONTEXT;
result = DecryptCBC(session_context, iv, pattern, data_addr, data_length,
destination);
if (result != OEMCrypto_SUCCESS) return result;
} else {
result = DecryptCTR(session_context, iv, block_offset, pattern, data_addr,
data_length, destination);
if (result != OEMCrypto_SUCCESS) return result;
}
}
if (session_context->compute_hash) {
bool can_compute_hash = false;
if (session_context->current_content_key_index <
session_context->num_content_keys) {
CryptoKey* current_content_key =
session_context
->content_keys[session_context->current_content_key_index];
if (CheckKey(current_content_key, CONTENT_KEY, CONTENT_KEY_DECRYPT)) {
uint32_t control_bits = 0;
if (current_content_key->is_entitled_content_key &&
current_content_key->entitlement_key_index <
session_context->num_entitlement_keys) {
CryptoKey* entitlement_key =
session_context->entitlement_keys[current_content_key
->entitlement_key_index];
if (CheckKey(entitlement_key, ENTITLEMENT_KEY,
ENTITLEMENT_KEY_DECRYPT)) {
control_bits = entitlement_key->key_control_block.control_bits;
}
} else {
control_bits = current_content_key->key_control_block.control_bits;
}
can_compute_hash =
(control_bits & CONTROL_ALLOW_HASH_VERIFICATION) != 0;
}
}
if (!can_compute_hash) {
session_context->hash_error = OEMCrypto_ERROR_UNKNOWN_FAILURE;
session_context->compute_hash = false;
session_context->current_hash = 0;
session_context->current_frame_number = 0;
} else {
if (OEMCrypto_FirstSubsample & subsample_flags) {
result = Crc32Init(&session_context->current_hash);
if (result != OEMCrypto_SUCCESS) return result;
}
result =
Crc32Cont(destination, data_length, session_context->current_hash,
&session_context->current_hash);
if (result != OEMCrypto_SUCCESS) return result;
if (OEMCrypto_LastSubsample & subsample_flags) {
if (session_context->current_hash != session_context->given_hash) {
/* Update bad_frame_number if this is the first bad frame. */
if (session_context->hash_error == OEMCrypto_SUCCESS) {
session_context->bad_frame_number =
session_context->current_frame_number;
session_context->hash_error = OEMCrypto_ERROR_BAD_HASH;
}
}
session_context->compute_hash = false;
}
}
}
return SetStatePostCall(session_context, API_DECRYPTCENC);
}
OEMCryptoResult OEMCrypto_CopyBuffer(OEMCrypto_SESSION session,
const uint8_t* data_addr,
size_t data_length,
OEMCrypto_DestBufferDesc* out_buffer,
uint8_t subsample_flags) {
if (g_odkitee_system_state != SYSTEM_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. */
if (data_addr == NULL || data_length == 0 || out_buffer == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t max_buffer_size = MaxBufferSizeForDecrypt();
if (max_buffer_size != 0 && data_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
uint8_t* destination;
OEMCryptoResult result =
set_destination(out_buffer, data_length, subsample_flags, &destination);
if (result != OEMCrypto_SUCCESS) return result;
memmove(destination, data_addr, data_length);
return OEMCrypto_SUCCESS;
}
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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERICENCRYPT);
if (in_buffer == NULL || buffer_length == 0 || iv == NULL ||
out_buffer == NULL || algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length % AES_BLOCK_SIZE != 0) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t max_buffer_size = MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
result = CheckCurrentContentKeyUsage(session_context, CONTROL_ALLOW_ENCRYPT,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
CryptoKey* current_content_key =
session_context->content_keys[session_context->current_content_key_index];
if (current_content_key->key_size != KEY_SIZE_128) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = AESCBCEncrypt(current_content_key->key_handle, in_buffer,
buffer_length, iv, out_buffer);
if (result != OEMCrypto_SUCCESS) return result;
return 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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERICDECRYPT);
if (in_buffer == NULL || buffer_length == 0 || iv == NULL ||
out_buffer == NULL || algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (buffer_length % AES_BLOCK_SIZE != 0) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t max_buffer_size = MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
result = CheckCurrentContentKeyUsage(session_context, CONTROL_ALLOW_DECRYPT,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
CryptoKey* current_content_key =
session_context->content_keys[session_context->current_content_key_index];
if (current_content_key->key_size != KEY_SIZE_128) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = AESCBCDecrypt(current_content_key->key_handle,
current_content_key->key_size, in_buffer,
buffer_length, iv, out_buffer);
if (result != OEMCrypto_SUCCESS) return result;
return 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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERICSIGN);
if (signature_length == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (in_buffer == NULL || buffer_length == 0 || signature == NULL ||
algorithm != OEMCrypto_HMAC_SHA256) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*signature_length = SHA256_DIGEST_LENGTH;
uint32_t max_buffer_size = MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
result = CheckCurrentContentKeyUsage(session_context, CONTROL_ALLOW_SIGN,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
CryptoKey* 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->key_size != KEY_SIZE_256) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = HMAC_SHA256(current_content_key->key_handle, in_buffer,
buffer_length, signature);
if (result != OEMCrypto_SUCCESS) return result;
return 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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERICVERIFY);
if (in_buffer == NULL || buffer_length == 0 || signature == NULL ||
signature_length != SHA256_DIGEST_LENGTH ||
algorithm != OEMCrypto_HMAC_SHA256) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t max_buffer_size = MaxBufferSizeForGenericCrypto();
if (max_buffer_size != 0 && buffer_length > max_buffer_size) {
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
result = CheckCurrentContentKeyUsage(session_context, CONTROL_ALLOW_VERIFY,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
CryptoKey* 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->key_size != KEY_SIZE_256) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t calculated_signature[SHA256_DIGEST_LENGTH];
result = HMAC_SHA256(current_content_key->key_handle, in_buffer,
buffer_length, calculated_signature);
if (result != OEMCrypto_SUCCESS) return result;
if (memcmp(signature, calculated_signature, SHA256_DIGEST_LENGTH) != 0) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
return SetStatePostCall(session_context, API_GENERICVERIFY);
}
OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert(const uint8_t* rot,
size_t rotLength,
uint8_t* wrappedRot,
size_t* wrappedRotLength,
const uint8_t* transportKey,
size_t transportKeyLength) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* rot,
size_t rotLength) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ProvisioningError;
}
return GetProvisioningMethod();
}
OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() == OEMCrypto_OEMCertificate) {
return ValidateOEMPrivateKey();
} else if (GetProvisioningMethod() == OEMCrypto_Keybox) {
return ValidateKeybox();
} else {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() != OEMCrypto_Keybox &&
GetProvisioningMethod() != OEMCrypto_OEMCertificate) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (idLength == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (GetProvisioningMethod() == OEMCrypto_OEMCertificate) {
uint32_t id_length;
OEMCryptoResult result = GetDeviceIDForOEMCert(deviceID, &id_length);
*idLength = id_length;
return result;
} else {
if (*idLength < KEYBOX_DEVICE_ID_SIZE) {
*idLength = KEYBOX_DEVICE_ID_SIZE;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (deviceID == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
return GetDeviceIDFromKeybox(deviceID);
}
}
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() != OEMCrypto_Keybox) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (keyDataLength == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (*keyDataLength < KEYBOX_KEY_DATA_SIZE) {
*keyDataLength = KEYBOX_KEY_DATA_SIZE;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (keyData == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
*keyDataLength = KEYBOX_KEY_DATA_SIZE;
return GetKeyDataFromKeybox(keyData);
}
OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() != OEMCrypto_Keybox) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (buffer == NULL || length < sizeof(WidevineKeybox)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return LoadTestKeybox(buffer);
}
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(OEMCrypto_SESSION session,
uint8_t* public_cert,
size_t* public_cert_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() != OEMCrypto_OEMCertificate) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GETOEMPUBLICCERTIFICATE);
if (result != OEMCrypto_SUCCESS) return result;
if (public_cert_length == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
uint32_t oem_cert_length;
result = GetOEMPublicCertificateLength(&oem_cert_length);
if (result != OEMCrypto_SUCCESS) return result;
if (oem_cert_length > *public_cert_length) {
*public_cert_length = oem_cert_length;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (public_cert == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
*public_cert_length = oem_cert_length;
result = GetOEMPublicCertificate(public_cert);
if (result != OEMCrypto_SUCCESS) return result;
return SetStatePostCall(session_context, API_GETOEMPUBLICCERTIFICATE);
}
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (randomData == NULL || dataLength == 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return RandomBytes(randomData, dataLength);
}
uint32_t OEMCrypto_APIVersion(void) { return 15; }
const char* OEMCrypto_BuildInformation(void) {
return "Widevine OEMCrypto TA v15.2.0";
}
uint8_t OEMCrypto_Security_Patch_Level(void) { return 0; }
const char* OEMCrypto_SecurityLevel(void) { return "L3"; }
OEMCryptoResult OEMCrypto_GetHDCPCapability(
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (current == NULL || maximum == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*current = CurrentHDCPCapability();
*maximum = MaxHDCPCapability();
return OEMCrypto_SUCCESS;
}
bool OEMCrypto_SupportsUsageTable(void) { return true; }
size_t OEMCrypto_MaximumUsageTableHeaderSize() {
return MAX_NUMBER_OF_USAGE_ENTRIES;
}
/* TODO((b/154764983): Obsolete. This function will be removed in v16. */
OEMCryptoResult OEMCrypto_UpdateUsageTable(void) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
/* TODO((b/154764983): Obsolete. This function will be removed in v16. */
OEMCryptoResult OEMCrypto_DeleteUsageEntry(
OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length,
const uint8_t* message, size_t message_length, const uint8_t* signature,
size_t signature_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
/* TODO((b/154764983): Obsolete. This function will be removed in v16. */
OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t* pst,
size_t pst_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
bool OEMCrypto_IsAntiRollbackHwPresent(void) {
return IsAntiRollbackHWPresent();
}
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (count == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
uint32_t num_open_sessions = 0;
OEMCryptoResult result = NumberOfOpenSessions(&num_open_sessions);
*count = num_open_sessions;
return result;
}
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (max == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
*max = MaxNumberOfSessions();
return OEMCrypto_SUCCESS;
}
uint32_t OEMCrypto_SupportedCertificates(void) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) return 0;
return SupportedCertificates();
}
bool OEMCrypto_IsSRMUpdateSupported(void) { return false; }
OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
uint32_t OEMCrypto_GetAnalogOutputFlags(void) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) return 0;
uint32_t analog_bits = 0;
if (HasAnalogDisplay()) analog_bits |= 0x1;
if (CanDisableAnalogDisplay()) analog_bits |= 0x2;
if (SupportsCGMS_A()) analog_bits |= 0x4;
return analog_bits;
}
uint32_t OEMCrypto_ResourceRatingTier(void) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) return 0;
return GetResourceRatingTier();
}
static OEMCryptoResult RewrapDeviceRSAKey(OEMCryptoSession* session,
const uint8_t* enc_rsa_key,
uint32_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
ASSERT(session != NULL && enc_rsa_key != NULL && enc_rsa_key_length != 0 &&
enc_rsa_key_iv != NULL && wrapped_rsa_key != NULL,
"Parameters are NULL or 0");
if (!CheckKey(session->encryption_key, ENCRYPTION_KEY,
ENCRYPTION_KEY_DECRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Decrypt and deserialize the DRM private key. */
uint8_t pkcs8_rsa_key[PKCS8_RSA_KEY_MAX_SIZE];
OEMCryptoResult result = AESCBCDecrypt(
session->encryption_key->key_handle, session->encryption_key->key_size,
enc_rsa_key, enc_rsa_key_length, enc_rsa_key_iv, pkcs8_rsa_key);
if (result != OEMCrypto_SUCCESS) return result;
uint32_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
if (padding > 16) return OEMCrypto_ERROR_INVALID_RSA_KEY;
uint32_t rsa_key_length = 0;
if (SubOverflowU32(enc_rsa_key_length, padding, &rsa_key_length)) {
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
/* Check that it's a valid DRM RSA key. */
result = LoadDRMRSAKey(session, pkcs8_rsa_key, rsa_key_length);
if (result != OEMCrypto_SUCCESS) return result;
WrappedRSAKey* wrapped = (WrappedRSAKey*)(wrapped_rsa_key);
/* Pick a random context and IV for generating keys. */
result = RandomBytes(wrapped->context, sizeof(wrapped->context));
if (result != OEMCrypto_SUCCESS) return result;
result = RandomBytes(wrapped->iv, sizeof(wrapped->iv));
if (result != OEMCrypto_SUCCESS) return result;
/* Generate mac and encryption keys based off of the device key. */
result = DeriveMacAndEncryptionKeysFromDeviceKey(
session, wrapped->context, sizeof(wrapped->context), wrapped->context,
sizeof(wrapped->context));
if (result != OEMCrypto_SUCCESS) return result;
/* Encrypt the DRM key with the derived encryption key. */
if (!CheckKey(session->encryption_key, ENCRYPTION_KEY,
ENCRYPTION_KEY_ENCRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = AESCBCEncrypt(session->encryption_key->key_handle, pkcs8_rsa_key,
enc_rsa_key_length, wrapped->iv, wrapped->enc_rsa_key);
if (result != OEMCrypto_SUCCESS) return result;
/* Use the mac_key_server to compute the signature of the rest of the
WrappedRSAKey. */
if (!CheckKey(session->mac_key_server, MAC_KEY_SERVER,
MAC_KEY_SERVER_VERIFY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t wrapped_buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
return HMAC_SHA256(session->mac_key_server->key_handle, wrapped->context,
wrapped_buffer_size - sizeof(wrapped->signature),
wrapped->signature);
}
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
const uint8_t* encrypted_message_key, size_t encrypted_message_key_length,
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() != OEMCrypto_OEMCertificate) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_REWRAPDEVICERSAKEY30);
if (result != OEMCrypto_SUCCESS) return result;
if (wrapped_rsa_key_length == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
/* For the TA implementation, the wrapped key and the encrypted key are the
same size -- just encrypted with different keys. We add 32 bytes for a
context, 32 for iv, and 32 bytes for a signature. Important: This layout
must match OEMCrypto_LoadDeviceRSAKey below. */
uint32_t buffer_size = 0;
if (AddOverflowUX(enc_rsa_key_length, sizeof(WrappedRSAKey), &buffer_size)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (*wrapped_rsa_key_length < buffer_size) {
*wrapped_rsa_key_length = buffer_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*wrapped_rsa_key_length = buffer_size;
if (unaligned_nonce == NULL || encrypted_message_key == NULL ||
encrypted_message_key_length == 0 || enc_rsa_key == NULL ||
enc_rsa_key_length == 0 || enc_rsa_key_iv == NULL ||
wrapped_rsa_key == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (enc_rsa_key_length > PKCS8_RSA_KEY_MAX_SIZE) {
/* Impossible to continue. */
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
uint32_t nonce = unaligned_dereference_uint32(unaligned_nonce);
/* Validate nonce. */
if (!CheckNonce(&session_context->nonce_table, nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
FlushNonces(&session_context->nonce_table);
/* 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];
uint32_t message_key_length = sizeof(message_key);
result = 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. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = CreateKey(&session_context->encryption_key, message_key,
message_key_length, ENCRYPTION_KEY, ENCRYPTION_KEY_DECRYPT,
KEY_SIZE_128);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = RewrapDeviceRSAKey(session_context, enc_rsa_key, enc_rsa_key_length,
enc_rsa_key_iv, wrapped_rsa_key);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = SetStatePostCall(session_context, API_REWRAPDEVICERSAKEY30);
cleanup : {
OEMCryptoResult free_key_result = FreeKey(&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;
}
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (GetProvisioningMethod() != OEMCrypto_Keybox) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_REWRAPDEVICERSAKEY);
if (result != OEMCrypto_SUCCESS) return result;
if (wrapped_rsa_key_length == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
/* For the TA implementation, the wrapped key and the encrypted key are the
same size -- just encrypted with different keys. We add 32 bytes for a
context, 32 for iv, and 32 bytes for a signature. Important: This layout
must match OEMCrypto_LoadDeviceRSAKey below. */
uint32_t buffer_size = 0;
if (AddOverflowUX(enc_rsa_key_length, sizeof(WrappedRSAKey), &buffer_size)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (*wrapped_rsa_key_length < buffer_size) {
*wrapped_rsa_key_length = buffer_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*wrapped_rsa_key_length = buffer_size;
if (message == NULL || message_length == 0 || signature == NULL ||
signature_length != SHA256_DIGEST_LENGTH || unaligned_nonce == NULL ||
enc_rsa_key == NULL || enc_rsa_key_length == 0 ||
enc_rsa_key_iv == NULL || wrapped_rsa_key_length == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (enc_rsa_key_length > PKCS8_RSA_KEY_MAX_SIZE) {
/* Impossible to continue. */
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
if (!range_check(message, message_length, (uint8_t*)unaligned_nonce,
sizeof(uint32_t), false) ||
!range_check(message, message_length, enc_rsa_key, enc_rsa_key_length,
false) ||
!range_check(message, message_length, enc_rsa_key_iv, KEY_IV_SIZE,
false)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
uint32_t nonce = unaligned_dereference_uint32(unaligned_nonce);
/* Validate nonce. */
if (!CheckNonce(&session_context->nonce_table, nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
FlushNonces(&session_context->nonce_table);
/* Use mac_key_server from previous call to GenerateDerivedKeys to verify the
message. */
result = VerifySignatureWithMacKeyServer(session_context, message,
message_length, signature);
if (result != OEMCrypto_SUCCESS) goto cleanup;
if (session_context->encryption_key != NULL)
session_context->encryption_key->key_operation = ENCRYPTION_KEY_DECRYPT;
result = RewrapDeviceRSAKey(session_context, enc_rsa_key, enc_rsa_key_length,
enc_rsa_key_iv, wrapped_rsa_key);
if (result != OEMCrypto_SUCCESS) goto cleanup;
result = SetStatePostCall(session_context, API_REWRAPDEVICERSAKEY);
cleanup : {
OEMCryptoResult free_key_result = FreeKey(&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;
}
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_LOADDEVICERSAKEY);
if (result != OEMCrypto_SUCCESS) return result;
if (wrapped_rsa_key == NULL ||
wrapped_rsa_key_length <= sizeof(WrappedRSAKey)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
WrappedRSAKey* wrapped = (WrappedRSAKey*)(wrapped_rsa_key);
result = DeriveMacAndEncryptionKeysFromDeviceKey(
session_context, wrapped->context, sizeof(wrapped->context),
wrapped->context, sizeof(wrapped->context));
if (result != OEMCrypto_SUCCESS) goto cleanup;
session_context->encryption_key->key_operation = ENCRYPTION_KEY_DECRYPT;
if (!CheckKey(session_context->encryption_key, ENCRYPTION_KEY,
ENCRYPTION_KEY_DECRYPT)) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
/* Decrypt the DRM private key. */
uint8_t pkcs8_rsa_key[PKCS8_RSA_KEY_MAX_SIZE];
uint32_t enc_rsa_key_length = wrapped_rsa_key_length - sizeof(WrappedRSAKey);
result = AESCBCDecrypt(session_context->encryption_key->key_handle,
session_context->encryption_key->key_size,
wrapped->enc_rsa_key, enc_rsa_key_length, wrapped->iv,
pkcs8_rsa_key);
if (result != OEMCrypto_SUCCESS) goto cleanup;
uint32_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
if (padding > 16) {
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
goto cleanup;
}
uint32_t rsa_key_length = 0;
if (SubOverflowU32(enc_rsa_key_length, padding, &rsa_key_length)) {
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
goto cleanup;
}
result = VerifySignatureWithMacKeyServer(
session_context, wrapped->context,
wrapped_rsa_key_length - sizeof(wrapped->signature), wrapped->signature);
if (result != OEMCrypto_SUCCESS) goto cleanup;
/* Attempt to load in the DRM private key. */
result = LoadDRMRSAKey(session_context, pkcs8_rsa_key, rsa_key_length);
if (result != OEMCrypto_SUCCESS) goto cleanup;
session_context->drm_private_key->key_operation = DRM_RSA_PRIVATE_KEY_SIGN;
result = SetStatePostCall(session_context, API_LOADDEVICERSAKEY);
cleanup : {
OEMCryptoResult free_key_result = FreeMacAndEncryptionKeys(session_context);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
if (result != OEMCrypto_SUCCESS) {
FreeKey(&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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GENERATERSASIGNATURE);
if (result != OEMCrypto_SUCCESS) return result;
if (message == NULL || message_length == 0 || signature_length == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (padding_scheme == kSign_PKCS1_Block1 && message_length > 83) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
if (session_context->state == SESSION_LOAD_OEM_RSA_KEY &&
padding_scheme == kSign_RSASSA_PSS) {
result = SignMessageWithOEMPrivateKey(message, message_length, signature,
(uint32_t*)signature_length);
if (result != OEMCrypto_SUCCESS) return result;
} else if (session_context->state == SESSION_LOAD_DRM_RSA_KEY) {
if (!CheckKey(session_context->drm_private_key, DRM_RSA_PRIVATE_KEY,
DRM_RSA_PRIVATE_KEY_SIGN)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
} else if ((session_context->drm_private_key->allowed_schemes &
padding_scheme) != padding_scheme) {
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
return RSASign(session_context->drm_private_key->key_handle, message,
message_length, signature, (uint32_t*)signature_length,
padding_scheme);
} else {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return SetStatePostCall(session_context, API_GENERATERSASIGNATURE);
}
OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer,
size_t* header_buffer_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return CreateUsageTableHeader(header_buffer, header_buffer_length);
}
OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* header_buffer,
size_t header_buffer_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return LoadUsageTableHeader(header_buffer, header_buffer_length);
}
OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session,
uint32_t* usage_entry_number) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO(b/158763087): add pre/post checks for session state. */
result = CreateNewUsageEntry(session_context, usage_entry_number);
return result;
}
OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session,
uint32_t usage_entry_number,
const uint8_t* buffer,
size_t buffer_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO(b/158763087): add pre/post checks for session state. */
result = LoadUsageEntry(session_context, usage_entry_number, buffer,
buffer_length);
return result;
}
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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO(b/158763087): add pre/post checks for session state. */
result =
UpdateUsageEntry(session_context, header_buffer, header_buffer_length,
entry_buffer, entry_buffer_length);
return result;
}
OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO(b/158763087): add pre/post checks for session state. */
result = DeactivateUsageEntry(session_context, pst, pst_length);
return result;
}
OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
const uint8_t* pst, size_t pst_length,
uint8_t* buffer, size_t* buffer_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO(b/158763087): add pre/post checks for session state. */
result = ReportUsage(session_context, pst, pst_length, buffer, buffer_length);
return result;
}
OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session,
uint32_t new_index) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO(b/158763087): add pre/post checks for session state. */
return MoveEntry(session_context, new_index);
}
OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count,
uint8_t* header_buffer,
size_t* header_buffer_length) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return ShrinkUsageTableHeader(new_entry_count, header_buffer,
header_buffer_length);
}
OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length) {
/* Only needed for upgrades from v12. */
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void) {
/* Only needed for upgrades from v12. */
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_RemoveSRM(void) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
size_t pst_length) {
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_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_SETDECRYPTHASH);
if (result != OEMCrypto_SUCCESS) return result;
if (hash == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (hash_length < sizeof(uint32_t)) return OEMCrypto_ERROR_SHORT_BUFFER;
session_context->compute_hash = true;
session_context->current_frame_number = frame_number;
/* This is safe since it's host-to-host. */
session_context->given_hash = *(const uint32_t*)hash;
return SetStatePostCall(session_context, API_SETDECRYPTHASH);
}
OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session,
uint32_t* failed_frame_number) {
if (g_odkitee_system_state != SYSTEM_INITIALIZED) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoSession* session_context;
OEMCryptoResult result = GetSession(session, &session_context);
if (result != OEMCrypto_SUCCESS) return result;
result = CheckStatePreCall(session_context, API_GETHASHERRORCODE);
if (result != OEMCrypto_SUCCESS) return result;
if (failed_frame_number == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (session_context->hash_error != OEMCrypto_SUCCESS) {
*failed_frame_number = session_context->bad_frame_number;
}
result = SetStatePostCall(session_context, API_GETHASHERRORCODE);
if (result != OEMCrypto_SUCCESS) return result;
return session_context->hash_error;
}