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

947 lines
36 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 "oemcrypto_session.h"
#include "string.h"
#include "assert_interface.h"
#include "clock_interface.h"
#include "oemcrypto_endianness.h"
#include "logging_interface.h"
#include "oemcrypto_key_table.h"
#include "oemcrypto_nonce_table.h"
#include "oemcrypto_overflow.h"
#include "oemcrypto_session_key_table.h"
#include "oemcrypto_usage_table.h"
#include "root_of_trust_interface.h"
static void clear_nonce_table(OEMCryptoSession* session) {
for (int i = 0; i < NONCE_TABLE_SIZE; i++) {
session->nonce_table.age[i] = 0;
session->nonce_table.nonces[i] = 0;
session->nonce_table.state[i] = NT_STATE_INVALID;
}
}
OEMCryptoResult InitializeSession(OEMCryptoSession* session, uint32_t index) {
ASSERT(session != NULL, "session is NULL");
session->session_id = index;
session->state = SESSION_INVALID;
clear_nonce_table(session);
session->drm_private_key = NULL;
session->mac_key_client = NULL;
session->mac_key_server = NULL;
session->encryption_key = NULL;
session->session_key = NULL;
session->refresh_valid = false;
session->license_type = OEMCrypto_ContentLicense;
session->current_content_key_index = CONTENT_KEYS_PER_SESSION;
for (int i = 0; i < CONTENT_KEYS_PER_SESSION; i++) {
session->content_keys[i] = NULL;
}
session->num_content_keys = 0;
for (int i = 0; i < ENTITLEMENT_KEYS_PER_SESSION; i++) {
session->entitlement_keys[i] = NULL;
}
session->num_entitlement_keys = 0;
session->valid_srm_version = false;
session->timer_start = 0;
session->compute_hash = false;
session->current_hash = 0;
session->given_hash = 0;
session->current_frame_number = 0;
session->bad_frame_number = 0;
session->hash_error = OEMCrypto_SUCCESS;
session->usage_entry_status = SESSION_HAS_NO_ENTRY;
session->usage_entry_number = MAX_NUMBER_OF_USAGE_ENTRIES;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult TerminateSession(OEMCryptoSession* session) {
ASSERT(session != NULL, "session is NULL");
OEMCryptoResult result = OEMCrypto_SUCCESS;
result = FreeKey(&session->drm_private_key);
OEMCryptoResult free_key_result = FreeKey(&session->mac_key_client);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
free_key_result = FreeKey(&session->mac_key_server);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
free_key_result = FreeKey(&session->encryption_key);
if (result == OEMCrypto_SUCCESS) result = free_key_result;
free_key_result = FreeKey(&session->session_key);
if (result == OEMCrypto_SUCCESS) result = free_key_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;
}
clear_nonce_table(session);
if (session->usage_entry_status != SESSION_HAS_NO_ENTRY) {
ReleaseEntry(session, session->usage_entry_number);
}
memset(session, 0, sizeof(OEMCryptoSession));
return result;
}
OEMCryptoResult CheckStatePreCall(OEMCryptoSession* session,
OEMCryptoSessionAPI api) {
ASSERT(session != NULL, "session is NULL");
switch (api) {
case API_OPENSESSION:
switch (session->state) {
case SESSION_INVALID:
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
case API_CLOSESESSION:
return OEMCrypto_SUCCESS;
case API_GETOEMPUBLICCERTIFICATE:
switch (session->state) {
case (SESSION_OPENED):
case (SESSION_LOAD_OEM_RSA_KEY):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_GENERATEDERIVEDKEYS:
switch (session->state) {
case (SESSION_OPENED):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_GENERATENONCE:
switch (session->state) {
case (SESSION_OPENED):
case (SESSION_DERIVED_KEYS):
case (SESSION_DERIVED_KEYS_FROM_SESSION_KEY):
case (SESSION_KEYS_LOADED):
case (SESSION_DECRYPT_KEY_SELECTED):
case (SESSION_LOAD_OEM_RSA_KEY):
case (SESSION_LOAD_DRM_RSA_KEY):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_GENERATERSASIGNATURE:
switch (session->state) {
case (SESSION_LOAD_OEM_RSA_KEY):
case (SESSION_LOAD_DRM_RSA_KEY):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_REWRAPDEVICERSAKEY:
switch (session->state) {
case (SESSION_DERIVED_KEYS):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_REWRAPDEVICERSAKEY30:
switch (session->state) {
case (SESSION_LOAD_OEM_RSA_KEY):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_LOADDEVICERSAKEY:
switch (session->state) {
case (SESSION_LOAD_OEM_RSA_KEY):
case (SESSION_OPENED):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_DERIVEKEYSFROMSESSIONKEY:
switch (session->state) {
case (SESSION_LOAD_DRM_RSA_KEY):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_LOADKEYS:
switch (session->state) {
case (SESSION_DERIVED_KEYS_FROM_SESSION_KEY):
case (SESSION_KEYS_LOADED):
case (SESSION_DECRYPT_KEY_SELECTED):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_REFRESHKEYS:
switch (session->state) {
case (SESSION_KEYS_LOADED):
case (SESSION_DECRYPT_KEY_SELECTED):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_GENERATESIGNATURE:
switch (session->state) {
case (SESSION_DERIVED_KEYS):
case (SESSION_DERIVED_KEYS_FROM_SESSION_KEY):
case (SESSION_KEYS_LOADED):
case (SESSION_DECRYPT_KEY_SELECTED):
/* The next two are needed for license release in v15. */
case (SESSION_OPENED):
case (SESSION_LOAD_DRM_RSA_KEY):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_SELECTKEY:
case API_LOADENTITLEDCONTENTKEYS:
case API_QUERYKEYCONTROL:
switch (session->state) {
case (SESSION_KEYS_LOADED):
case (SESSION_DECRYPT_KEY_SELECTED):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_DECRYPTCENC:
case API_GENERICENCRYPT:
case API_GENERICDECRYPT:
case API_GENERICSIGN:
case API_GENERICVERIFY:
case API_GETHASHERRORCODE:
switch (session->state) {
case (SESSION_DECRYPT_KEY_SELECTED):
return OEMCrypto_SUCCESS;
default:
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
case API_SETDECRYPTHASH:
switch (session->state) {
case (SESSION_INVALID):
return OEMCrypto_ERROR_INVALID_CONTEXT;
default:
return OEMCrypto_SUCCESS;
}
default:
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}
OEMCryptoResult SetStatePostCall(OEMCryptoSession* session,
OEMCryptoSessionAPI api) {
ASSERT(session != NULL, "session is NULL");
switch (api) {
case API_OPENSESSION:
session->state = SESSION_OPENED;
break;
case API_GETOEMPUBLICCERTIFICATE:
session->state = SESSION_LOAD_OEM_RSA_KEY;
break;
case API_GENERATEDERIVEDKEYS:
session->state = SESSION_DERIVED_KEYS;
break;
case API_GENERATENONCE:
case API_GENERATERSASIGNATURE:
case API_REWRAPDEVICERSAKEY:
case API_REWRAPDEVICERSAKEY30:
case API_GENERATESIGNATURE:
case API_REFRESHKEYS:
case API_LOADENTITLEDCONTENTKEYS:
case API_GENERICENCRYPT:
case API_GENERICDECRYPT:
case API_GENERICSIGN:
case API_GENERICVERIFY:
case API_SETDECRYPTHASH:
case API_GETHASHERRORCODE:
case API_QUERYKEYCONTROL:
/* State does not change. */
break;
case API_LOADDEVICERSAKEY:
session->state = SESSION_LOAD_DRM_RSA_KEY;
break;
case API_DERIVEKEYSFROMSESSIONKEY:
session->state = SESSION_DERIVED_KEYS_FROM_SESSION_KEY;
break;
case API_LOADKEYS:
session->state = SESSION_KEYS_LOADED;
break;
case API_SELECTKEY:
case API_DECRYPTCENC:
session->state = SESSION_DECRYPT_KEY_SELECTED;
break;
default:
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult StartTimer(OEMCryptoSession* session) {
ASSERT(session != NULL, "session is NULL");
OEMCrypto_Clock_Security_Level clock_type;
return GetSystemTime(&session->timer_start, &clock_type);
}
OEMCryptoResult CurrentTimer(const OEMCryptoSession* session, uint64_t* diff) {
ASSERT(session != NULL, "session is NULL");
ASSERT(diff != NULL, "diff is NULL");
uint64_t current_time;
OEMCrypto_Clock_Security_Level clock_type;
OEMCryptoResult result = GetSystemTime(&current_time, &clock_type);
if (result != OEMCrypto_SUCCESS) return result;
if (SubOverflowU64(current_time, session->timer_start, diff)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult LoadDRMRSAKey(OEMCryptoSession* session,
const uint8_t* pkcs8_rsa_key,
uint32_t rsa_key_length) {
ASSERT(session != NULL, "session is NULL");
ASSERT(pkcs8_rsa_key != NULL, "pkcs8_rsa_key is NULL");
ASSERT(rsa_key_length != 0, "rsa_key_length is 0");
if (rsa_key_length < 8) return OEMCrypto_ERROR_INVALID_RSA_KEY;
/* Determine the padding scheme allowed. */
uint32_t allowed_schemes;
if ((memcmp(pkcs8_rsa_key, "SIGN", 4) == 0)) {
uint32_t schemes_n;
memcpy((uint8_t*)&schemes_n, pkcs8_rsa_key + 4, sizeof(uint32_t));
allowed_schemes = HostToNetworkU32(schemes_n);
pkcs8_rsa_key += 8;
rsa_key_length -= 8;
} else {
allowed_schemes = kSign_RSASSA_PSS;
}
if ((GetRSAPaddingSchemes() & allowed_schemes) != allowed_schemes) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
/* Verify that the key is valid. */
OEMCryptoResult result =
CreateKey(&session->drm_private_key, pkcs8_rsa_key, rsa_key_length,
DRM_RSA_PRIVATE_KEY, UNKNOWN_KEY_OPERATION, UNKNOWN_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
session->drm_private_key->allowed_schemes = allowed_schemes;
return OEMCrypto_SUCCESS;
}
typedef enum DeriveKeyType {
GENERIC_DERIVE_KEY_TYPE = 0x24514123,
KEYBOX_DERIVE_KEY_TYPE = 0x0be5a960,
DEVICE_KEY_DERIVE_KEY_TYPE = 0x4933dcdd,
} DeriveKeyType;
static OEMCryptoResult DeriveKey(const CryptoKey* master_key, uint8_t counter,
const uint8_t* context,
uint32_t context_length, uint8_t* out,
DeriveKeyType derive_key_type) {
switch (derive_key_type) {
case GENERIC_DERIVE_KEY_TYPE:
return DeriveKeyFromKeyHandle(master_key->key_handle, counter, context,
context_length, out);
case DEVICE_KEY_DERIVE_KEY_TYPE:
return DeriveKeyFromDeviceKey(counter, context, context_length, out);
case KEYBOX_DERIVE_KEY_TYPE:
return DeriveKeyFromKeybox(counter, context, context_length, out);
default:
/* Unimplemented or invalid. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
static OEMCryptoResult DeriveMacAndEncryptionKeys(
OEMCryptoSession* session, const CryptoKey* master_key,
const uint8_t* mac_key_context, uint32_t mac_key_context_length,
const uint8_t* enc_key_context, uint32_t enc_key_context_length,
DeriveKeyType derive_key_type) {
ASSERT(
session != NULL && mac_key_context != NULL &&
mac_key_context_length != 0 && enc_key_context != NULL &&
enc_key_context_length != 0 &&
!(master_key == NULL && derive_key_type == GENERIC_DERIVE_KEY_TYPE),
"Parameters are NULL or 0");
/* Generate derived keys for mac keys. */
uint8_t mac_key_server[MAC_KEY_SIZE];
uint8_t mac_key_client[MAC_KEY_SIZE];
OEMCryptoResult result =
DeriveKey(master_key, 1, mac_key_context, mac_key_context_length,
mac_key_server, derive_key_type);
if (result != OEMCrypto_SUCCESS) return result;
result = DeriveKey(master_key, 2, mac_key_context, mac_key_context_length,
mac_key_server + KEY_SIZE_128, derive_key_type);
if (result != OEMCrypto_SUCCESS) return result;
result = DeriveKey(master_key, 3, mac_key_context, mac_key_context_length,
mac_key_client, derive_key_type);
if (result != OEMCrypto_SUCCESS) return result;
result = DeriveKey(master_key, 4, mac_key_context, mac_key_context_length,
mac_key_client + KEY_SIZE_128, derive_key_type);
if (result != OEMCrypto_SUCCESS) return result;
/* Generate derived key for encryption key. */
uint8_t encryption_key[KEY_SIZE_128];
result = DeriveKey(master_key, 1, enc_key_context, enc_key_context_length,
encryption_key, derive_key_type);
if (result != OEMCrypto_SUCCESS) return result;
result = CreateKey(&session->mac_key_server, mac_key_server, MAC_KEY_SIZE,
MAC_KEY_SERVER, MAC_KEY_SERVER_VERIFY, MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
result = CreateKey(&session->mac_key_client, mac_key_client, MAC_KEY_SIZE,
MAC_KEY_CLIENT, MAC_KEY_CLIENT_SIGN, MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) {
FreeKey(&session->mac_key_server);
return result;
}
result = CreateKey(&session->encryption_key, encryption_key, KEY_SIZE_128,
ENCRYPTION_KEY, ENCRYPTION_KEY_ENCRYPT, KEY_SIZE_128);
if (result != OEMCrypto_SUCCESS) {
FreeKey(&session->mac_key_server);
FreeKey(&session->mac_key_client);
}
return result;
}
OEMCryptoResult DeriveMacAndEncryptionKeysFromCryptoKey(
OEMCryptoSession* session, const CryptoKey* master_key,
const uint8_t* mac_key_context, uint32_t mac_key_context_length,
const uint8_t* enc_key_context, uint32_t enc_key_context_length) {
return DeriveMacAndEncryptionKeys(
session, master_key, mac_key_context, mac_key_context_length,
enc_key_context, enc_key_context_length, GENERIC_DERIVE_KEY_TYPE);
}
OEMCryptoResult DeriveMacAndEncryptionKeysFromDeviceKey(
OEMCryptoSession* 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) {
return DeriveMacAndEncryptionKeys(
session, NULL, mac_key_context, mac_key_context_length, enc_key_context,
enc_key_context_length, DEVICE_KEY_DERIVE_KEY_TYPE);
}
OEMCryptoResult DeriveMacAndEncryptionKeysFromKeybox(
OEMCryptoSession* 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 (GetProvisioningMethod() != OEMCrypto_Keybox) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoResult result = ValidateKeybox();
if (result != OEMCrypto_SUCCESS) return result;
return DeriveMacAndEncryptionKeys(
session, NULL, mac_key_context, mac_key_context_length, enc_key_context,
enc_key_context_length, KEYBOX_DERIVE_KEY_TYPE);
}
OEMCryptoResult VerifySignatureWithMacKeyServer(OEMCryptoSession* session,
const uint8_t* message,
uint32_t message_length,
const uint8_t* signature) {
ASSERT(session != NULL && message != NULL && message_length != 0 &&
signature != NULL,
"Parameters are NULL or 0");
if (!CheckKey(session->mac_key_server, MAC_KEY_SERVER,
MAC_KEY_SERVER_VERIFY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
OEMCryptoResult result =
HMAC_SHA256(session->mac_key_server->key_handle, message, message_length,
computed_signature);
if (result != OEMCrypto_SUCCESS) return result;
if (memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH) != 0) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
return OEMCrypto_SUCCESS;
}
static OEMCryptoResult CheckStatusOnline(OEMCryptoSession* session,
uint32_t nonce, uint32_t control) {
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (!(control & CONTROL_NONCE_ENABLED)) {
/* TODO(b/154764983): fix this. */
/* Server error. Continue, and assume nonce required. */
LOGE("Server error: nonce not enabled.");
}
if (!CheckNonce(&session->nonce_table, nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
switch (session->usage_entry_status) {
default: /* Invalid status. */
case SESSION_HAS_NO_ENTRY:
return OEMCrypto_ERROR_INVALID_CONTEXT;
case SESSION_HAS_LOADED_ENTRY:
/* Cannot reload usage entry for online license. */
return OEMCrypto_ERROR_INVALID_CONTEXT;
case SESSION_HAS_NEW_ENTRY:
return OEMCrypto_SUCCESS;
}
}
static OEMCryptoResult CheckStatusOffline(OEMCryptoSession* session,
uint32_t nonce, uint32_t control) {
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (!(control & CONTROL_NONCE_ENABLED)) {
/* TODO(b/154764983): fix this. */
/* Server error. Continue, and assume nonce required. */
LOGE("Server error: nonce not enabled.");
}
switch (session->usage_entry_status) {
default: /* Invalid status. */
case SESSION_HAS_NO_ENTRY:
return OEMCrypto_ERROR_INVALID_CONTEXT;
case SESSION_HAS_LOADED_ENTRY:
/* We do not check the nonce. Instead, LoadKeys will verify the pst and
* mac keys match. */
return OEMCrypto_SUCCESS;
case SESSION_HAS_NEW_ENTRY:
if (!CheckNonce(&session->nonce_table, nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
return OEMCrypto_SUCCESS;
}
}
static OEMCryptoResult check_nonce_or_entry(OEMCryptoSession* session,
KeyControlBlock key_control_block) {
/* Note: we only check the nonce if the bit is enabled. */
uint8_t replay_bit =
(uint8_t)(key_control_block.control_bits & CONTROL_REPLAY_MASK);
switch (replay_bits) {
case CONTROL_NONCE_REQUIRED:
/* Online license. Nonce always required. */
return CheckStatusOnline(session, key_control_block.nonce,
key_control_block.control_bits);
break;
case CONTROL_NONCE_OR_ENTRY:
/* Offline license. Nonce required on first load. */
return CheckStatusOffline(session, key_control_block.nonce,
key_control_block.control_bits);
break;
default:
if ((key_control_block.control_bits & CONTROL_NONCE_ENABLED) &&
!CheckNonce(&session->nonce_table, key_control_block.nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
return OEMCrypto_SUCCESS;
}
}
OEMCryptoResult InstallKey(OEMCryptoSession* session, const uint8_t* key_id,
uint32_t key_id_length, const uint8_t* key_data,
uint32_t key_data_length, const uint8_t* key_data_iv,
const uint8_t* key_control,
const uint8_t* key_control_iv) {
ASSERT(session != NULL && key_id != NULL && key_id_length != 0 &&
key_data != NULL && key_data_length != 0 && key_data_iv != NULL &&
key_control != NULL && key_control_iv != NULL,
"Parameters are NULL or 0");
uint8_t raw_key[KEY_SIZE_256];
uint8_t raw_key_control[KEY_CONTROL_SIZE];
CryptoKey** current_key_ptr;
uint32_t current_key_index = 0;
CryptoKeyType key_type;
CryptoKeyOperation key_operation;
if (session->license_type == OEMCrypto_ContentLicense) {
if (key_data_length != KEY_SIZE_128 && key_data_length != KEY_SIZE_256) {
/* Generic crypto allows both key sizes. */
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
current_key_index = session->num_content_keys;
if (current_key_index == CONTENT_KEYS_PER_SESSION) {
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
current_key_ptr = &session->content_keys[current_key_index];
key_type = CONTENT_KEY;
key_operation = CONTENT_KEY_DECRYPT;
} else {
if (key_data_length != KEY_SIZE_256) return OEMCrypto_ERROR_INVALID_CONTEXT;
current_key_index = session->num_entitlement_keys;
if (current_key_index == ENTITLEMENT_KEYS_PER_SESSION) {
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
current_key_ptr = &session->entitlement_keys[current_key_index];
key_type = ENTITLEMENT_KEY;
key_operation = ENTITLEMENT_KEY_DECRYPT;
}
if (session->encryption_key != NULL) {
session->encryption_key->key_operation = ENCRYPTION_KEY_DECRYPT;
}
if (!CheckKey(session->encryption_key, ENCRYPTION_KEY,
ENCRYPTION_KEY_DECRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = AESCBCDecrypt(
session->encryption_key->key_handle, session->encryption_key->key_size,
key_data, key_data_length, key_data_iv, raw_key);
if (result != OEMCrypto_SUCCESS) return result;
result = CreateKey(current_key_ptr, raw_key, key_data_length, key_type,
key_operation, key_data_length);
if (result != OEMCrypto_SUCCESS) return result;
CryptoKey* current_key = *current_key_ptr;
memcpy(current_key->key_id, key_id, key_id_length);
current_key->key_id_size = key_id_length;
current_key->session_key_index = current_key_index;
if (!CheckKey(current_key, key_type, key_operation)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* We use the first 16 bytes regardless of the license type to decrypt the key
control. */
result = AESCBCDecrypt(current_key->key_handle, KEY_SIZE_128, key_control,
KEY_CONTROL_SIZE, key_control_iv, raw_key_control);
if (result != OEMCrypto_SUCCESS) goto cleanup;
current_key->key_control_block = ParseKeyControlBlock(raw_key_control);
if (!(current_key->key_control_block.valid)) {
result = OEMCrypto_ERROR_INVALID_CONTEXT;
goto cleanup;
}
if ((current_key->key_control_block.control_bits &
CONTROL_REQUIRE_ANTI_ROLLBACK_HARDWARE) &&
!IsAntiRollbackHWPresent()) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
uint8_t minimum_patch_level = (current_key->key_control_block.control_bits &
CONTROL_SECURITY_PATCH_LEVEL_MASK) >>
CONTROL_SECURITY_PATCH_LEVEL_SHIFT;
if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup;
}
result = check_nonce_or_entry(session, current_key->key_control_block);
if (result != OEMCrypto_SUCCESS) goto cleanup;
if (current_key->key_control_block.control_bits &
CONTROL_SRM_VERSION_REQUIRED) {
if (!session->valid_srm_version) {
/* Require local display only. */
current_key->key_control_block.control_bits |= CONTROL_HDCP_VERSION_MASK;
}
}
if (session->license_type == OEMCrypto_ContentLicense) {
current_key->is_entitled_content_key = false;
} else {
/* Entitlement keys will have entitled content keys loaded in later. */
current_key->has_entitled_content_key = false;
}
cleanup:
if (result != OEMCrypto_SUCCESS) FreeKey(current_key_ptr);
return result;
}
OEMCryptoResult UpdateMacKeys(OEMCryptoSession* session,
const uint8_t* enc_mac_keys,
const uint8_t* mac_keys_iv) {
ASSERT(session != NULL && enc_mac_keys != NULL && mac_keys_iv != NULL,
"Parameters are NULL");
uint8_t raw_mac_keys[2 * MAC_KEY_SIZE];
if (!CheckKey(session->encryption_key, ENCRYPTION_KEY,
ENCRYPTION_KEY_DECRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = AESCBCDecrypt(
session->encryption_key->key_handle, session->encryption_key->key_size,
enc_mac_keys, 2 * MAC_KEY_SIZE, mac_keys_iv, raw_mac_keys);
if (result != OEMCrypto_SUCCESS) return result;
OEMCryptoResult free_mac_key_server_result =
FreeKey(&session->mac_key_server);
OEMCryptoResult free_mac_key_client_result =
FreeKey(&session->mac_key_client);
if (free_mac_key_server_result != OEMCrypto_SUCCESS) {
return free_mac_key_server_result;
}
if (free_mac_key_client_result != OEMCrypto_SUCCESS) {
return free_mac_key_client_result;
}
result = CreateKey(&session->mac_key_server, raw_mac_keys, MAC_KEY_SIZE,
MAC_KEY_SERVER, MAC_KEY_SERVER_VERIFY, MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
result = CreateKey(&session->mac_key_client, raw_mac_keys + MAC_KEY_SIZE,
MAC_KEY_SIZE, MAC_KEY_CLIENT, MAC_KEY_CLIENT_SIGN,
MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) FreeKey(&session->mac_key_server);
return result;
}
OEMCryptoResult RefreshKey(OEMCryptoSession* session, const uint8_t* key_id,
uint32_t key_id_length, const uint8_t* key_control,
const uint8_t* key_control_iv) {
ASSERT(session != NULL && key_control != NULL,
"session or key_control is NULL");
KeyControlBlock key_control_block;
if (key_id == NULL) {
key_control_block = ParseKeyControlBlock(key_control);
if (!key_control_block.valid) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if ((key_control_block.control_bits & CONTROL_NONCE_ENABLED) &&
!CheckNonce(&session->nonce_table, key_control_block.nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
UpdateDurationForAllKeys(session, key_control_block);
return OEMCrypto_SUCCESS;
}
bool is_content_key = session->license_type == OEMCrypto_ContentLicense;
CryptoKey* key =
FindKeyFromTable(session, is_content_key, key_id, key_id_length);
if (key == NULL) return OEMCrypto_ERROR_NO_CONTENT_KEY;
uint8_t raw_key_control[KEY_CONTROL_SIZE];
if (key_control_iv != NULL) {
CryptoKeyType key_type = UNKNOWN_KEY_TYPE;
CryptoKeyOperation key_operation = UNKNOWN_KEY_OPERATION;
if (is_content_key) {
key_type = CONTENT_KEY;
key_operation = CONTENT_KEY_DECRYPT;
} else {
key_type = ENTITLEMENT_KEY;
key_operation = ENTITLEMENT_KEY_DECRYPT;
}
if (!CheckKey(key, key_type, key_operation)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Decrypt using only the first 128 bits of the key. */
OEMCryptoResult result =
AESCBCDecrypt(key->key_handle, KEY_SIZE_128, key_control,
KEY_CONTROL_SIZE, key_control_iv, raw_key_control);
if (result != OEMCrypto_SUCCESS) return result;
} else {
memcpy(raw_key_control, key_control, KEY_CONTROL_SIZE);
}
key_control_block = ParseKeyControlBlock(raw_key_control);
if (!key_control_block.valid) return OEMCrypto_ERROR_INVALID_CONTEXT;
if ((key_control_block.control_bits & CONTROL_NONCE_ENABLED) &&
!CheckNonce(&session->nonce_table, key_control_block.nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
key->key_control_block.duration = key_control_block.duration;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult CheckCurrentContentKeyUsage(const OEMCryptoSession* session,
uint32_t use_type,
OEMCryptoBufferType buffer_type) {
ASSERT(session != NULL && (buffer_type == OEMCrypto_BufferType_Clear ||
buffer_type == OEMCrypto_BufferType_Secure),
"session is NULL or invalid buffer_type");
if (session->current_content_key_index >= session->num_content_keys) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
CryptoKey* current_content_key =
session->content_keys[session->current_content_key_index];
if (!CheckKey(current_content_key, CONTENT_KEY, CONTENT_KEY_DECRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
KeyControlBlock control = current_content_key->key_control_block;
if (current_content_key->is_entitled_content_key) {
if (current_content_key->entitlement_key_index >=
session->num_entitlement_keys) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
CryptoKey* entitlement_key =
session->entitlement_keys[current_content_key->entitlement_key_index];
if (!CheckKey(entitlement_key, ENTITLEMENT_KEY, ENTITLEMENT_KEY_DECRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
control = entitlement_key->key_control_block;
}
if (use_type && (!(control.control_bits & use_type))) {
/* Could not use this key for the given use type. */
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoResult result;
if (control.control_bits & CONTROL_DATA_PATH_SECURE) {
if (!IsClosedPlatform() && buffer_type == OEMCrypto_BufferType_Clear) {
return OEMCrypto_ERROR_DECRYPT_FAILED;
}
}
switch (session->usage_entry_status) {
case SESSION_HAS_NO_ENTRY:
/* No entry, don't do anything. */
break;
case SESSION_HAS_DEACTIVATED_ENTRY:
return OEMCrypto_ERROR_KEY_EXPIRED;
case SESSION_HAS_NEW_ENTRY:
case SESSION_HAS_LOADED_ENTRY:
/* TODO: this is not very efficient to call on every decrypt. This will be
* more optimized when we mix with the ODK library. */
result = UpdateLastPlaybackTime(session);
if (result != OEMCrypto_SUCCESS) return result;
break;
default:
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (control.duration > 0) {
uint64_t time_elapsed = 0;
result = CurrentTimer(session, &time_elapsed);
if (result != OEMCrypto_SUCCESS) return result;
if (control.duration < time_elapsed) {
return OEMCrypto_ERROR_KEY_EXPIRED;
}
}
OEMCrypto_HDCP_Capability capability = CurrentHDCPCapability();
if (capability != HDCP_NO_DIGITAL_OUTPUT &&
(control.control_bits & CONTROL_HDCP_REQUIRED)) {
/* Check to see if HDCP requirements are satisfied. */
uint8_t required_hdcp =
(control.control_bits & CONTROL_HDCP_VERSION_MASK) >>
CONTROL_HDCP_VERSION_SHIFT;
if (required_hdcp > capability || capability == HDCP_NONE) {
return OEMCrypto_ERROR_INSUFFICIENT_HDCP;
}
}
/* We can't control analog output if the output is clear. Similarly, if
analog is not disabled, we should fail. */
if (control.control_bits & CONTROL_DISABLE_ANALOG_OUTPUT) {
if (buffer_type == OEMCrypto_BufferType_Clear ||
(IsAnalogDisplayActive() && !DisableAnalogDisplay())) {
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
}
/* If CGMS-A is active, we must turn off analog output. */
if (control.control_bits & CONTROL_CGMS_MASK) {
if (buffer_type == OEMCrypto_BufferType_Clear) {
/* Similar to the above, we can't control CGMS if output is clear. */
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
if (!IsCGMS_AActive() && IsAnalogDisplayActive() &&
!DisableAnalogDisplay()) {
/* CGMS must be active if analog output is active. */
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult DecryptCBC(const OEMCryptoSession* session,
const uint8_t* initial_iv,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data,
uint32_t cipher_data_length, uint8_t* clear_data) {
ASSERT(session != NULL && initial_iv != NULL && pattern != NULL &&
cipher_data != NULL && cipher_data_length != 0 &&
clear_data != NULL,
"Parameters are NULL or 0");
if (session->current_content_key_index >= session->num_content_keys) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
CryptoKey* current_content_key =
session->content_keys[session->current_content_key_index];
if (!CheckKey(current_content_key, CONTENT_KEY, CONTENT_KEY_DECRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t iv[AES_BLOCK_SIZE];
uint8_t next_iv[AES_BLOCK_SIZE];
memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE);
uint32_t l = 0;
uint32_t pattern_offset = pattern->offset;
while (l < cipher_data_length) {
uint32_t size = cipher_data_length - l;
if (size > AES_BLOCK_SIZE) size = AES_BLOCK_SIZE;
uint32_t pattern_length = pattern->encrypt + pattern->skip;
bool skip_block =
(pattern_offset >= pattern->encrypt) && (pattern_length > 0);
if (pattern_length > 0) {
pattern_offset = (pattern_offset + 1) % pattern_length;
}
if (skip_block || (size < AES_BLOCK_SIZE)) {
memmove(&clear_data[l], &cipher_data[l], size);
} else {
uint8_t aes_output[AES_BLOCK_SIZE];
/* Save the iv for the next block, in case cipher_data is in the same
buffer as clear_data. */
memcpy(next_iv, &cipher_data[l], AES_BLOCK_SIZE);
OEMCryptoResult result = AESDecrypt(current_content_key->key_handle,
&cipher_data[l], aes_output);
if (result != OEMCrypto_SUCCESS) return result;
for (uint32_t n = 0; n < AES_BLOCK_SIZE; n++) {
clear_data[l + n] = aes_output[n] ^ iv[n];
}
memcpy(iv, next_iv, AES_BLOCK_SIZE);
}
l += size;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult DecryptCTR(const OEMCryptoSession* session,
const uint8_t* initial_iv, uint32_t block_offset,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
const uint8_t* cipher_data,
uint32_t cipher_data_length, uint8_t* clear_data) {
ASSERT(session != NULL && initial_iv != NULL && pattern != NULL &&
cipher_data != NULL && cipher_data_length != 0 &&
clear_data != NULL,
"Parameters are NULL or 0");
if (session->current_content_key_index >= session->num_content_keys) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
CryptoKey* current_content_key =
session->content_keys[session->current_content_key_index];
if (!CheckKey(current_content_key, CONTENT_KEY, CONTENT_KEY_DECRYPT)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t iv[AES_BLOCK_SIZE];
memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE);
uint32_t l = 0;
uint32_t pattern_offset = pattern->offset;
while (l < cipher_data_length) {
uint32_t size = cipher_data_length - l;
if (size > AES_BLOCK_SIZE - block_offset)
size = AES_BLOCK_SIZE - block_offset;
uint32_t pattern_length = pattern->encrypt + pattern->skip;
bool skip_block =
(pattern_offset >= pattern->encrypt) && (pattern_length > 0);
if (pattern_length > 0) {
pattern_offset = (pattern_offset + 1) % pattern_length;
}
if (skip_block) {
memmove(&clear_data[l], &cipher_data[l], size);
} else {
uint8_t aes_output[AES_BLOCK_SIZE];
AESEncrypt(current_content_key->key_handle, iv, aes_output);
for (uint32_t n = 0; n < size; n++) {
clear_data[l + n] = aes_output[n + block_offset] ^ cipher_data[l + n];
}
/* Increment the lower 8 bytes of the iv only. */
for (int n = 15; n > 7; n--) {
if (++iv[n] != 0) break;
};
}
l += size;
block_offset = 0;
}
return OEMCrypto_SUCCESS;
}