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

147 lines
6.1 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_key.h"
#include "string.h"
#include "assert_interface.h"
#include "oemcrypto_endianness.h"
/* Mask combined with key pointers for the CryptoKey struct to ensure that a key
is well-formed. */
static uint64_t COOKIE_MASK = 0x1bc15e9624a069ffULL;
static uint64_t calculate_cookie(CryptoKey* key) {
ASSERT(key != NULL, "key is NULL");
ASSERT(key->key_handle != NULL, "key_handle is NULL");
return COOKIE_MASK ^ (uint64_t)((uintptr_t)key) ^
(uint64_t)((uintptr_t)key->key_handle);
}
OEMCryptoResult InitializeCryptoKey(CryptoKey* key,
const uint8_t* serialized_bytes,
uint32_t serialized_bytes_length,
CryptoKeyType key_type,
CryptoKeyOperation key_operation,
CryptoKeySize key_size) {
ASSERT(key != NULL, "key is NULL");
ASSERT(serialized_bytes != NULL, "serialized_bytes is NULL");
ASSERT(serialized_bytes_length > 0, "serialized_bytes_length is 0");
ASSERT(key_type != UNKNOWN_KEY_TYPE, "key_type is UNKNOWN_KEY_TYPE");
/* Note that for some keys, we can only know the size from context, since
serialized_bytes_length corresponds to the size of the serialized key, not
the key itself necessarily. So, it's okay for key_size to be unknown.
Similarly, sometimes we want to just load the key without using it, so the
operation is allowed to be unknown. */
OEMCryptoResult result = CreateKeyHandle(
serialized_bytes, serialized_bytes_length, key_type, &(key->key_handle));
if (result != OEMCrypto_SUCCESS) return result;
key->cookie = calculate_cookie(key);
memset(key->key_id, 0, KEY_ID_MAX_SIZE);
key->key_id_size = 0;
key->key_type = key_type;
key->key_operation = key_operation;
key->key_size = key_size;
key->allowed_schemes = 0;
memset(&key->key_control_block, 0, sizeof(key->key_control_block));
key->entitled_content_key_index = 0;
key->has_entitled_content_key = false;
key->entitlement_key_index = 0;
key->is_entitled_content_key = false;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult InitializeCryptoKeyFromWrappedKey(
CryptoKey* key, const uint8_t* wrapped_key, uint32_t wrapped_key_length,
CryptoKeyType key_type, CryptoKeyOperation key_operation,
CryptoKeySize key_size) {
ASSERT(key != NULL, "key is NULL");
ASSERT(wrapped_key != NULL, "wrapped_key is NULL");
ASSERT(wrapped_key_length > 0, "wrapped_key_length is 0");
ASSERT(key_type != UNKNOWN_KEY_TYPE, "key_type is UNKNOWN_KEY_TYPE");
/* Note that for some keys, we can only know the size from context, since
wrapped_key_length corresponds to the size of the serialized key, not
the key itself necessarily. So, it's okay for key_size to be unknown.
Similarly, sometimes we want to just load the key without using it, so the
operation is allowed to be unknown. */
OEMCryptoResult result = UnwrapIntoKeyHandle(wrapped_key, wrapped_key_length,
key_type, &(key->key_handle));
if (result != OEMCrypto_SUCCESS) return result;
key->cookie = calculate_cookie(key);
memset(key->key_id, 0, KEY_ID_MAX_SIZE);
key->key_id_size = 0;
key->key_type = key_type;
key->key_operation = key_operation;
key->key_size = key_size;
key->allowed_schemes = 0;
memset(&key->key_control_block, 0, sizeof(key->key_control_block));
key->entitled_content_key_index = 0;
key->has_entitled_content_key = false;
key->entitlement_key_index = 0;
key->is_entitled_content_key = false;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult WrapCryptoKey(CryptoKey* key, uint8_t* buffer,
size_t buffer_length) {
ASSERT(key != NULL, "key is NULL");
ASSERT(buffer != NULL, "wrapped_key is NULL");
ASSERT(buffer_length > 0, "wrapped_key_length is 0");
return WrapKey(buffer, buffer_length, key->key_type, key->key_handle);
}
OEMCryptoResult FreeCryptoKey(CryptoKey* key) {
ASSERT(key != NULL, "key is NULL");
if (key->key_handle != NULL) {
OEMCryptoResult result = FreeKeyHandle(key->key_handle);
if (result != OEMCrypto_SUCCESS) {
return result;
}
}
memset(key, 0, sizeof(CryptoKey));
return OEMCrypto_SUCCESS;
}
bool CheckKey(CryptoKey* key, CryptoKeyType key_type,
CryptoKeyOperation key_operation) {
return key != NULL && key->key_type == key_type &&
key->key_operation == key_operation &&
calculate_cookie(key) == key->cookie;
}
/* This extracts 4 bytes in network byte order to a 32 bit integer in host byte
order. */
static uint32_t extract_field_from_KCB(const uint8_t* kcb, uint8_t index) {
ASSERT(kcb != NULL && index <= 3,
"Key control block is NULL or index is invalid");
uint8_t byte_index = index * 4;
return NetworkToHostU32(*(const uint32_t*)(&kcb[byte_index]));
}
KeyControlBlock ParseKeyControlBlock(const uint8_t* kcb) {
ASSERT(kcb != NULL, "kcb is NULL");
KeyControlBlock key_control_block;
memset(&key_control_block, 0, sizeof(key_control_block));
memcpy(key_control_block.verification, kcb, 4);
key_control_block.duration = extract_field_from_KCB(kcb, 1);
key_control_block.nonce = extract_field_from_KCB(kcb, 2);
key_control_block.control_bits = extract_field_from_KCB(kcb, 3);
const char* verification = key_control_block.verification;
if (memcmp(verification, "kctl", 4) && /* original verification */
memcmp(verification, "kc09", 4) && /* add in version 9 api */
memcmp(verification, "kc10", 4) && /* add in version 10 api */
memcmp(verification, "kc11", 4) && /* add in version 11 api */
memcmp(verification, "kc12", 4) && /* add in version 12 api */
memcmp(verification, "kc13", 4) && /* add in version 13 api */
memcmp(verification, "kc14", 4) && /* add in version 14 api */
memcmp(verification, "kc15", 4)) { /* add in version 15 api */
key_control_block.valid = false;
} else {
key_control_block.valid = true;
}
return key_control_block;
}