147 lines
6.1 KiB
C
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;
|
|
}
|