2032 lines
80 KiB
C
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(¤t_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;
|
|
}
|