First Publicly Shared Version of ODKiTEE v15

This commit is contained in:
John W. Bruce
2020-07-24 12:03:58 -07:00
commit eaa8984c06
56 changed files with 21391 additions and 0 deletions

31
README.md Normal file
View File

@@ -0,0 +1,31 @@
# OEMCrypto Development Kit For Trusted Execution Environments
ODKiTEE is the Widevine hardened reference implementation of OEMCrypto suitable
to run in a TEE. It is written in C with a thin porting interface to make it
easier to port to various trusted environments.
## Current Status
This very early preview release contains an early version of the ODKiTEE source
code. It contains only the following:
1) Code for an IPC layer that implements the OEMCrypto API functions, translates
the calls into serialized objects, deserializes the objects inside the TEE,
and invokes the appropriate TA function
2) Code for a Trusted Application that implements the logic of OEMCrypto
No build system is included. No implementation of the porting layers for working
with different TEE OSes and chip hardware is included.
In addition, the code herein has the following known limitations:
1) The usage table code does not yet encrypt the usage table information.
2) The code is only sporadically and opportunistically hardened.
3) Some minor functionality is still missing, though it should all be marked
with TODO comments.
If you have received this code, Widevine is looking for your feedback! Please
let us know where it can be improved. Don't hesitate to call out things you
think we already know, particularly as regards hardening. We want to know
whether the places we see room for improvement are the same as the ones where
you do.

2031
oemcrypto_ta/oemcrypto.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_
#define OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_
#ifdef OEMCRYPTO_TA_TEST_ONLY
/* Only used for testing. This is so the TA can directly build with the
unittests. */
/* clang-format off */
#define OEMCrypto_Initialize _oecc01
#define OEMCrypto_Terminate _oecc02
#define OEMCrypto_InstallKeybox _oecc03
#define OEMCrypto_InstallRootKeyCertificate _oecc03
#define OEMCrypto_InstallKeyboxOrOEMCert _oecc03
#define OEMCrypto_GetKeyData _oecc04
#define OEMCrypto_IsKeyboxValid _oecc05
#define OEMCrypto_IsRootKeyCertificateValid _oecc05
#define OEMCrypto_IsKeyboxOrOEMCertValid _oecc05
#define OEMCrypto_GetRandom _oecc06
#define OEMCrypto_GetDeviceID _oecc07
#define OEMCrypto_WrapKeybox _oecc08
#define OEMCrypto_WrapRootKeyCertificate _oecc08
#define OEMCrypto_WrapKeyboxOrOEMCert _oecc08
#define OEMCrypto_OpenSession _oecc09
#define OEMCrypto_CloseSession _oecc10
#define OEMCrypto_DecryptCTR_V10 _oecc11
#define OEMCrypto_GenerateDerivedKeys _oecc12
#define OEMCrypto_GenerateSignature _oecc13
#define OEMCrypto_GenerateNonce _oecc14
#define OEMCrypto_LoadKeys_V8 _oecc15
#define OEMCrypto_RefreshKeys_V14 _oecc16
#define OEMCrypto_SelectKey_V13 _oecc17
#define OEMCrypto_RewrapDeviceRSAKey _oecc18
#define OEMCrypto_LoadDeviceRSAKey _oecc19
#define OEMCrypto_GenerateRSASignature_V8 _oecc20
#define OEMCrypto_DeriveKeysFromSessionKey _oecc21
#define OEMCrypto_APIVersion _oecc22
#define OEMCrypto_SecurityLevel _oecc23
#define OEMCrypto_Generic_Encrypt _oecc24
#define OEMCrypto_Generic_Decrypt _oecc25
#define OEMCrypto_Generic_Sign _oecc26
#define OEMCrypto_Generic_Verify _oecc27
#define OEMCrypto_GetHDCPCapability_V9 _oecc28
#define OEMCrypto_SupportsUsageTable _oecc29
#define OEMCrypto_UpdateUsageTable _oecc30
#define OEMCrypto_DeactivateUsageEntry_V12 _oecc31
#define OEMCrypto_ReportUsage _oecc32
#define OEMCrypto_DeleteUsageEntry _oecc33
#define OEMCrypto_DeleteOldUsageTable _oecc34
#define OEMCrypto_LoadKeys_V9_or_V10 _oecc35
#define OEMCrypto_GenerateRSASignature _oecc36
#define OEMCrypto_GetMaxNumberOfSessions _oecc37
#define OEMCrypto_GetNumberOfOpenSessions _oecc38
#define OEMCrypto_IsAntiRollbackHwPresent _oecc39
#define OEMCrypto_CopyBuffer_V14 _oecc40
#define OEMCrypto_QueryKeyControl _oecc41
#define OEMCrypto_LoadTestKeybox_V13 _oecc42
#define OEMCrypto_ForceDeleteUsageEntry _oecc43
#define OEMCrypto_GetHDCPCapability _oecc44
#define OEMCrypto_LoadTestRSAKey _oecc45
#define OEMCrypto_Security_Patch_Level _oecc46
#define OEMCrypto_LoadKeys_V11_or_V12 _oecc47
#define OEMCrypto_DecryptCENC _oecc48
#define OEMCrypto_GetProvisioningMethod _oecc49
#define OEMCrypto_GetOEMPublicCertificate _oecc50
#define OEMCrypto_RewrapDeviceRSAKey30 _oecc51
#define OEMCrypto_SupportedCertificates _oecc52
#define OEMCrypto_IsSRMUpdateSupported _oecc53
#define OEMCrypto_GetCurrentSRMVersion _oecc54
#define OEMCrypto_LoadSRM _oecc55
#define OEMCrypto_LoadKeys_V13 _oecc56
#define OEMCrypto_RemoveSRM _oecc57
#define OEMCrypto_CreateUsageTableHeader _oecc61
#define OEMCrypto_LoadUsageTableHeader _oecc62
#define OEMCrypto_CreateNewUsageEntry _oecc63
#define OEMCrypto_LoadUsageEntry _oecc64
#define OEMCrypto_UpdateUsageEntry _oecc65
#define OEMCrypto_DeactivateUsageEntry _oecc66
#define OEMCrypto_ShrinkUsageTableHeader _oecc67
#define OEMCrypto_MoveEntry _oecc68
#define OEMCrypto_CopyOldUsageEntry _oecc69
#define OEMCrypto_CreateOldUsageEntry _oecc70
#define OEMCrypto_GetAnalogOutputFlags _oecc71
#define OEMCrypto_LoadTestKeybox _oecc78
#define OEMCrypto_LoadEntitledContentKeys_V14 _oecc79
#define OEMCrypto_SelectKey _oecc81
#define OEMCrypto_LoadKeys_V14 _oecc82
#define OEMCrypto_LoadKeys _oecc83
#define OEMCrypto_SetSandbox _oecc84
#define OEMCrypto_ResourceRatingTier _oecc85
#define OEMCrypto_SupportsDecryptHash _oecc86
#define OEMCrypto_InitializeDecryptHash _oecc87
#define OEMCrypto_SetDecryptHash _oecc88
#define OEMCrypto_GetHashErrorCode _oecc89
#define OEMCrypto_BuildInformation _oecc90
#define OEMCrypto_RefreshKeys _oecc91
#define OEMCrypto_LoadEntitledContentKeys _oecc92
#define OEMCrypto_CopyBuffer _oecc93
#else
#define OEMCrypto_Initialize _oecctee01
#define OEMCrypto_Terminate _oecctee02
#define OEMCrypto_InstallKeybox _oecctee03
#define OEMCrypto_InstallRootKeyCertificate _oecctee03
#define OEMCrypto_InstallKeyboxOrOEMCert _oecctee03
#define OEMCrypto_GetKeyData _oecctee04
#define OEMCrypto_IsKeyboxValid _oecctee05
#define OEMCrypto_IsRootKeyCertificateValid _oecctee05
#define OEMCrypto_IsKeyboxOrOEMCertValid _oecctee05
#define OEMCrypto_GetRandom _oecctee06
#define OEMCrypto_GetDeviceID _oecctee07
#define OEMCrypto_WrapKeybox _oecctee08
#define OEMCrypto_WrapRootKeyCertificate _oecctee08
#define OEMCrypto_WrapKeyboxOrOEMCert _oecctee08
#define OEMCrypto_OpenSession _oecctee09
#define OEMCrypto_CloseSession _oecctee10
#define OEMCrypto_DecryptCTR_V10 _oecctee11
#define OEMCrypto_GenerateDerivedKeys _oecctee12
#define OEMCrypto_GenerateSignature _oecctee13
#define OEMCrypto_GenerateNonce _oecctee14
#define OEMCrypto_LoadKeys_V8 _oecctee15
#define OEMCrypto_RefreshKeys_V14 _oecctee16
#define OEMCrypto_SelectKey_V13 _oecctee17
#define OEMCrypto_RewrapDeviceRSAKey _oecctee18
#define OEMCrypto_LoadDeviceRSAKey _oecctee19
#define OEMCrypto_GenerateRSASignature_V8 _oecctee20
#define OEMCrypto_DeriveKeysFromSessionKey _oecctee21
#define OEMCrypto_APIVersion _oecctee22
#define OEMCrypto_SecurityLevel _oecctee23
#define OEMCrypto_Generic_Encrypt _oecctee24
#define OEMCrypto_Generic_Decrypt _oecctee25
#define OEMCrypto_Generic_Sign _oecctee26
#define OEMCrypto_Generic_Verify _oecctee27
#define OEMCrypto_GetHDCPCapability_V9 _oecctee28
#define OEMCrypto_SupportsUsageTable _oecctee29
#define OEMCrypto_UpdateUsageTable _oecctee30
#define OEMCrypto_DeactivateUsageEntry_V12 _oecctee31
#define OEMCrypto_ReportUsage _oecctee32
#define OEMCrypto_DeleteUsageEntry _oecctee33
#define OEMCrypto_DeleteOldUsageTable _oecctee34
#define OEMCrypto_LoadKeys_V9_or_V10 _oecctee35
#define OEMCrypto_GenerateRSASignature _oecctee36
#define OEMCrypto_GetMaxNumberOfSessions _oecctee37
#define OEMCrypto_GetNumberOfOpenSessions _oecctee38
#define OEMCrypto_IsAntiRollbackHwPresent _oecctee39
#define OEMCrypto_CopyBuffer_V14 _oecctee40
#define OEMCrypto_QueryKeyControl _oecctee41
#define OEMCrypto_LoadTestKeybox_V13 _oecctee42
#define OEMCrypto_ForceDeleteUsageEntry _oecctee43
#define OEMCrypto_GetHDCPCapability _oecctee44
#define OEMCrypto_LoadTestRSAKey _oecctee45
#define OEMCrypto_Security_Patch_Level _oecctee46
#define OEMCrypto_LoadKeys_V11_or_V12 _oecctee47
#define OEMCrypto_DecryptCENC _oecctee48
#define OEMCrypto_GetProvisioningMethod _oecctee49
#define OEMCrypto_GetOEMPublicCertificate _oecctee50
#define OEMCrypto_RewrapDeviceRSAKey30 _oecctee51
#define OEMCrypto_SupportedCertificates _oecctee52
#define OEMCrypto_IsSRMUpdateSupported _oecctee53
#define OEMCrypto_GetCurrentSRMVersion _oecctee54
#define OEMCrypto_LoadSRM _oecctee55
#define OEMCrypto_LoadKeys_V13 _oecctee56
#define OEMCrypto_RemoveSRM _oecctee57
#define OEMCrypto_CreateUsageTableHeader _oecctee61
#define OEMCrypto_LoadUsageTableHeader _oecctee62
#define OEMCrypto_CreateNewUsageEntry _oecctee63
#define OEMCrypto_LoadUsageEntry _oecctee64
#define OEMCrypto_UpdateUsageEntry _oecctee65
#define OEMCrypto_DeactivateUsageEntry _oecctee66
#define OEMCrypto_ShrinkUsageTableHeader _oecctee67
#define OEMCrypto_MoveEntry _oecctee68
#define OEMCrypto_CopyOldUsageEntry _oecctee69
#define OEMCrypto_CreateOldUsageEntry _oecctee70
#define OEMCrypto_GetAnalogOutputFlags _oecctee71
#define OEMCrypto_LoadTestKeybox _oecctee78
#define OEMCrypto_LoadEntitledContentKeys_V14 _oecctee79
#define OEMCrypto_SelectKey _oecctee81
#define OEMCrypto_LoadKeys_V14 _oecctee82
#define OEMCrypto_LoadKeys _oecctee83
#define OEMCrypto_SetSandbox _oecctee84
#define OEMCrypto_ResourceRatingTier _oecctee85
#define OEMCrypto_SupportsDecryptHash _oecctee86
#define OEMCrypto_InitializeDecryptHash _oecctee87
#define OEMCrypto_SetDecryptHash _oecctee88
#define OEMCrypto_GetHashErrorCode _oecctee89
#define OEMCrypto_BuildInformation _oecctee90
#define OEMCrypto_RefreshKeys _oecctee91
#define OEMCrypto_LoadEntitledContentKeys _oecctee92
#define OEMCrypto_CopyBuffer _oecctee93
/* clang-format on */
#endif
#endif /* OEMCRYPTO_TA_OEMCRYPTO_API_MACROS_H_ */

View File

@@ -0,0 +1,26 @@
// Copyright 2020 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_endianness.h"
#include <stdint.h>
#include <string.h>
uint32_t HostToNetworkU32(uint32_t host) {
uint8_t bytes[] = {
(uint8_t)(host >> 24u),
(uint8_t)(host >> 16u),
(uint8_t)(host >> 8u),
(uint8_t)(host >> 0u),
};
uint32_t network;
memcpy(&network, bytes, sizeof(network));
return network;
}
uint32_t NetworkToHostU32(uint32_t network) {
// These functions are symmetrical on any byte-ordering used in practice.
return HostToNetworkU32(network);
}

View File

@@ -0,0 +1,17 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef OEMCRYPTO_ENDIANNESS_H_
#define OEMCRYPTO_ENDIANNESS_H_
#include <stdint.h>
// The functions in this header convert between network and host byte order for
// various basic integer types.
uint32_t HostToNetworkU32(uint32_t host);
uint32_t NetworkToHostU32(uint32_t network);
#endif // OEMCRYPTO_ENDIANNESS_H_

View File

@@ -0,0 +1,146 @@
/* 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;
}

View File

@@ -0,0 +1,94 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_KEY_H_
#define OEMCRYPTO_TA_OEMCRYPTO_KEY_H_
#include "crypto_interface.h"
#include "oemcrypto_key_types.h"
typedef struct KeyControlBlock {
bool valid;
char verification[4];
uint32_t duration;
uint32_t nonce;
uint32_t control_bits;
} KeyControlBlock;
typedef struct CryptoKey {
/* Index into the global key table. */
uint32_t key_table_index;
uint64_t cookie;
CryptoKeyType key_type;
CryptoKeyOperation key_operation;
/* key_handle is owned by the TEE. */
TEE_Key_Handle key_handle;
CryptoKeySize key_size;
uint32_t allowed_schemes; /* For DRM RSA keys only */
/* For entitlement or content keys only. */
uint8_t key_id[KEY_ID_MAX_SIZE];
uint8_t key_id_size;
KeyControlBlock key_control_block;
/* Index into either the content or entitlement key table in the session. */
uint32_t session_key_index;
/* For entitlement keys only. */
uint32_t entitled_content_key_index; /* Index of entitled content key that
this entitles. */
bool has_entitled_content_key;
/* For content keys only. */
uint32_t entitlement_key_index; /* Index of entitlement key that entitles this
key. */
bool is_entitled_content_key;
OEMCryptoCipherMode cipher_mode;
} CryptoKey;
/* Initializes the data fields in the |key| and sets |key|'s key_handle to
the key handle formed from |serialized_bytes| and |size|.
Returns the result of CreateKeyHandle if it fails and OEMCrypto_SUCCESS
otherwise.
|serialized_bytes_length| must be > 0 and |key_type| must be valid.
Caller retains ownership of all pointers and they must not be NULL. */
OEMCryptoResult InitializeCryptoKey(CryptoKey* key,
const uint8_t* serialized_bytes,
uint32_t serialized_bytes_length,
CryptoKeyType key_type,
CryptoKeyOperation key_operation,
CryptoKeySize key_size);
/* Initializes the data fields in the |key| and sets |key|'s key_handle to
the key handle formed from |wrapped_key| and |size|.
The data in |wrapped_key| was previously computed in WrapCryptoKey.
Returns the result of CreateKeyHandle if it fails and OEMCrypto_SUCCESS
otherwise.
|serialized_bytes_length| must be > 0 and |key_type| must be valid.
Caller retains ownership of all pointers and they must not be NULL. */
OEMCryptoResult InitializeCryptoKeyFromWrappedKey(
CryptoKey* key, const uint8_t* wrapped_key, uint32_t wrapped_key_length,
CryptoKeyType key_type, CryptoKeyOperation key_operation,
CryptoKeySize key_size);
/* Wraps the key data into a buffer that can be saved to the file system. The
wrapping must be device unique. Caller retains ownership of |key| and
|buffer| and they must not be NULL. Caller ensures that buffer_length is at
least as big as the wrapped key size specified in
oemcrypto_config_macros.h. */
OEMCryptoResult WrapCryptoKey(CryptoKey* key, uint8_t* buffer,
size_t buffer_length);
/* Frees the key handle associated with this key if it exists and then clears
the key. Returns the result of freeing the key handle.
Caller retains ownership of |key| and it must not be NULL. */
OEMCryptoResult FreeCryptoKey(CryptoKey* key);
/* Checks to make sure that |key| isn't NULL, its cookie is valid, its key_type
matches |key_type|, and its key operation matches |key_operation|. */
bool CheckKey(CryptoKey* key, CryptoKeyType key_type,
CryptoKeyOperation key_operation);
/* Parses the given |kcb| into a KeyControlBlock. If the verification fails, the
key control block is marked invalid. Returns the parsed KeyControlBlock.
Caller retains ownership of all pointers and they must not be NULL. */
KeyControlBlock ParseKeyControlBlock(const uint8_t* kcb);
#endif /* OEMCRYPTO_TA_OEMCRYPTO_KEY_H_ */

View File

@@ -0,0 +1,143 @@
/* 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_table.h"
#include "stdint.h"
#include "string.h"
#include "assert_interface.h"
#include "logging_interface.h"
static KeyTable key_table;
static bool key_table_initialized = false;
OEMCryptoResult InitializeKeyTable(void) {
ASSERT(MAX_NUMBER_OF_KEYS > 0, "MAX_NUMBER_OF_KEYS must be > 0");
ASSERT(MAX_NUMBER_OF_KEYS < UINT32_MAX - 1,
"MAX_NUMBER_OF_KEYS is too large");
if (key_table_initialized) {
return OEMCrypto_ERROR_INIT_FAILED;
}
key_table.size = MAX_NUMBER_OF_KEYS;
key_table.first_free_key = 0;
for (uint32_t i = 0; i < key_table.size; i++) {
key_table.next_free_key[i] = i + 1;
key_table.is_free[i] = true;
memset(&key_table.keys[i], 0, sizeof(CryptoKey));
}
key_table_initialized = true;
return OEMCrypto_SUCCESS;
}
uint32_t MaxNumberOfKeys(void) { return MAX_NUMBER_OF_KEYS; }
OEMCryptoResult NumberOfUsedKeys(uint32_t* num_used_keys) {
ASSERT(num_used_keys != NULL, "num_used_keys is NULL");
if (!key_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
for (uint32_t i = 0; i < key_table.size; i++) {
if (!key_table.is_free[i]) {
(*num_used_keys)++;
}
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult GrabKey(uint32_t* index) {
ASSERT(index != NULL, "index is NULL");
if (!key_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
if (key_table.first_free_key == MAX_NUMBER_OF_KEYS) {
return OEMCrypto_ERROR_TOO_MANY_KEYS;
}
*index = key_table.first_free_key;
key_table.first_free_key = key_table.next_free_key[*index];
key_table.is_free[*index] = false;
key_table.keys[*index].key_table_index = *index;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult GetKey(uint32_t index, CryptoKey** key) {
ASSERT(key != NULL, "key is NULL");
if (!key_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
if (index >= key_table.size || key_table.is_free[index]) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
*key = &key_table.keys[index];
return OEMCrypto_SUCCESS;
}
OEMCryptoResult CreateKey(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");
OEMCryptoResult result;
if (*key != NULL) {
result = FreeKey(key);
if (result != OEMCrypto_SUCCESS) return result;
}
uint32_t key_table_index = 0;
result = GrabKey(&key_table_index);
if (result != OEMCrypto_SUCCESS) return result;
result = GetKey(key_table_index, key);
if (result != OEMCrypto_SUCCESS) {
FreeKey(key);
return result;
}
result = InitializeCryptoKey(*key, serialized_bytes, serialized_bytes_length,
key_type, key_operation, key_size);
if (result != OEMCrypto_SUCCESS) {
FreeKey(key);
return result;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult FreeKey(CryptoKey** key) {
ASSERT(key != NULL, "key is NULL");
if (*key == NULL) return OEMCrypto_SUCCESS;
uint32_t index = (*key)->key_table_index;
if (!key_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
if (index >= key_table.size || key_table.is_free[index]) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
OEMCryptoResult result = FreeCryptoKey(&key_table.keys[index]);
if (result != OEMCrypto_SUCCESS) {
return result;
}
key_table.next_free_key[index] = key_table.first_free_key;
key_table.is_free[index] = true;
key_table.first_free_key = index;
*key = NULL;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult TerminateKeyTable(void) {
if (!key_table_initialized) {
return OEMCrypto_ERROR_TERMINATE_FAILED;
}
OEMCryptoResult result = OEMCrypto_SUCCESS;
for (uint32_t i = 0; i < key_table.size; i++) {
if (!key_table.is_free[i]) {
result = OEMCrypto_ERROR_TERMINATE_FAILED;
/* Attempt to free the key. */
CryptoKey* key = &key_table.keys[i];
OEMCryptoResult free_result = FreeKey(&key);
if (free_result != OEMCrypto_SUCCESS) {
LOGE("Could not free key at index %d with error: %d", i, free_result);
}
}
}
key_table_initialized = false;
return result;
}

View File

@@ -0,0 +1,79 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_KEY_TABLE_H_
#define OEMCRYPTO_TA_OEMCRYPTO_KEY_TABLE_H_
#include "oemcrypto_config_interface.h"
#include "oemcrypto_key.h"
typedef struct KeyTable {
CryptoKey keys[MAX_NUMBER_OF_KEYS];
uint32_t size;
uint32_t first_free_key;
uint32_t next_free_key[MAX_NUMBER_OF_KEYS];
bool is_free[MAX_NUMBER_OF_KEYS];
} KeyTable;
/* Initializes the key table so the session can grab keys at a late point.
Returns OEMCrypto_ERROR_INIT_FAILED if the key table has already been
initialized and OEMCrypto_SUCCESS otherwise. */
OEMCryptoResult InitializeKeyTable(void);
/* Gets the max number of keys. */
uint32_t MaxNumberOfKeys(void);
/* Gets the number of currently used keys. Returns
OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table has not been initialized
and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of |num_used_keys| and it must not be NULL. */
OEMCryptoResult NumberOfUsedKeys(uint32_t* num_used_keys);
/* Attempts to grab an unused entry in the key table and set *|index| to the
entry position. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table
has not been initialized and OEMCrypto_ERROR_TOO_MANY_KEYS if there are no
keys left to grab. Returns OEMCrypto_SUCCESS otherwise.
Caller retains ownership of |index| and it must not be NULL. */
OEMCryptoResult GrabKey(uint32_t* index);
/* Sets key to the key at |index| in the key table if it is free. Returns
OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table has not been initialized
and OEMCrypto_ERROR_INVALID_CONTEXT if the key has not been grabbed or if the
index is invalid. Returns OEMCrypto_SUCCESS otherwise.
If successful, caller gains ownership of *|key| and |key| must not be NULL.
*/
OEMCryptoResult GetKey(uint32_t index, CryptoKey** key);
/* Grabs, gets, and initializes a CryptoKey using |serialized_bytes| and
GrabKey, GetKey, and InitializeCryptoKey and sets the result in *|key|.
If |key| points to an existing key, this method tries to free it before
continuing. If there is an error in generating the new key, this method will
free it before returning and set *|key| to NULL.
If successful, caller gains ownership of *|key| and it must not be NULL. */
OEMCryptoResult CreateKey(CryptoKey** key, const uint8_t* serialized_bytes,
uint32_t serialized_bytes_length,
CryptoKeyType key_type,
CryptoKeyOperation key_operation,
CryptoKeySize key_size);
/* Given a pointer to a CryptoKey*, attempts to free the CryptoKey it points to
if it exists, and then sets the pointer to the CryptoKey to NULL.
Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the key table has not been
initialized, OEMCrypto_ERROR_INVALID_CONTEXT if the non-null CryptoKey has
not been grabbed or if its index is invalid. Returns the result of freeing
the CryptoKey otherwise.
If there is an existing error in the caller, in which case this is likely
used for cleanup, that error will be returned and the result of this shall be
ignored.
Caller retains ownership of *|key| but **|key| will be destroyed if *|key|
is not NULL. */
OEMCryptoResult FreeKey(CryptoKey** key);
/* Clears and cleans up the key table. The key table must be reinitialized to be
used. Returns OEMCrypto_ERROR_TERMINATE_FAILED if the table has not been
initialized or if there are any active keys still. Returns OEMCrypto_SUCCESS
otherwise. */
OEMCryptoResult TerminateKeyTable(void);
#endif /* OEMCRYPTO_TA_OEMCRYPTO_KEY_TABLE_H_ */

View File

@@ -0,0 +1,125 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_KEY_TYPES_H_
#define OEMCRYPTO_TA_OEMCRYPTO_KEY_TYPES_H_
#include "stdint.h"
/* The type of the cryptographic key. Both used for validation and
identification of a key blob. Note that root of trusts are not included in
the scope of this interface. They shouldn't be represented by a CryptoKey. */
typedef enum CryptoKeyType {
UNKNOWN_KEY_TYPE = (int)0xf849b0c5,
CONTENT_KEY = (int)0x336592b0,
ENTITLEMENT_KEY = (int)0x375af9af,
DRM_RSA_PRIVATE_KEY = (int)0x2e912492,
MAC_KEY_SERVER = (int)0xa09c9790,
MAC_KEY_CLIENT = (int)0x05f09a35,
ENCRYPTION_KEY = (int)0x8976b781,
SESSION_KEY = (int)0xbc792301,
} CryptoKeyType;
/* Dictates the current valid operation state of the key. This is to ensure that
keys are not used outside of the scope of their current operation. */
typedef enum CryptoKeyOperation {
UNKNOWN_KEY_OPERATION = (int)0x502bc2ac,
CONTENT_KEY_DECRYPT = (int)0x8638add1,
ENTITLEMENT_KEY_DECRYPT = (int)0x2d247825,
DRM_RSA_PRIVATE_KEY_SIGN = (int)0xb058fafc,
DRM_RSA_PRIVATE_KEY_DECRYPT = (int)0xe6cf4d35,
MAC_KEY_SERVER_SIGN = (int)0x2f7e2983,
MAC_KEY_SERVER_VERIFY = (int)0x0281fd41,
MAC_KEY_CLIENT_SIGN = (int)0x28c9fef2,
ENCRYPTION_KEY_ENCRYPT = (int)0x162ae24f,
ENCRYPTION_KEY_DECRYPT = (int)0x338b8c63,
SESSION_KEY_DERIVE = (int)0xcc279cc5,
} CryptoKeyOperation;
/* The valid possible sizes of the crypto key. */
typedef enum CryptoKeySize {
UNKNOWN_KEY_SIZE = 0,
KEY_SIZE_128 = 16,
KEY_SIZE_160 = 20,
KEY_SIZE_256 = 32,
KEY_SIZE_384 = 48,
KEY_SIZE_512 = 64,
KEY_SIZE_1024 = 128,
KEY_SIZE_2048 = 256,
KEY_SIZE_3072 = 384,
KEY_SIZE_4096 = 512,
} CryptoKeySize;
/* This is the format of a Widevine keybox. */
typedef struct WidevineKeybox { /* 128 bytes total. */
/* C character array identifying the device. Padded with NULL to fit array. */
uint8_t device_id[32];
/* 128 bit AES key assigned to device. Generated by Widevine. */
uint8_t device_key[16];
/* Key Data. Encrypted data. */
uint8_t data[72];
/* Constant code used to recognize a valid keybox "kbox" = 0x6b626f78. */
uint8_t magic[4];
/* The CRC checksum of the first 124 bytes of the keybox. */
uint8_t crc[4];
} WidevineKeybox;
/* Key Control Block Bit Masks: */
#define CONTROL_OBSERVE_DATA_PATH (1 << 31)
#define CONTROL_OBSERVE_HDCP (1 << 30)
#define CONTROL_OBSERVE_CGMS (1 << 29)
#define CONTROL_REQUIRE_ANTI_ROLLBACK_HARDWARE (1 << 28)
#define CONTROL_ALLOW_HASH_VERIFICATION (1 << 24)
#define SHARED_LICENSE (1 << 23)
#define CONTROL_SRM_VERSION_REQUIRED (1 << 22)
#define CONTROL_DISABLE_ANALOG_OUTPUT (1 << 21)
#define CONTROL_SECURITY_PATCH_LEVEL_SHIFT 15
#define CONTROL_SECURITY_PATCH_LEVEL_MASK \
(0x3F << CONTROL_SECURITY_PATCH_LEVEL_SHIFT)
#define CONTROL_REPLAY_MASK (0x03 << 13)
#define CONTROL_NONCE_REQUIRED (0x01 << 13)
#define CONTROL_NONCE_OR_ENTRY (0x02 << 13)
#define CONTROL_HDCP_VERSION_SHIFT 9
#define CONTROL_HDCP_VERSION_MASK (0x0F << CONTROL_HDCP_VERSION_SHIFT)
#define CONTROL_ALLOW_ENCRYPT (1 << 8)
#define CONTROL_ALLOW_DECRYPT (1 << 7)
#define CONTROL_ALLOW_SIGN (1 << 6)
#define CONTROL_ALLOW_VERIFY (1 << 5)
#define CONTROL_DATA_PATH_SECURE (1 << 4)
#define CONTROL_NONCE_ENABLED (1 << 3)
#define CONTROL_HDCP_REQUIRED (1 << 2)
#define CONTROL_CGMS_MASK 0x03
#define CONTROL_CGMS_COPY_FREELY 0x00
#define CONTROL_CGMS_COPY_ONCE 0x02
#define CONTROL_CGMS_COPY_NEVER 0x03
/* Various constants and sizes */
#define AES_BLOCK_SIZE 16
#define KEY_CONTROL_SIZE 16
#define KEY_ID_MAX_SIZE 16
#define KEY_IV_SIZE 16
#define KEY_PAD_SIZE 16
#define KEY_SIZE 16
#define MAC_KEY_SIZE 32
#define KEYBOX_KEY_DATA_SIZE 72
#define KEYBOX_DEVICE_ID_SIZE 32
#define SRM_REQUIREMENT_SIZE 12
#define SHA256_DIGEST_LENGTH 32
#define SHA_DIGEST_LENGTH 20
/* TODO(b/145026434): The value of PKCS8_RSA_KEY_MAX_SIZE is purely from testing
with openssl and is not derived from any specification. Generating 3072-bit
PKCS8 RSA keys gave me keys of a max size of 1794 bytes so I added a bit of
padding to make sure it's okay. This is NOT guaranteed to work for all DRM
keys. */
#define PKCS8_RSA_KEY_MAX_SIZE 2000
typedef struct WrappedRSAKey {
uint8_t signature[MAC_KEY_SIZE];
uint8_t context[MAC_KEY_SIZE];
uint8_t iv[KEY_IV_SIZE];
uint8_t enc_rsa_key[];
} WrappedRSAKey;
#endif /* OEMCRYPTO_TA_OEMCRYPTO_KEY_TYPES_H_ */

View File

@@ -0,0 +1,81 @@
/* 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_nonce_table.h"
#include "stddef.h"
#include "assert_interface.h"
void AddNonce(NonceTable* nonce_table, uint32_t nonce) {
ASSERT(nonce_table != NULL, "nonce_table is NULL");
int new_slot = -1;
int oldest_slot = -1;
/* Flush any nonce_table->nonces that have been checked but not flushed.
After flush, nonce_table->nonces will be either valid or invalid. */
FlushNonces(nonce_table);
for (int i = 0; i < NONCE_TABLE_SIZE; i++) {
/* Increase nonce_table->age of all valid nonce_table->nonces. */
if (nonce_table->state[i] == NT_STATE_VALID) {
nonce_table->age[i]++;
if (oldest_slot == -1) {
oldest_slot = i;
} else {
if (nonce_table->age[i] > nonce_table->age[oldest_slot]) {
oldest_slot = i;
}
}
} else {
if (new_slot == -1) {
nonce_table->age[i] = 0;
nonce_table->nonces[i] = nonce;
nonce_table->state[i] = NT_STATE_VALID;
new_slot = i;
}
}
}
if (new_slot == -1) {
/* reuse oldest */
ASSERT(oldest_slot != -1, "oldest_slot is -1");
int i = oldest_slot;
nonce_table->age[i] = 0;
nonce_table->nonces[i] = nonce;
nonce_table->state[i] = NT_STATE_VALID;
}
}
bool CheckNonce(NonceTable* nonce_table, uint32_t nonce) {
ASSERT(nonce_table != NULL, "nonce_table is NULL");
for (int i = 0; i < NONCE_TABLE_SIZE; i++) {
if (nonce_table->state[i] != NT_STATE_INVALID) {
if (nonce_table->nonces[i] == nonce) {
nonce_table->state[i] = NT_STATE_FLUSH_PENDING;
return true;
}
}
}
return false;
}
bool NonceCollision(NonceTable* nonce_table, uint32_t nonce) {
ASSERT(nonce_table != NULL, "nonce_table is NULL");
for (int i = 0; i < NONCE_TABLE_SIZE; i++) {
if (nonce_table->nonces[i] == nonce &&
nonce_table->state[i] != NT_STATE_INVALID) {
return true;
}
}
return false;
}
void FlushNonces(NonceTable* nonce_table) {
ASSERT(nonce_table != NULL, "nonce_table is NULL");
for (int i = 0; i < NONCE_TABLE_SIZE; i++) {
if (nonce_table->state[i] == NT_STATE_FLUSH_PENDING) {
nonce_table->state[i] = NT_STATE_INVALID;
}
}
}

View File

@@ -0,0 +1,41 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_NONCE_TABLE_H_
#define OEMCRYPTO_TA_OEMCRYPTO_NONCE_TABLE_H_
#include "stdbool.h"
#include "stdint.h"
#define NONCE_TABLE_SIZE 4
typedef enum NonceTableState {
NT_STATE_INVALID = 0x73624fe4,
NT_STATE_VALID = 0x7fb3983a,
NT_STATE_FLUSH_PENDING = 0x1b9654ae,
} NonceTableState;
typedef struct NonceTable {
NonceTableState state[NONCE_TABLE_SIZE];
uint32_t age[NONCE_TABLE_SIZE];
uint32_t nonces[NONCE_TABLE_SIZE];
} NonceTable;
/* Adds |nonce| to the |nonce_table|. Flushes any nonces that have already been
checked and then either adds it to the nonce table or replaces the oldest
valid nonce in the table. */
void AddNonce(NonceTable* nonce_table, uint32_t nonce);
/* Checks the |nonce| exists in the |nonce_table|. Marks the nonce as ready to
be flushed if it exists.
Returns false if it does not exist in the table. */
bool CheckNonce(NonceTable* nonce_table, uint32_t nonce);
/* Verify that the nonce is not the same as any in |nonce_table|. */
bool NonceCollision(NonceTable* nonce_table, uint32_t nonce);
/* Flush any nonces that have been checked in |nonce_table|. */
void FlushNonces(NonceTable* nonce_table);
#endif // OEMCRYPTO_TA_OEMCRYPTO_NONCE_TABLE_H_

View File

@@ -0,0 +1,56 @@
/* 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 "stddef.h"
#include "stdint.h"
int SubOverflowIX(int a, int b, int* c) {
if (a >= b) {
if (c) {
*c = a - b;
}
return 0;
}
return 1;
}
int SubOverflowU32(uint32_t a, uint32_t b, uint32_t* c) {
if (a >= b) {
if (c) {
*c = a - b;
}
return 0;
}
return 1;
}
int SubOverflowU64(uint64_t a, uint64_t b, uint64_t* c) {
if (a >= b) {
if (c) {
*c = a - b;
}
return 0;
}
return 1;
}
int AddOverflowUX(size_t a, size_t b, size_t* c) {
if (SIZE_MAX - a >= b) {
if (c) {
*c = a + b;
}
return 0;
}
return 1;
}
int SubOverflowUX(size_t a, size_t b, size_t* c) {
if (a >= b) {
if (c) {
*c = a - b;
}
return 0;
}
return 1;
}

View File

@@ -0,0 +1,32 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_OVERFLOW_H_
#define OEMCRYPTO_TA_OEMCRYPTO_OVERFLOW_H_
/* Defines operators that check for overflow in arithmetic.
Uses the standards GNU builtins if defined and custom overflow functions
otherwise. */
/* TODO(b/145244569): Unify this with the interface for the odk. */
#ifndef __has_builtin
# define __has_builtin(x) 0
#endif
#if (defined(__GNUC__) && __GNUC__ >= 5) || \
__has_builtin(__builtin_add_overflow)
# define SubOverflowIX __builtin_sub_overflow
# define SubOverflowU32 __builtin_sub_overflow
# define SubOverflowU64 __builtin_sub_overflow
# define AddOverflowUX __builtin_add_overflow
# define SubOverflowUX __builtin_sub_overflow
#else
int SubOverflowIX(int a, int b, int* c);
int SubOverflowU32(uint32_t a, uint32_t b, uint32_t* c);
int SubOverflowU64(uint64_t a, uint64_t b, uint64_t* c);
int AddOverflowUX(size_t a, size_t b, size_t* c);
int SubOverflowUX(size_t a, size_t b, size_t* c);
#endif
#endif /* OEMCRYPTO_TA_OEMCRYPTO_OVERFLOW_H_ */

View File

@@ -0,0 +1,100 @@
/* Copyright 2020 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_serialized_usage_table.h"
#include "stddef.h"
#include "string.h"
#include "assert_interface.h"
#include "oemcrypto_usage_table.h"
/* Porting layer includes: */
#include "assert_interface.h"
/* The buffer size we need to reserve for a signed header with the given number
of entries.
TODO(b/158720996): use serialization and allow variable sized headers. This
code currently uses memcpy to serialize data. That works as long as we do not
try to change message format or want to change the header size.
*/
size_t SignedHeaderSize(int table_size) {
return sizeof(SignedSavedUsageHeader);
}
size_t SignedEntrySize() { return sizeof(SignedSavedUsageEntry); }
OEMCryptoResult PackSignedUsageHeader(uint8_t* buffer, size_t buffer_size,
const SignedSavedUsageHeader* header) {
if (buffer_size < sizeof(SignedSavedUsageHeader)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(buffer, header, sizeof(SignedSavedUsageHeader));
return OEMCrypto_SUCCESS;
}
OEMCryptoResult PackUsageHeader(uint8_t* buffer, size_t buffer_size,
const SavedUsageHeader* header) {
if (buffer_size < sizeof(SavedUsageHeader)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(buffer, header, sizeof(SavedUsageHeader));
return OEMCrypto_SUCCESS;
}
OEMCryptoResult PackSignedUsageEntry(uint8_t* buffer, size_t buffer_size,
const SignedSavedUsageEntry* entry) {
if (buffer_size < sizeof(SignedSavedUsageEntry)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(buffer, entry, sizeof(SignedSavedUsageEntry));
return OEMCrypto_SUCCESS;
}
OEMCryptoResult PackUsageEntry(uint8_t* buffer, size_t buffer_size,
const SavedUsageEntry* entry) {
if (buffer_size < sizeof(SavedUsageEntry)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(buffer, entry, sizeof(SavedUsageEntry));
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UnpackSignedUsageHeader(const uint8_t* buffer,
size_t buffer_size,
SignedSavedUsageHeader* header) {
if (buffer_size < sizeof(SignedSavedUsageHeader)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(header, buffer, sizeof(SignedSavedUsageHeader));
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UnpackUsageHeader(const uint8_t* buffer, size_t buffer_size,
SavedUsageHeader* header) {
if (buffer_size < sizeof(SavedUsageHeader)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(header, buffer, sizeof(SavedUsageHeader));
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UnpackSignedUsageEntry(const uint8_t* buffer,
size_t buffer_size,
SignedSavedUsageEntry* entry) {
if (buffer_size < sizeof(SignedSavedUsageEntry)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(entry, buffer, sizeof(SignedSavedUsageEntry));
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UnpackUsageEntry(const uint8_t* buffer, size_t buffer_size,
SavedUsageEntry* entry) {
if (buffer_size < sizeof(SavedUsageEntry)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memcpy(entry, buffer, sizeof(SavedUsageEntry));
return OEMCrypto_SUCCESS;
}

View File

@@ -0,0 +1,118 @@
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
source code may only be used and distributed under the Widevine Master
License Agreement. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SERIALIZED_USAGE_TABLE_H_
#define OEMCRYPTO_TA_OEMCRYPTO_SERIALIZED_USAGE_TABLE_H_
#include "OEMCryptoCENC.h"
#include "oemcrypto_config_macros.h"
#include "oemcrypto_key_types.h"
/* File types. */
#define USAGE_TABLE_HEADER 0x68656164
#define USAGE_TABLE_ENTRY 0x656e7472
#define SIGNED_USAGE_TABLE_HEADER 0x48454144
#define SIGNED_USAGE_TABLE_ENTRY 0x456e7472
#define MAX_PST_LENGTH 255
/* This can be changed to allow for newer code to load older files. */
#define CURRENT_FILE_FORMAT_VERSION 1
/* This is the usage header, as saved to the file system, before encryption. */
typedef struct SavedUsageHeader {
uint32_t file_type; /* This should always be USAGE_TABLE_HEADER */
uint32_t format_version; /* For future backwards compatibility. */
uint64_t master_generation_number;
uint32_t table_size; /* Number of entries in the table. */
/* These are the generation numbers of each entry. */
uint64_t generation_numbers[MAX_NUMBER_OF_USAGE_ENTRIES];
} SavedUsageHeader;
/* This is all of the data we wish to save as part of a usage table entry,
* before encryption. */
typedef struct SavedUsageEntry {
uint32_t file_type; /* This should always be USAGE_TABLE_ENTRY. */
uint32_t format_version; /* For future backwards compatibility. */
uint32_t index; /* Index into usage header. */
uint64_t generation_number; /* Used to prevent rollback. */
int64_t time_of_license_received; /* Time in seconds on system clock. */
int64_t time_of_first_decrypt; /* Time in seconds on system clock. */
int64_t time_of_last_decrypt; /* Time in seconds on system clock.. */
/* Status of the entry or license, as documented in OEMCrypto spec. */
enum OEMCrypto_Usage_Entry_Status status;
/* Server Mac key wrapped for this device. */
uint8_t mac_key_server[WRAPPED_MAC_KEY_SIZE];
/* Client Mac key wrapped for this device. */
uint8_t mac_key_client[WRAPPED_MAC_KEY_SIZE];
/* Provider session token for this license. */
uint8_t pst[MAX_PST_LENGTH + 1]; /* add 1 for padding. */
uint8_t pst_length;
} SavedUsageEntry;
/* TODO(b/158720996): This should be updated when we switch to using odkitee
* serialization. */
/* In order to encrypt the data, we'll copy it to a slightly larger buffer
* that is a whole multiple of an AES block. */
#define PADDED_HEADER_BUFFER_SIZE (16 * (1 + sizeof(SavedUsageHeader) / 16))
#define PADDED_ENTRY_BUFFER_SIZE (16 * (1 + sizeof(SavedUsageEntry) / 16))
/* This is the usage header, as saved to the file system, after encryption and
* signing. */
typedef struct SignedSavedUsageHeader {
uint32_t file_type; /* This should always be SIGNED_USAGE_TABLE_HEADER */
/* Used for future backwards compatibility. */
uint32_t format_version;
/* The size of the saved buffer. At most PADDED_HEADER_BUFFER_SIZE. Must be
* a multiple of 16, one AES block size. */
uint32_t buffer_size;
/* Signature of the buffer using a device unique key. */
uint8_t signature[SHA256_DIGEST_LENGTH];
/* An encrypted SavedUsageHeader. */
uint8_t buffer[PADDED_HEADER_BUFFER_SIZE];
} SignedSavedUsageHeader;
/* This is a usage table entry, as saved to the file system, after encryption
* and signing. */
typedef struct SignedSavedUsageEntry {
uint32_t file_type; /* This should always be SIGNED_USAGE_TABLE_ENTRY */
/* Used for future backwards compatibility. */
uint32_t format_version;
/* The size of the saved buffer. At most PADDED_HEADER_BUFFER_SIZE. Must be
* a multiple of 16, one AES block size. */
uint32_t buffer_size;
uint8_t signature[SHA256_DIGEST_LENGTH];
/* Signature of the buffer using a device unique key. */
uint8_t buffer[PADDED_ENTRY_BUFFER_SIZE]; /* An encrypted SavedUsageEntry */
} SignedSavedUsageEntry;
/* TODO(b/158720996): use serialization to turn these structures into messages
* for saving. */
/* Size of a serialized SignedSavedUsageHeader with the specified table size. */
size_t SignedHeaderSize(int table_size);
/* Size of a serialized SignedSavedUsageEntry. */
size_t SignedEntrySize(void);
OEMCryptoResult PackSignedUsageHeader(uint8_t* buffer, size_t buffer_size,
const SignedSavedUsageHeader* header);
OEMCryptoResult PackUsageHeader(uint8_t* buffer, size_t buffer_size,
const SavedUsageHeader* header);
OEMCryptoResult PackSignedUsageEntry(uint8_t* buffer, size_t buffer_size,
const SignedSavedUsageEntry* entry);
OEMCryptoResult PackUsageEntry(uint8_t* buffer, size_t buffer_size,
const SavedUsageEntry* entry);
OEMCryptoResult UnpackSignedUsageHeader(const uint8_t* buffer,
size_t buffer_size,
SignedSavedUsageHeader* header);
OEMCryptoResult UnpackUsageHeader(const uint8_t* buffer, size_t buffer_size,
SavedUsageHeader* header);
OEMCryptoResult UnpackSignedUsageEntry(const uint8_t* buffer,
size_t buffer_size,
SignedSavedUsageEntry* entry);
OEMCryptoResult UnpackUsageEntry(const uint8_t* buffer, size_t buffer_size,
SavedUsageEntry* entry);
#endif // OEMCRYPTO_TA_OEMCRYPTO_SERIALIZED_USAGE_TABLE_H_

View File

@@ -0,0 +1,946 @@
/* 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;
}

View File

@@ -0,0 +1,250 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SESSION_H_
#define OEMCRYPTO_TA_OEMCRYPTO_SESSION_H_
#include "OEMCryptoCENC.h"
#include "oemcrypto_config_interface.h"
#include "oemcrypto_key.h"
#include "oemcrypto_nonce_table.h"
/* The different states in which an OEMCrypto session can be in. */
typedef enum OEMCryptoSessionState {
SESSION_OPENED = (int)0xc159acf3,
SESSION_INVALID = (int)0x23a27071,
SESSION_LOAD_OEM_RSA_KEY = (int)0x9d7cae94,
SESSION_LOAD_DRM_RSA_KEY = (int)0xbc17f592,
SESSION_DERIVED_KEYS = (int)0x6f73d57c,
SESSION_DERIVED_KEYS_FROM_SESSION_KEY = (int)0xb852a5ef,
SESSION_KEYS_LOADED = (int)0xce93bdc3,
SESSION_DECRYPT_KEY_SELECTED = (int)0x9877e0a6,
} OEMCryptoSessionState;
/* The API being executed for the current session. */
typedef enum OEMCryptoSessionAPI {
API_OPENSESSION = (int)0x45956770,
API_CLOSESESSION = (int)0xa84dc5a7,
API_GETOEMPUBLICCERTIFICATE = (int)0xe6cf7c3e,
API_GENERATENONCE = (int)0xb9f80df2,
API_GENERATERSASIGNATURE = (int)0x450c7dd0,
API_REWRAPDEVICERSAKEY30 = (int)0x76b99787,
API_LOADDEVICERSAKEY = (int)0xb0143a2e,
API_DERIVEKEYSFROMSESSIONKEY = (int)0xf29462c0,
API_LOADKEYS = (int)0x55c0291c,
API_REFRESHKEYS = (int)0xc3f785fe,
API_GENERATESIGNATURE = (int)0x0c4352f4,
API_LOADENTITLEDCONTENTKEYS = (int)0x498bc417,
API_SELECTKEY = (int)0xef2e58fb,
API_DECRYPTCENC = (int)0xf5ad6301,
API_GENERICENCRYPT = (int)0x3bd1f139,
API_GENERICDECRYPT = (int)0xcfc2970a,
API_GENERICSIGN = (int)0xb721196a,
API_GENERICVERIFY = (int)0xe09fa38b,
API_SETDECRYPTHASH = (int)0x427f7e9b,
API_GETHASHERRORCODE = (int)0xde3477cc,
API_QUERYKEYCONTROL = (int)0x7ab3659c,
API_GENERATEDERIVEDKEYS = (int)0x59b1e187,
API_REWRAPDEVICERSAKEY = (int)0x800fef5d,
} OEMCryptoSessionAPI;
typedef enum UsageEntryStatus {
SESSION_HAS_NO_ENTRY = (int)0xacbad562,
SESSION_HAS_NEW_ENTRY = (int)0x4545babc,
SESSION_HAS_LOADED_ENTRY = (int)0x766bca12,
SESSION_HAS_DEACTIVATED_ENTRY = (int)0xdacba37,
} UsageEntryStatus;
typedef struct OEMCryptoSession {
OEMCrypto_SESSION session_id;
OEMCryptoSessionState state;
NonceTable nonce_table;
CryptoKey* drm_private_key;
CryptoKey* mac_key_server;
CryptoKey* mac_key_client;
CryptoKey* encryption_key;
CryptoKey* session_key;
bool refresh_valid;
OEMCrypto_LicenseType license_type;
uint32_t current_content_key_index;
CryptoKey* content_keys[CONTENT_KEYS_PER_SESSION];
uint32_t num_content_keys;
CryptoKey* entitlement_keys[ENTITLEMENT_KEYS_PER_SESSION];
uint32_t num_entitlement_keys;
bool valid_srm_version;
uint64_t timer_start;
/* These are used when doing full decrypt path testing. */
bool compute_hash; /* True if the current frame needs a hash. */
uint32_t current_hash; /* Running CRC hash of frame. */
uint32_t given_hash; /* True CRC hash of frame. */
uint32_t current_frame_number; /* Current frame for CRC hash. */
uint32_t bad_frame_number; /* Frame number with bad hash. */
OEMCryptoResult hash_error; /* Error code for first bad frame. */
UsageEntryStatus usage_entry_status;
uint32_t usage_entry_number;
/* If |recent_decrypt| is true, then a usage report cannot be generated
* without first updating the usage entry. It should be set to true whenever a
* key is used. */
bool recent_decrypt;
} OEMCryptoSession;
/* Initializes session context.
Returns OEMCrypto_SUCCESS.
Caller retains ownership of |session| and it must not be NULL. */
OEMCryptoResult InitializeSession(OEMCryptoSession* session, uint32_t index);
/* Cleans up a session declaration by freeing any used keys and clearing any
state so the session could be reused in a future OpenSession call.
Returns the result of freeing the keys in the session.
Caller retains ownership of |session| and it must not be NULL. */
OEMCryptoResult TerminateSession(OEMCryptoSession* session);
/* Asserts that the session state in |session| is set to the appropriate value
that is needed to execute |api|.
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE on failure, and OEMCrypto_SUCCESS on
success.
Caller retains ownership of |session| and it must not be NULL. */
OEMCryptoResult CheckStatePreCall(OEMCryptoSession* session,
OEMCryptoSessionAPI api);
/* Sets the session state in |session| to the appropriate state for having just
executed |api|.
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE on failure, and OEMCrypto_SUCCESS on
success.
Caller retains ownership of |session| and it must not be NULL. */
OEMCryptoResult SetStatePostCall(OEMCryptoSession* session,
OEMCryptoSessionAPI api);
/* Initializes the timer_start field in the |session| to the current online
time.
Returns the result of getting the online time.
Caller retains ownership of |session| and it must not be NULL. */
OEMCryptoResult StartTimer(OEMCryptoSession* session);
/* Calculates the diff of the timer_start field in |session| and the current
online time and places it in |diff|.
Returns the result of getting the online time or subtracting the times.
Caller retains ownership of all pointers and they must not be NULL. */
OEMCryptoResult CurrentTimer(const OEMCryptoSession* session, uint64_t* diff);
/* Attempts to deserialize the DRM RSA key and load it into |session|'s
drm_private_key field. Returns the result of creating the private key.
|rsa_key_length| must be > 0.
Caller retains ownership of all pointers and they must not be NULL. */
OEMCryptoResult LoadDRMRSAKey(OEMCryptoSession* session,
const uint8_t* pkcs8_rsa_key,
uint32_t rsa_key_length);
/* Derives mac and encryption keys from the specific key. Uses AES-128-CMAC
and |mac_ and enc_key_contexts| to derive and create a mac_key_server,
mac_key_client, and encryption_key for the |session|.
Returns the result of the derivation if it fails or the result of key
creation.
All lengths must be > 0.
Caller retains ownership of all pointers and they must not be NULL. */
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);
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);
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);
/* Verifies |signature| of |message| with the mac_key_server stored in
|session|. Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if the mac_key_server is
invalid, the result of signature calculation if it fails,
OEMCrypto_ERROR_SIGNATURE_FAILURE if the calculated and provided signatures
don't match, and OEMCrypto_SUCCESS otherwise.
|message_length| must be > 0.
Caller retains ownership of all pointers and they must not be NULL. */
OEMCryptoResult VerifySignatureWithMacKeyServer(OEMCryptoSession* session,
const uint8_t* message,
uint32_t message_length,
const uint8_t* signature);
/* Installs the key using the given data into the |session| depending on the
license_type. Decrypts the |key_data| using the encryption_key in the session
and then uses the first 128 bits of the decrypted key to decrypt
|key_control|. Returns OEMCrypto_ERROR_INSUFFICIENT_RESOURCES if the session
cannot hold any more keys, OEMCrypto_ERROR_INVALID_CONTEXT if the key control
block is invalid, OEMCrypto_ERROR_INVALID_NONCE if nonce is required and the
provided nonce is not in the session's nonce table,
OEMCrypto_ERROR_UNKNOWN FAILURE for all other failures, and OEMCrypto_SUCCESS
otherwise.
All lengths must be > 0.
Caller retains ownership of all pointers and they must not be NULL. */
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);
/* Updates the mac_key_server and mac_key_client in |session|. Decrypts
|enc_mac_keys| using the encryption_key and splits them into the two mac
keys. Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if the encryption_key is not
valid and the result of creating the mac keys otherwise.
Caller retains ownership of all pointers and they must not be NULL. */
OEMCryptoResult UpdateMacKeys(OEMCryptoSession* session,
const uint8_t* enc_mac_keys,
const uint8_t* mac_keys_iv);
/* Given a |session| and either a raw or encrypted |key_control|, refreshes
either the key with the matching |key_id| or all keys of the current license
type. If |key_id| or |key_control_iv| are NULL, the key control is assumed to
be unencrypted. If |key_id| is NULL, all keys of the current license type
will have their durations updated.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if the key control is invalid,
OEMCrypto_ERROR_INVALID_NONCE if there's no matching nonce,
OEMCrypto_ERROR_NO_CONTENT_KEY if there is no key with the same key id,
OEMCrypto_ERROR_UNKNOWN_FAILURE for all other failures, and OEMCrypto_SUCCESS
otheriwse.
Caller retains ownership of all pointers, and |session| and |key_control|
must not be NULL. */
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);
/* Checks whether the current content key selected in the |session| can be used
in the operation given by |use_type| and with the given |buffer_type|.
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if there is no valid current content
key, OEMCrypto_ERROR_INVALID_CONTEXT if if |use_type| is not allowed by the
key control block or if the replay mask is set in the key control block,
OEMCrypto_DECRYPT_FAILED if the |buffer_type| is not allowed on the device,
OEMCrypto_ERROR_KEY_EXPIRED if the duration in the key control block has
passed, OEMCrypto_ERROR_INSUFFICIENT_HDCP if the HDCP requirements are not
met, OEMCrypto_ERROR_ANALOG_OUTPUT if the analog display requirements are not
met, and OEMCrypto_SUCCESS otherwise.
|buffer_type| is disallowed to be OEMCrypto_BufferType_Direct.
Caller retains ownership of |session| and it must not be NULL. */
OEMCryptoResult CheckCurrentContentKeyUsage(const OEMCryptoSession* session,
uint32_t use_type,
OEMCryptoBufferType buffer_type);
/* Decrypts the |cipher_data_length| bytes of given |cipher_data| and places it
in |clear_data| using the |session|'s current content key, |initial_iv|,
|pattern|, and potentially a |block_offset|.
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if the current content key is
invalid, the result of AESDecrypt or AESEncrypt if the fail, and
OEMCrypto_SUCCESS otherwise.
Lengths must not be 0.
Caller retains ownership of all parameters and they must not be NULL. */
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);
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);
#endif /* OEMCRYPTO_TA_OEMCRYPTO_SESSION_H_ */

View File

@@ -0,0 +1,59 @@
/* 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_key_table.h"
#include "string.h"
#include "assert_interface.h"
static CryptoKey** get_key_table(OEMCryptoSession* session,
OEMCrypto_LicenseType license_type,
uint32_t* num_keys) {
ASSERT(session != NULL && num_keys != NULL, "Parameters are NULL");
ASSERT(license_type == OEMCrypto_ContentLicense ||
license_type == OEMCrypto_EntitlementLicense,
"session has invalid license type");
if (license_type == OEMCrypto_ContentLicense) {
*num_keys = session->num_content_keys;
return session->content_keys;
} else {
*num_keys = session->num_entitlement_keys;
return session->entitlement_keys;
}
}
CryptoKey* FindKeyFromTable(OEMCryptoSession* session, bool is_content_key,
const uint8_t* key_id, uint32_t key_id_length) {
ASSERT(session != NULL && key_id != NULL && key_id_length != 0,
"Parameters are NULL or 0");
uint32_t num_keys;
ASSERT(
session->license_type == OEMCrypto_EntitlementLicense || is_content_key,
"Cannot get entitlement key for content license");
OEMCrypto_LicenseType license_type =
is_content_key ? OEMCrypto_ContentLicense : OEMCrypto_EntitlementLicense;
CryptoKey** key_table = get_key_table(session, license_type, &num_keys);
for (uint32_t i = 0; i < num_keys; i++) {
CryptoKey* key = key_table[i];
ASSERT(key != NULL, "key at index %d is NULL", i);
if (key_id_length == key->key_id_size &&
memcmp(key->key_id, key_id, key_id_length) == 0) {
return key;
}
}
return NULL;
}
void UpdateDurationForAllKeys(OEMCryptoSession* session,
KeyControlBlock key_control_block) {
ASSERT(session != NULL, "session is NULL");
uint32_t num_keys;
CryptoKey** key_table =
get_key_table(session, session->license_type, &num_keys);
for (uint32_t i = 0; i < num_keys; i++) {
ASSERT(key_table[i] != NULL, "key at index %d is NULL", i);
key_table[i]->key_control_block.duration = key_control_block.duration;
}
}

View File

@@ -0,0 +1,26 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SESSION_KEY_TABLE_H_
#define OEMCRYPTO_TA_OEMCRYPTO_SESSION_KEY_TABLE_H_
#include "oemcrypto_key.h"
#include "oemcrypto_session.h"
/* Finds the key from the key table corresponding to the given |is_content_key|
with the given |key_id| and |key_id_length|.
Returns either the key if there is a match or NULL otherwise.
|key_id_length| must be > 0 and |is_content_key| can only be false if the
session has an OEMCrypto_EntitlementLicense.
Caller retains ownership of all parameters and they must not be NULL. */
CryptoKey* FindKeyFromTable(OEMCryptoSession* session, bool is_content_key,
const uint8_t* key_id, uint32_t key_id_length);
/* For the given |session|'s license_type, updates the associated keys to the
duration in the |key_control_block|.
Caller retains ownership of all pointers and they must not be NULL. */
void UpdateDurationForAllKeys(OEMCryptoSession* session,
KeyControlBlock key_control_block);
#endif /* OEMCRYPTO_TA_OEMCRYPTO_SESSION_KEY_TABLE_H_ */

View File

@@ -0,0 +1,106 @@
/* 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_table.h"
#include "stdint.h"
#include "string.h"
#include "assert_interface.h"
#include "logging_interface.h"
#include "oemcrypto_key.h"
static SessionTable session_table;
static uint32_t open_session_count = 0;
static bool session_table_initialized = false;
OEMCryptoResult InitializeSessionTable(void) {
ASSERT(MAX_NUMBER_OF_SESSIONS > 0, "MAX_NUMBER_OF_SESSIONS must be > 0");
ASSERT(MAX_NUMBER_OF_SESSIONS <= UINT32_MAX - 1,
"MAX_NUMBER_OF_SESSIONS is too large");
if (session_table_initialized) {
return OEMCrypto_ERROR_INIT_FAILED;
}
session_table.first_free_session = 0;
for (uint32_t i = 0; i < MAX_NUMBER_OF_SESSIONS; i++) {
session_table.next_free_session[i] = i + 1;
session_table.is_free[i] = true;
memset(&session_table.sessions[i], 0, sizeof(OEMCryptoSession));
}
session_table_initialized = true;
open_session_count = 0;
return OEMCrypto_SUCCESS;
}
uint32_t MaxNumberOfSessions(void) { return MAX_NUMBER_OF_SESSIONS; }
OEMCryptoResult NumberOfOpenSessions(uint32_t* num_open_sessions) {
ASSERT(num_open_sessions != NULL, "num_open_sessions is NULL");
if (!session_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
*num_open_sessions = open_session_count;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult GrabSession(uint32_t* index) {
ASSERT(index != NULL, "index is NULL");
if (!session_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
if (session_table.first_free_session == MAX_NUMBER_OF_SESSIONS) {
return OEMCrypto_ERROR_TOO_MANY_SESSIONS;
}
*index = session_table.first_free_session;
session_table.first_free_session = session_table.next_free_session[*index];
session_table.is_free[*index] = false;
open_session_count++;
return InitializeSession(&session_table.sessions[*index], *index);
}
OEMCryptoResult GetSession(uint32_t index, OEMCryptoSession** session) {
ASSERT(session != NULL, "session is NULL");
if (!session_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
if (index >= MAX_NUMBER_OF_SESSIONS || session_table.is_free[index]) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
*session = &session_table.sessions[index];
return OEMCrypto_SUCCESS;
}
OEMCryptoResult FreeSession(uint32_t index) {
if (!session_table_initialized) {
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
if (index >= MAX_NUMBER_OF_SESSIONS || session_table.is_free[index]) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
session_table.next_free_session[index] = session_table.first_free_session;
session_table.is_free[index] = true;
session_table.first_free_session = index;
open_session_count--;
return TerminateSession(&session_table.sessions[index]);
}
OEMCryptoResult TerminateSessionTable(void) {
if (!session_table_initialized) {
return OEMCrypto_ERROR_TERMINATE_FAILED;
}
OEMCryptoResult result = OEMCrypto_SUCCESS;
for (int i = 0; i < MAX_NUMBER_OF_SESSIONS; i++) {
if (!session_table.is_free[i]) {
result = OEMCrypto_ERROR_TERMINATE_FAILED;
/* Attempt to free the session. */
OEMCryptoResult free_result = FreeSession(i);
if (free_result != OEMCrypto_SUCCESS) {
LOGE("Could not free session %d with error: %d", i, free_result);
}
}
}
session_table_initialized = false;
open_session_count = 0;
return result;
}

View File

@@ -0,0 +1,59 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_SESSION_TABLE_H_
#define OEMCRYPTO_TA_OEMCRYPTO_SESSION_TABLE_H_
#include "OEMCryptoCENC.h"
#include "oemcrypto_config_interface.h"
#include "oemcrypto_session.h"
typedef struct SessionTable {
OEMCryptoSession sessions[MAX_NUMBER_OF_SESSIONS];
uint32_t first_free_session;
uint32_t next_free_session[MAX_NUMBER_OF_SESSIONS];
bool is_free[MAX_NUMBER_OF_SESSIONS];
} SessionTable;
/* Initializes the session table for future OpenSession calls. Returns
OEMCrypto_ERROR_INIT_FAILED if the session table has already been initialized
and OEMCrypto_SUCCESS otherwise. */
OEMCryptoResult InitializeSessionTable(void);
/* Gets the max number of sessions. */
uint32_t MaxNumberOfSessions(void);
/* Gets the number of open sessions. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED
if the session table has not been initialized and OEMCrypto_SUCCESS
otherwise.
Caller retains ownership of |num_open_sessions| and it must not be NULL. */
OEMCryptoResult NumberOfOpenSessions(uint32_t* num_open_sessions);
/* Attempts to grab an open entry in the session table and set |index| to the
entry position. Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the session
table has not been initialized and OEMCrypto_ERROR_TOO_MANY_SESSIONS if there
are no sessions left to grab. Returns OEMCrypto_SUCCESS otherwise.
Caller retains ownership of |index| and it must not be NULL. */
OEMCryptoResult GrabSession(uint32_t* index);
/* Sets session to the session at |index| in the session table if it is free.
Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the session table has not been
initialized and OEMCrypto_ERROR_INVALID_SESSION if the session has not been
grabbed or if the index is invalid. Returns OEMCrypto_SUCCESS otherwise.
Caller retains ownership of |session| and it must not be NULL. */
OEMCryptoResult GetSession(uint32_t index, OEMCryptoSession** session);
/* Given a non-free session |index|, attempts to free it so it can be reused.
Returns OEMCrypto_ERROR_SYSTEM_INVALIDATED if the session table has not been
initialized and OEMCrypto_ERROR_INVALID_SESSION if the session has not been
grabbed or if the index is invalid. Returns OEMCrypto_SUCCESS otherwise. */
OEMCryptoResult FreeSession(uint32_t index);
/* Clears and cleans up the session table. The session table must be
reinitialized to be used. Returns OEMCrypto_ERROR_TERMINATE_FAILED if the
table has not been initialized or if there are any active sessions still.
Returns OEMCrypto_SUCCESS otherwise. */
OEMCryptoResult TerminateSessionTable(void);
#endif /* OEMCRYPTO_TA_OEMCRYPTO_SESSION_TABLE_H_ */

View File

@@ -0,0 +1,976 @@
/* Copyright 2020 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_usage_table.h"
#include "stddef.h"
#include "string.h"
#include "OEMCryptoCENC.h"
#include "assert_interface.h"
#include "clock_interface.h"
#include "crypto_interface.h"
#include "logging_interface.h"
#include "oemcrypto_serialized_usage_table.h"
#include "oemcrypto_session.h"
/* Porting layer includes: */
#include "assert_interface.h"
#include "generation_number_interface.h"
/**
The usage table consists of a short array of active entries, and a large
array of possible entries. The usage table header is stored in the large
array. The large array stores the generation number for each entry and an
index into the short array if the entry is active. The short array holds
entries that have been loaded and are connected to a session.
*/
/* Global usage table states. Used to tell if we need to load the usage table or
* not. */
typedef enum UsageTableState {
/* Initial state. */
USAGE_TABLE_NOT_INITIALIZED = (int)0xC887d04,
/* Error state. Actually, anything but the other states are considered
* errors, but we can explicitly set the state to this value. */
USAGE_TABLE_ERROR_STATE = (int)0xd06ca1,
/* After the usage table has been initialized to an empty table. The usage
* table cannot be used in this state because the generation number is not yet
* valid. */
USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE = (int)0x1776dcfa,
/* After the usage table has been loaded or created with
* OEMCrypto_LoadUsageTableHeader or OEMCrypto_CreateUsageTableHeader. The key
* distinction between this state and the previous one is that the generation
* number is valid in this state. The usage table might be empty in this
* state, but it is ready to have new entries created. */
USAGE_TABLE_ACTIVE_STATE = (int)0x7331face,
} UsageTableState;
/* Indicates if a usage entry is active or not. */
typedef enum UsageEntryState {
USAGE_ENTRY_ACTIVE = (int)0x42fab1c,
USAGE_ENTRY_NOT_ACTIVE = (int)0x39abcab4,
} UsageEntryState;
/* Data storage for a usage entry in memory. It has the data for a usage entry
* that is saved to the filesystem, and also all of the transient state, such as
* the current session it's tied to and the current status. */
typedef struct ResidentUsageEntry {
/* The part of the usage entry that is saved to the file system. */
SavedUsageEntry data;
OEMCrypto_SESSION session_id; /* Which session this entry is actively in. */
UsageEntryState state; /* Whether this entry is active or not. */
bool forbid_report;
bool recent_decrypt;
int next_free_active_entry; /* A linked list of unused active entries. */
} ResidentUsageEntry;
/* A data structure holding the current, active usage table -- that means it has
* a header and all of the resident entries. */
typedef struct ActiveUsageTable {
/* The master generation number. */
uint64_t master_generation_number;
/* This number of active usage entries. */
uint32_t active_table_size;
/* The head of the free list for active entries. */
uint32_t first_free_active_entry;
/* Storage for the active entries. */
ResidentUsageEntry entries[MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES];
uint32_t table_size; /* Number of entries in the table. */
/* These are the generation numbers of each entry. */
uint64_t generation_numbers[MAX_NUMBER_OF_USAGE_ENTRIES];
/* Map from usage header index into array of active entries. This is updated
* when an entry is loaded or created and then again when it is released. */
struct {
UsageEntryState state;
uint32_t active_entry_index;
} active_entry_map[MAX_NUMBER_OF_USAGE_ENTRIES];
} ActiveUsageTable;
/* A file local variable indicating whether the usage table has been initialized
* or not. */
static UsageTableState g_usage_table_state = USAGE_TABLE_NOT_INITIALIZED;
/* The file local variable holding the current active usage table. This is only
* valid if |g_usage_table_state| is USAGE_TABLE_ACTIVE. */
static ActiveUsageTable g_usage_table;
/* TODO(b/158771223): figure out a way to avoid __attribute__(packed). */
typedef struct {
uint8_t signature[20]; // -- HMAC SHA1 of the rest of the report.
uint8_t status; // current status of entry. (OEMCrypto_Usage_Entry_Status)
uint8_t clock_security_level;
uint8_t pst_length;
uint8_t padding; // make int64's word aligned.
int64_t seconds_since_license_received; // now - time_of_license_received
int64_t seconds_since_first_decrypt; // now - time_of_first_decrypt
int64_t seconds_since_last_decrypt; // now - time_of_last_decrypt
uint8_t pst[];
} __attribute__((packed)) OEMCrypto_PST_Report;
/* TODO(b/158131747): add htonll64 to common hton functions. */
int64_t htonll64(int64_t x) {
uint8_t* bytes = (uint8_t*)(&x);
uint64_t y = 0;
for (int i = 0; i < 8; i++) {
y = (y << 8) | bytes[i];
}
return (int64_t)y;
}
static void ClearTable(void) {
g_usage_table.active_table_size = 0;
g_usage_table.first_free_active_entry = 0;
for (int i = 0; i < MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES; i++) {
memset(&g_usage_table.entries[i].data, 0, sizeof(SavedUsageEntry));
g_usage_table.entries[i].session_id = MAX_NUMBER_OF_SESSIONS;
g_usage_table.entries[i].state = USAGE_ENTRY_NOT_ACTIVE;
g_usage_table.entries[i].forbid_report = true;
g_usage_table.entries[i].recent_decrypt = false;
g_usage_table.entries[i].next_free_active_entry = i + 1;
}
g_usage_table.table_size = 0;
memset(&g_usage_table.generation_numbers, 0,
sizeof(g_usage_table.generation_numbers));
for (int i = 0; i < MAX_NUMBER_OF_USAGE_ENTRIES; i++) {
g_usage_table.active_entry_map[i].state = USAGE_ENTRY_NOT_ACTIVE;
g_usage_table.active_entry_map[i].active_entry_index =
MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES;
}
}
/* This serializes, encrypts, and signs the current header into the given
* buffer. It is an error if the buffer is not big enough. The header fields
* and the master generation number in the current header will be updated. */
static OEMCryptoResult EncryptAndSignHeader(uint8_t* header_buffer,
size_t header_buffer_length) {
if (!header_buffer) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (header_buffer_length < SignedHeaderSize(g_usage_table.table_size)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
SavedUsageHeader header = {
.file_type = USAGE_TABLE_HEADER,
.format_version = CURRENT_FILE_FORMAT_VERSION,
.master_generation_number = g_usage_table.master_generation_number,
.table_size = g_usage_table.table_size,
};
memcpy(header.generation_numbers, g_usage_table.generation_numbers,
header.table_size * sizeof(uint64_t));
SignedSavedUsageHeader signed_header = {
.file_type = SIGNED_USAGE_TABLE_HEADER,
.format_version = CURRENT_FILE_FORMAT_VERSION,
.buffer_size = PADDED_HEADER_BUFFER_SIZE,
.buffer = {0},
};
uint8_t temp_buffer[PADDED_HEADER_BUFFER_SIZE];
OEMCryptoResult result =
PackUsageHeader(temp_buffer, PADDED_HEADER_BUFFER_SIZE, &header);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO(b/158766099): encrypt header. */
memcpy(signed_header.buffer, temp_buffer, PADDED_HEADER_BUFFER_SIZE);
/* TODO(b/158766099): sign header. */
memset(signed_header.signature, 0x42, SHA256_DIGEST_LENGTH);
result = PackSignedUsageHeader(header_buffer, header_buffer_length,
&signed_header);
return result;
}
/* This decrypts and deserializes usage table header from the given buffer. The
* signature of the buffer is verified. The generation number is verified by the
* calling function. It is an error if the buffer is not big enough. */
static OEMCryptoResult DecryptAndVerifyHeader(const uint8_t* header_buffer,
size_t header_buffer_length,
SavedUsageHeader* header) {
if (!header_buffer || !header) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* TODO(b/158720996): We can't really check the buffer size because we do not
* yet know the table size. This should be done in the Unpack* functions. */
if (header_buffer_length < SignedHeaderSize(0)) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
SignedSavedUsageHeader signed_header;
OEMCryptoResult result = UnpackSignedUsageHeader(
header_buffer, header_buffer_length, &signed_header);
if (result != OEMCrypto_SUCCESS) return result;
if (signed_header.file_type != SIGNED_USAGE_TABLE_HEADER) {
/* We were given the wrong file. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signed_header.format_version != CURRENT_FILE_FORMAT_VERSION) {
/* In the future, we might handle backwards compatible versions. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signed_header.buffer_size != PADDED_HEADER_BUFFER_SIZE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* TODO(b/158766099): verify signature. */
/* TODO(b/158766099): decrypt header. */
uint8_t temp_buffer[PADDED_HEADER_BUFFER_SIZE];
memcpy(&temp_buffer, signed_header.buffer, sizeof(SavedUsageHeader));
result = UnpackUsageHeader(temp_buffer, PADDED_HEADER_BUFFER_SIZE, header);
if (result != OEMCrypto_SUCCESS) return result;
if (header->file_type != USAGE_TABLE_HEADER) {
/* We were given the wrong file. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (header->format_version != CURRENT_FILE_FORMAT_VERSION) {
/* In the future, we might handle backwards compatible versions. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
/* This serializes, encrypts, and signs the specified entry into the given
* buffer. It is an error if the buffer is not big enough. The header fields in
* the entry will be updated, but the generation number will not. */
static OEMCryptoResult EncryptAndSignEntry(SavedUsageEntry* entry,
uint8_t* entry_buffer,
size_t entry_buffer_length) {
if (!entry_buffer || !entry) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (entry_buffer_length < SignedEntrySize()) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
entry->file_type = USAGE_TABLE_ENTRY;
entry->format_version = CURRENT_FILE_FORMAT_VERSION;
SignedSavedUsageEntry signed_entry;
signed_entry.file_type = SIGNED_USAGE_TABLE_ENTRY;
signed_entry.format_version = CURRENT_FILE_FORMAT_VERSION;
signed_entry.buffer_size = PADDED_ENTRY_BUFFER_SIZE;
memset(signed_entry.buffer, 0, sizeof(signed_entry.buffer));
uint8_t temp_buffer[PADDED_ENTRY_BUFFER_SIZE];
OEMCryptoResult result =
PackUsageEntry(temp_buffer, PADDED_ENTRY_BUFFER_SIZE, entry);
if (result != OEMCrypto_SUCCESS) return result;
/* TODO: encrypt entry with device specific key. */
memcpy(signed_entry.buffer, temp_buffer, PADDED_ENTRY_BUFFER_SIZE);
/* TODO: sign entry with device specific key. */
memset(signed_entry.signature, 0x42, SHA256_DIGEST_LENGTH);
result =
PackSignedUsageEntry(entry_buffer, entry_buffer_length, &signed_entry);
return result;
}
/* This decrypts and deserializes a usage table entry from the given buffer. The
* signature of the buffer is verified. The generation number is verified by the
* calling function. If the buffer is not big enough the error
* OEMCrypto_ERROR_SHORT_BUFFER is returned. */
static OEMCryptoResult DecryptAndVerifyEntry(const uint8_t* entry_buffer,
size_t entry_buffer_length,
SavedUsageEntry* entry) {
if (!entry_buffer || !entry) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (entry_buffer_length < SignedEntrySize()) {
return OEMCrypto_ERROR_SHORT_BUFFER;
}
SignedSavedUsageEntry signed_entry;
OEMCryptoResult result =
UnpackSignedUsageEntry(entry_buffer, entry_buffer_length, &signed_entry);
if (result != OEMCrypto_SUCCESS) return result;
if (signed_entry.file_type != SIGNED_USAGE_TABLE_ENTRY) {
/* We were given the wrong file. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signed_entry.format_version != CURRENT_FILE_FORMAT_VERSION) {
/* In the future, we might handle backwards compatible versions. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (signed_entry.buffer_size != PADDED_ENTRY_BUFFER_SIZE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* TODO: verify signature with device specific key. */
/* TODO: decrypt entry with device specific key. */
uint8_t temp_buffer[PADDED_ENTRY_BUFFER_SIZE];
memcpy(&temp_buffer, signed_entry.buffer, signed_entry.buffer_size);
result = UnpackUsageEntry(temp_buffer, signed_entry.buffer_size, entry);
if (result != OEMCrypto_SUCCESS) return result;
if (entry->file_type != USAGE_TABLE_ENTRY) {
/* We were given the wrong file. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (entry->format_version != CURRENT_FILE_FORMAT_VERSION) {
/* In the future, we might handle backwards compatible versions. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
static OEMCryptoResult RollGenerationNumber(ResidentUsageEntry* entry) {
if (!entry || entry->data.index >= MAX_NUMBER_OF_USAGE_ENTRIES) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint64_t new_generation_number = g_usage_table.master_generation_number + 1;
if (!TEE_SaveGenerationNumber(new_generation_number)) {
g_usage_table_state = USAGE_TABLE_ERROR_STATE;
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
g_usage_table.master_generation_number = new_generation_number;
entry->data.generation_number++;
g_usage_table.generation_numbers[entry->data.index] =
entry->data.generation_number;
return OEMCrypto_SUCCESS;
}
// Clear out memory for the usage table.
OEMCryptoResult InitializeUsageTable(void) {
if (!TEE_PrepareGenerationNumber()) {
LOGE("Could not load generation number.");
return OEMCrypto_ERROR_INIT_FAILED;
}
ClearTable();
g_usage_table_state = USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE;
return OEMCrypto_SUCCESS;
}
// Erase data from usage table.
OEMCryptoResult TerminateUsageTable(void) {
g_usage_table_state = USAGE_TABLE_NOT_INITIALIZED;
return OEMCrypto_SUCCESS;
}
/* Create a new usage table. */
OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer,
size_t* header_buffer_length) {
if (!header_buffer_length) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
size_t size = SignedHeaderSize(0);
if (*header_buffer_length < size) {
*header_buffer_length = size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
/* Clear the table before we check the state -- we want to have an empty table
* after this function whether there was an error or not. */
ClearTable();
*header_buffer_length = size;
if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) {
/* Something went wrong. The system should give up and re-init. */
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
} else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
/* Creating a new header when one was already active. This is OK but
* rare. The system would do this to delete all the entries -- maybe on
* reprovisioning, but there are other reasons. Howver, we want to keep the
* same generation number. So we don't try to load it. */
} else if (g_usage_table_state ==
USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE) {
/* We are creating a brand new table. Try to load the generation number. */
if (!TEE_LoadGenerationNumber(&g_usage_table.master_generation_number)) {
g_usage_table_state = USAGE_TABLE_ERROR_STATE;
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
} else {
/* Only other valid state is not initialized, which is not valid for this
* function. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
g_usage_table_state = USAGE_TABLE_ACTIVE_STATE;
return EncryptAndSignHeader(header_buffer, *header_buffer_length);
}
/* Load a usage table header. */
OEMCryptoResult LoadUsageTableHeader(const uint8_t* buffer,
size_t buffer_length) {
/* Clear the table before we check the state -- we want to have an empty table
* before we load a new one, or we want an empty one if there is an error. */
ClearTable();
if (g_usage_table_state == USAGE_TABLE_ERROR_STATE) {
/* Something went wrong. The system should give up and re-init. */
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
} else if (g_usage_table_state == USAGE_TABLE_ACTIVE_STATE) {
/* Loading a header when one was already active is an indication that the
* system was going to terminate, but changed its mind - e.g. because
* delayed termination was canceled. We keep the existing generation
* numbers. */
} else if (g_usage_table_state ==
USAGE_TABLE_INITIALIZED_BUT_NOT_LOADED_STATE) {
/* We are loading a brand new table. Try to load the generation number. */
if (!TEE_LoadGenerationNumber(&g_usage_table.master_generation_number)) {
g_usage_table_state = USAGE_TABLE_ERROR_STATE;
return OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
} else {
/* Only other valid state is not initialized, which is not valid for this
* function. */
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
SavedUsageHeader header;
OEMCryptoResult result =
DecryptAndVerifyHeader(buffer, buffer_length, &header);
if (result != OEMCrypto_SUCCESS) return result;
if (g_usage_table.master_generation_number + 1 ==
header.master_generation_number ||
g_usage_table.master_generation_number - 1 ==
header.master_generation_number) {
/* Skew of 1 is a warning, but we continue on. */
result = OEMCrypto_WARNING_GENERATION_SKEW;
} else if (g_usage_table.master_generation_number !=
header.master_generation_number) {
/* Skew of more than 1 is an error. Clean the table and return an error. */
ClearTable();
/* Leave the state as active, so that the generation number is kept. It is
* not a security risk to leave an empty usage header in memory. */
g_usage_table_state = USAGE_TABLE_ACTIVE_STATE;
return OEMCrypto_ERROR_GENERATION_SKEW;
}
g_usage_table.table_size = header.table_size;
memset(g_usage_table.generation_numbers, 0,
sizeof(g_usage_table.generation_numbers));
memcpy(g_usage_table.generation_numbers, header.generation_numbers,
header.table_size * sizeof(uint64_t));
g_usage_table_state = USAGE_TABLE_ACTIVE_STATE;
return result;
}
/* Grabs a free active usage entry off of the free list of active entries. It
* updates the free list, the active_entry_map, and the session for a new
* entry. It does NOT update the entry's generation number and does NOT update
* the usage header. It does sanity checks. */
static OEMCryptoResult GrabEntry(OEMCryptoSession* session,
uint32_t usage_entry_number,
ResidentUsageEntry** entry_ptr) {
if (!session || !entry_ptr ||
usage_entry_number >= MAX_NUMBER_OF_USAGE_ENTRIES) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Check that the session doesn't have entry already. */
if (session->usage_entry_status != SESSION_HAS_NO_ENTRY) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
/* TODO(b/154764983): return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; */
}
/* Check that another session is not already using this entry. */
if (g_usage_table.active_entry_map[usage_entry_number].state !=
USAGE_ENTRY_NOT_ACTIVE) {
return OEMCrypto_ERROR_INVALID_SESSION;
}
/* Check that we have some room for a new active entry. */
if (g_usage_table.active_table_size >= MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES ||
g_usage_table.first_free_active_entry >=
MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES) {
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
/* Reserve a new entry. */
ResidentUsageEntry* entry =
&g_usage_table.entries[g_usage_table.first_free_active_entry];
/* Make sure the new entry is not already active. */
if (entry->state != USAGE_ENTRY_NOT_ACTIVE ||
g_usage_table.active_entry_map[usage_entry_number].state !=
USAGE_ENTRY_NOT_ACTIVE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Initialize the entry. */
memset(&entry->data, 0, sizeof(SavedUsageEntry));
entry->session_id = session->session_id;
entry->state = USAGE_ENTRY_ACTIVE;
/* Update the active entry map. */
g_usage_table.active_entry_map[usage_entry_number].state = USAGE_ENTRY_ACTIVE;
g_usage_table.active_entry_map[usage_entry_number].active_entry_index =
g_usage_table.first_free_active_entry;
/* Update the linked list of empty active usage entries. */
g_usage_table.first_free_active_entry = entry->next_free_active_entry;
/* Update the session. */
session->usage_entry_status = SESSION_HAS_NEW_ENTRY;
session->usage_entry_number = usage_entry_number;
*entry_ptr = entry;
return OEMCrypto_SUCCESS;
}
/** Release an active entry and put it back onto the free list. This does not
* save any data. It is usually done with the session is closing or when loading
* an entry generated an error. */
OEMCryptoResult ReleaseEntry(OEMCryptoSession* session,
uint32_t usage_entry_number) {
if (!session || usage_entry_number >= MAX_NUMBER_OF_USAGE_ENTRIES ||
g_usage_table_state != USAGE_TABLE_ACTIVE_STATE ||
session->usage_entry_number != usage_entry_number ||
session->usage_entry_status == SESSION_HAS_NO_ENTRY) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t active_entry_index =
g_usage_table.active_entry_map[usage_entry_number].active_entry_index;
if (active_entry_index >= MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Remove from session. */
session->usage_entry_status = SESSION_HAS_NO_ENTRY;
session->usage_entry_number = MAX_NUMBER_OF_USAGE_ENTRIES;
/* Remove from active entry map. */
g_usage_table.active_entry_map[usage_entry_number].state =
USAGE_ENTRY_NOT_ACTIVE;
g_usage_table.active_entry_map[usage_entry_number].active_entry_index =
MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES;
/* Clear the entry. */
memset(&g_usage_table.entries[active_entry_index].session_id, 0,
sizeof(SavedUsageEntry));
g_usage_table.entries[active_entry_index].session_id = MAX_NUMBER_OF_SESSIONS;
g_usage_table.entries[active_entry_index].state = USAGE_ENTRY_NOT_ACTIVE;
/* Add back to list of free active entries. */
g_usage_table.entries[active_entry_index].next_free_active_entry =
g_usage_table.first_free_active_entry;
g_usage_table.first_free_active_entry = active_entry_index;
return OEMCrypto_SUCCESS;
}
/* Find the active entry with the specified index into the usage table
* header. Return null if the entry number is bad or not active. */
ResidentUsageEntry* GetActiveEntry(uint32_t usage_entry_number) {
if (usage_entry_number > MAX_NUMBER_OF_USAGE_ENTRIES) {
return NULL;
}
if (g_usage_table.active_entry_map[usage_entry_number].state !=
USAGE_ENTRY_ACTIVE ||
g_usage_table.active_entry_map[usage_entry_number].active_entry_index >=
MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES) {
return NULL;
}
uint32_t index =
g_usage_table.active_entry_map[usage_entry_number].active_entry_index;
ResidentUsageEntry* entry = &g_usage_table.entries[index];
if (entry->state != USAGE_ENTRY_ACTIVE ||
entry->data.index != usage_entry_number) {
return NULL;
}
return entry;
}
/* Create a new usage entry and tie it to |session|. The new entry will have an
* entry number at the end of the array of all entries in the header, but it
* could be anywhere in the array of active entries. */
OEMCryptoResult CreateNewUsageEntry(OEMCryptoSession* session,
uint32_t* usage_entry_number) {
if (!session || !usage_entry_number) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Check that the header can grow. */
if (g_usage_table.table_size >= MAX_NUMBER_OF_USAGE_ENTRIES) {
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
uint32_t new_index = g_usage_table.table_size;
ResidentUsageEntry* entry = NULL;
OEMCryptoResult result = GrabEntry(session, new_index, &entry);
if (result != OEMCrypto_SUCCESS) return result;
g_usage_table.table_size++;
entry->data.index = new_index;
/* mark session as having new entry. */
session->usage_entry_status = SESSION_HAS_NEW_ENTRY;
/* Update the generation numbers. Increment the master GN, and then copy to
* the entry. Also copy to the header's array of entries. */
g_usage_table.master_generation_number++;
entry->data.generation_number = g_usage_table.master_generation_number;
g_usage_table.generation_numbers[new_index] =
g_usage_table.master_generation_number;
*usage_entry_number = new_index;
return OEMCrypto_SUCCESS;
}
/* Load a usage entry that had been saved to the file system and tie it to
* |session|. */
OEMCryptoResult LoadUsageEntry(OEMCryptoSession* session,
uint32_t usage_entry_number,
const uint8_t* buffer, size_t buffer_length) {
if (!session) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
ResidentUsageEntry* entry = NULL;
OEMCryptoResult result = GrabEntry(session, usage_entry_number, &entry);
if (result != OEMCrypto_SUCCESS) return result;
/* Load the entry. */
result = DecryptAndVerifyEntry(buffer, buffer_length, &entry->data);
if (result != OEMCrypto_SUCCESS) return result;
if (entry->data.index != usage_entry_number) {
entry = NULL;
ReleaseEntry(session, usage_entry_number);
return OEMCrypto_ERROR_INVALID_SESSION;
}
/* check generation number against header's table of generation numbers. */
uint64_t entry_gn = entry->data.generation_number;
uint64_t header_gn = g_usage_table.generation_numbers[usage_entry_number];
if (entry_gn + 1 == header_gn || entry_gn - 1 == header_gn) {
/* Skew of 1 is a warning, but we continue on. */
result = OEMCrypto_WARNING_GENERATION_SKEW;
} else if (entry_gn != header_gn) {
/* Skew of more than 1 is an error. Clean the table and return an error. */
entry = NULL;
ReleaseEntry(session, usage_entry_number);
return OEMCrypto_ERROR_GENERATION_SKEW;
}
/* mark session as having loaded entry or deactivated entry. */
if (entry->data.status == kActive || entry->data.status == kUnused) {
session->usage_entry_status = SESSION_HAS_LOADED_ENTRY;
} else {
session->usage_entry_status = SESSION_HAS_DEACTIVATED_ENTRY;
}
return result;
}
OEMCryptoResult UpdateUsageEntry(OEMCryptoSession* session,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length) {
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!header_buffer_length || !entry_buffer_length) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Check the size and let caller know if it's a short buffer. */
size_t header_size = SignedHeaderSize(g_usage_table.table_size);
size_t entry_size = SignedEntrySize();
if (*header_buffer_length < header_size ||
*entry_buffer_length < entry_size) {
*header_buffer_length = header_size;
*entry_buffer_length = entry_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*header_buffer_length = header_size;
*entry_buffer_length = entry_size;
/* Check that the session has an entry, and it's state is valid. */
uint32_t index = session->usage_entry_number;
ResidentUsageEntry* entry = GetActiveEntry(index);
if (!entry ||
(session->usage_entry_status != SESSION_HAS_NEW_ENTRY &&
session->usage_entry_status != SESSION_HAS_LOADED_ENTRY &&
session->usage_entry_status != SESSION_HAS_DEACTIVATED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* The new generation numbers. */
RollGenerationNumber(entry);
OEMCryptoResult result =
EncryptAndSignHeader(header_buffer, *header_buffer_length);
if (result != OEMCrypto_SUCCESS) return result;
result =
EncryptAndSignEntry(&entry->data, entry_buffer, *entry_buffer_length);
if (result == OEMCrypto_SUCCESS) {
entry->forbid_report = false;
entry->recent_decrypt = false;
}
return result;
}
OEMCryptoResult UpdateLastPlaybackTime(const OEMCryptoSession* session) {
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
ResidentUsageEntry* entry = GetActiveEntry(session->usage_entry_number);
if (!entry || (session->usage_entry_status != SESSION_HAS_NEW_ENTRY &&
session->usage_entry_status != SESSION_HAS_LOADED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint64_t now;
OEMCrypto_Clock_Security_Level clock_type;
OEMCryptoResult result = GetSystemTime(&now, &clock_type);
if (result != OEMCrypto_SUCCESS) return result;
entry->recent_decrypt = true;
entry->data.time_of_last_decrypt = now;
if (entry->data.status == kUnused) {
/* This is the first playback. */
entry->data.status = kActive;
entry->forbid_report = true;
entry->data.time_of_first_decrypt = now;
} else if (entry->data.status != kActive) {
/* License is inactive. Playback should be forbidden. */
return OEMCrypto_ERROR_KEY_EXPIRED;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult SetUsageEntryPST(OEMCryptoSession* session, const uint8_t* pst,
size_t pst_length) {
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
ResidentUsageEntry* entry = GetActiveEntry(session->usage_entry_number);
if (!entry || (session->usage_entry_status != SESSION_HAS_NEW_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!pst || pst_length == 0 || pst_length > MAX_PST_LENGTH) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
memcpy(entry->data.pst, pst, pst_length);
entry->data.pst_length = pst_length;
/* The PST is set when the license is loaded. This will be removed in v16 when
* we use the time of license signed instead of time of license loaded. */
uint64_t now;
OEMCrypto_Clock_Security_Level clock_type;
OEMCryptoResult result = GetSystemTime(&now, &clock_type);
if (result != OEMCrypto_SUCCESS) return result;
entry->data.time_of_license_received = now;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult VerfiyUsageEntryPST(OEMCryptoSession* session,
const uint8_t* pst, size_t pst_length) {
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
ResidentUsageEntry* entry = GetActiveEntry(session->usage_entry_number);
if (!entry || (session->usage_entry_status != SESSION_HAS_LOADED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!pst || pst_length == 0 || pst_length > MAX_PST_LENGTH) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (entry->data.pst_length != pst_length ||
memcmp(entry->data.pst, pst, pst_length)) {
return OEMCrypto_ERROR_WRONG_PST;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult SetUsageEntryMacKeys(const OEMCryptoSession* session) {
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
ResidentUsageEntry* entry = GetActiveEntry(session->usage_entry_number);
if (!entry || (session->usage_entry_status != SESSION_HAS_NEW_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result;
uint8_t wrapped_server_mac[WRAPPED_MAC_KEY_SIZE];
uint8_t wrapped_client_mac[WRAPPED_MAC_KEY_SIZE];
memset(wrapped_server_mac, 0, WRAPPED_MAC_KEY_SIZE);
memset(wrapped_client_mac, 0, WRAPPED_MAC_KEY_SIZE);
result = WrapCryptoKey(session->mac_key_server, wrapped_server_mac,
WRAPPED_MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
result = WrapCryptoKey(session->mac_key_client, wrapped_client_mac,
WRAPPED_MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
memcpy(entry->data.mac_key_server, wrapped_server_mac, WRAPPED_MAC_KEY_SIZE);
memcpy(entry->data.mac_key_client, wrapped_client_mac, WRAPPED_MAC_KEY_SIZE);
return OEMCrypto_SUCCESS;
}
OEMCryptoResult VerifysageEntryMacKeys(const OEMCryptoSession* session) {
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
ResidentUsageEntry* entry = GetActiveEntry(session->usage_entry_number);
if (!entry || (session->usage_entry_status != SESSION_HAS_LOADED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result;
uint8_t wrapped_server_mac[WRAPPED_MAC_KEY_SIZE];
uint8_t wrapped_client_mac[WRAPPED_MAC_KEY_SIZE];
memset(wrapped_server_mac, 0, WRAPPED_MAC_KEY_SIZE);
memset(wrapped_client_mac, 0, WRAPPED_MAC_KEY_SIZE);
result = WrapCryptoKey(session->mac_key_server, wrapped_server_mac,
WRAPPED_MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
result = WrapCryptoKey(session->mac_key_client, wrapped_client_mac,
WRAPPED_MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
if (memcmp(entry->data.mac_key_server, wrapped_server_mac,
WRAPPED_MAC_KEY_SIZE) ||
memcmp(entry->data.mac_key_client, wrapped_client_mac,
WRAPPED_MAC_KEY_SIZE)) {
return OEMCrypto_ERROR_WRONG_PST;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult DeactivateUsageEntry(OEMCryptoSession* session,
const uint8_t* pst, size_t pst_length) {
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* Check that the session has an entry, and it's state is valid. */
uint32_t index = session->usage_entry_number;
ResidentUsageEntry* entry = GetActiveEntry(index);
if (!entry ||
(session->usage_entry_status != SESSION_HAS_NEW_ENTRY &&
session->usage_entry_status != SESSION_HAS_LOADED_ENTRY &&
session->usage_entry_status != SESSION_HAS_DEACTIVATED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
session->usage_entry_status = SESSION_HAS_DEACTIVATED_ENTRY;
if (entry->data.status == kUnused) {
entry->data.status = kInactiveUnused;
} else if (entry->data.status == kActive) {
entry->data.status = kInactiveUsed;
}
RollGenerationNumber(entry);
entry->forbid_report = true;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ReportUsage(OEMCryptoSession* session, const uint8_t* pst,
size_t pst_length, uint8_t* buffer,
size_t* buffer_length) {
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t index = session->usage_entry_number;
ResidentUsageEntry* entry = GetActiveEntry(index);
if (!entry ||
(session->usage_entry_status != SESSION_HAS_NEW_ENTRY &&
session->usage_entry_status != SESSION_HAS_LOADED_ENTRY &&
session->usage_entry_status != SESSION_HAS_DEACTIVATED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
size_t length_needed = sizeof(OEMCrypto_PST_Report) + pst_length;
if (*buffer_length < length_needed) {
*buffer_length = length_needed;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*buffer_length = length_needed;
if (entry->forbid_report || entry->recent_decrypt) {
return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE;
}
if (pst_length == 0 || pst_length > MAX_PST_LENGTH ||
pst_length != entry->data.pst_length) {
return OEMCrypto_ERROR_WRONG_PST;
}
if (memcmp(entry->data.pst, pst, pst_length)) {
return OEMCrypto_ERROR_WRONG_PST;
}
uint64_t now;
OEMCrypto_Clock_Security_Level clock_type;
OEMCryptoResult result = GetSystemTime(&now, &clock_type);
if (result != OEMCrypto_SUCCESS) return result;
OEMCrypto_PST_Report* report = (OEMCrypto_PST_Report*)buffer;
report->seconds_since_license_received =
htonll64(now - entry->data.time_of_license_received);
report->seconds_since_first_decrypt =
htonll64(now - entry->data.time_of_first_decrypt);
report->seconds_since_last_decrypt =
htonll64(now - entry->data.time_of_last_decrypt);
report->status = entry->data.status;
report->clock_security_level = clock_type;
report->pst_length = pst_length;
memcpy(report->pst, pst, pst_length);
if (CheckKey(session->mac_key_client, MAC_KEY_CLIENT, MAC_KEY_CLIENT_SIGN)) {
/* If the session has mac keys, use those. */
result = HMAC_SHA1(session->mac_key_client->key_handle,
buffer + SHA_DIGEST_LENGTH,
length_needed - SHA_DIGEST_LENGTH, report->signature);
} else {
/* Otherwise, we use the mac key from the entry. */
CryptoKey key;
result = InitializeCryptoKeyFromWrappedKey(
&key, entry->data.mac_key_client, WRAPPED_MAC_KEY_SIZE, MAC_KEY_CLIENT,
MAC_KEY_CLIENT_SIGN, MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
result = HMAC_SHA1(key.key_handle, buffer + SHA_DIGEST_LENGTH,
length_needed - SHA_DIGEST_LENGTH, report->signature);
if (OEMCrypto_SUCCESS != FreeCryptoKey(&key)) {
if (OEMCrypto_SUCCESS != result) return result;
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
return result;
}
OEMCryptoResult SignReleaseRequest(OEMCryptoSession* session,
const uint8_t* message,
size_t message_length, uint8_t* signature,
size_t* signature_length) {
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t index = session->usage_entry_number;
ResidentUsageEntry* entry = GetActiveEntry(index);
if (!entry ||
(session->usage_entry_status != SESSION_HAS_NEW_ENTRY &&
session->usage_entry_status != SESSION_HAS_LOADED_ENTRY &&
session->usage_entry_status != SESSION_HAS_DEACTIVATED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
CryptoKey key;
OEMCryptoResult result = InitializeCryptoKeyFromWrappedKey(
&key, entry->data.mac_key_client, WRAPPED_MAC_KEY_SIZE, MAC_KEY_CLIENT,
MAC_KEY_CLIENT_SIGN, MAC_KEY_SIZE);
if (result != OEMCrypto_SUCCESS) return result;
result = HMAC_SHA256(key.key_handle, message, message_length, signature);
if (OEMCrypto_SUCCESS != FreeCryptoKey(&key)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return result;
}
OEMCryptoResult MoveEntry(OEMCryptoSession* session, uint32_t new_index) {
if (!session) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint32_t index = session->usage_entry_number;
ResidentUsageEntry* entry = GetActiveEntry(index);
if (!entry ||
(session->usage_entry_status != SESSION_HAS_NEW_ENTRY &&
session->usage_entry_status != SESSION_HAS_LOADED_ENTRY &&
session->usage_entry_status != SESSION_HAS_DEACTIVATED_ENTRY)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (g_usage_table.active_entry_map[new_index].state !=
USAGE_ENTRY_NOT_ACTIVE ||
g_usage_table.active_entry_map[new_index].active_entry_index !=
MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES) {
return OEMCrypto_ERROR_ENTRY_IN_USE;
}
/* Copy data from index to new_index. */
entry->data.index = new_index;
g_usage_table.active_entry_map[new_index].state = USAGE_ENTRY_ACTIVE;
g_usage_table.active_entry_map[new_index].active_entry_index =
g_usage_table.active_entry_map[index].active_entry_index;
session->usage_entry_number = new_index;
/* Update entry's generation number to be max generation number. */
entry->data.generation_number = g_usage_table.master_generation_number;
g_usage_table.generation_numbers[new_index] =
g_usage_table.master_generation_number;
/* Mark old index as unused. */
g_usage_table.active_entry_map[index].state = USAGE_ENTRY_NOT_ACTIVE;
g_usage_table.active_entry_map[index].active_entry_index =
MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ShrinkUsageTableHeader(uint32_t new_entry_count,
uint8_t* header_buffer,
size_t* header_buffer_length) {
if (g_usage_table_state != USAGE_TABLE_ACTIVE_STATE) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (new_entry_count >= g_usage_table.table_size) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
for (uint32_t i = new_entry_count; i < g_usage_table.table_size; i++) {
if (g_usage_table.active_entry_map[i].state != USAGE_ENTRY_NOT_ACTIVE) {
return OEMCrypto_ERROR_ENTRY_IN_USE;
}
}
size_t header_size = SignedHeaderSize(new_entry_count);
if (header_size > *header_buffer_length) {
*header_buffer_length = header_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
for (uint32_t i = new_entry_count; i < g_usage_table.table_size; i++) {
g_usage_table.active_entry_map[i].state = USAGE_ENTRY_NOT_ACTIVE;
g_usage_table.active_entry_map[i].active_entry_index =
MAX_NUMBER_OF_ACTIVE_USAGE_ENTRIES;
g_usage_table.generation_numbers[i] = 0;
}
g_usage_table.table_size = new_entry_count;
return EncryptAndSignHeader(header_buffer, *header_buffer_length);
}

View File

@@ -0,0 +1,142 @@
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
source code may only be used and distributed under the Widevine Master
License Agreement. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_USAGE_TABLE_H_
#define OEMCRYPTO_TA_OEMCRYPTO_USAGE_TABLE_H_
#include "stdbool.h"
#include "stdint.h"
#include "OEMCryptoCENC.h"
#include "oemcrypto_config_macros.h"
#include "oemcrypto_key_types.h"
#include "oemcrypto_session.h"
/**
* Clear out memory for the usage table. No other usage table functions may be
* called before this. */
OEMCryptoResult InitializeUsageTable(void);
/**
* Erase data from usage table. No other usage table functions may be called
* without calling InitializeUsageTable. */
OEMCryptoResult TerminateUsageTable(void);
/** Create a new empty usage table header. */
OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer,
size_t* header_buffer_length);
/** Load a usage table header. */
OEMCryptoResult LoadUsageTableHeader(const uint8_t* buffer,
size_t buffer_length);
/**
* Create a new usage table entry and attach it to the |session|. This may
* return an error if the usage table is full, or if too many open sessions have
* active usage entries. |session| must be open and not already have an entry
* associated with it.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult CreateNewUsageEntry(OEMCryptoSession* session,
uint32_t* usage_entry_number);
/**
* Load a usage table entry and attach it to the |session|. This may return an
* error if too many open sessions have active usage entries. |session| must be
* open and not already have an entry associated with it. The usage_entry_number
* must match that in the loaded entry.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult LoadUsageEntry(OEMCryptoSession* session,
uint32_t usage_entry_number,
const uint8_t* buffer, size_t buffer_length);
/**
* Release the active usage entry associated with |session|.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult ReleaseEntry(OEMCryptoSession* session,
uint32_t usage_entry_number);
/**
* Update all values in the usage entry associated with |session|. After
* updating values, the generation numbers are all updated and the master
* generation number is saved to persistent storage. Then the entry and the
* usage table header are saved to the specified buffer. If the buffer lengths
* are not large enough, none of the work above is completed -- instead the
* lengths are updated and OEMCrypto_ERROR_SHORT_BUFFER is returned.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult UpdateUsageEntry(OEMCryptoSession* session,
uint8_t* header_buffer,
size_t* header_buffer_length,
uint8_t* entry_buffer,
size_t* entry_buffer_length);
/**
* Update the playback times in the usage entry attached to |session|.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult UpdateLastPlaybackTime(const OEMCryptoSession* session);
/**
* Set the provider session token in the usage entry associated with |session|.
* This is done when a license is first loaded.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult SetUsageEntryPST(OEMCryptoSession* session, const uint8_t* pst,
size_t pst_length);
/**
* Verify the provider session token in the usage entry associated with
* |session|. This is done when a license is reloaded to verify the license
* matches the usage entry.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult VerfiyUsageEntryPST(OEMCryptoSession* session,
const uint8_t* pst, size_t pst_length);
/**
* Set the mac keys in the usage entry associated with |session|.
* This is done when a license is first loaded.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult SetUsageEntryMacKeys(const OEMCryptoSession* session);
/**
* Verify the mac keys in the usage entry associated with
* |session|. This is done when a license is reloaded to verify the license
* matches the usage entry.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult VerifysageEntryMacKeys(const OEMCryptoSession* session);
/**
* Mark the usage entry associated with |session| as deactivated. After this,
* the license may not be used to decrypt content.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult DeactivateUsageEntry(OEMCryptoSession* session,
const uint8_t* pst, size_t pst_length);
/**
* Generate a usage report from the entry associated with |session|.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult ReportUsage(OEMCryptoSession* session, const uint8_t* pst,
size_t pst_length, uint8_t* buffer,
size_t* buffer_length);
/**
* Sign |buffer| with the client mac key in the entry associated with |session|.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult SignReleaseRequest(OEMCryptoSession* session,
const uint8_t* message,
size_t message_length, uint8_t* signature,
size_t* signature_length);
/**
* Move the usage entry associated with |session| to the new index in the usage
* table header. The generation numbers are updated as specified in the
* OEMCrypto spec.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult MoveEntry(OEMCryptoSession* session, uint32_t new_index);
/**
* Shrink the usage table to the size specified.
* Pointers must be non-null and are owned by the caller. */
OEMCryptoResult ShrinkUsageTableHeader(uint32_t new_entry_count,
uint8_t* header_buffer,
size_t* header_buffer_length);
#endif /* OEMCRYPTO_TA_OEMCRYPTO_USAGE_TABLE_H_ */

View File

@@ -0,0 +1,19 @@
/* 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. */
#ifndef OEMCRYPTO_TA_ASSERT_INTERFACE_H_
#define OEMCRYPTO_TA_ASSERT_INTERFACE_H_
#include "logging_interface.h"
/* Abort the program execution. */
void Abort(void);
#define ASSERT(expression, ...) \
if (!(expression)) { \
LOGE(__VA_ARGS__); \
Abort(); \
}
#endif /* OEMCRYPTO_TA_ASSERT_INTERFACE_H_ */

View File

@@ -0,0 +1,42 @@
/* 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. */
#ifndef OEMCRYPTO_TA_CLOCK_INTERFACE_H_
#define OEMCRYPTO_TA_CLOCK_INTERFACE_H_
#include "stdint.h"
#include "OEMCryptoCENC.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Calculates the system time at the time of call and modifies |time_in_s|.
TODO(b/158719238): This needs some design work. We should wrap this in
something that can upgrade the clock type to being monotonic over reboot.
TODO(b/158719238): Reword this paragraph to say what the real requirements
are:
This time does not need to be strictly increasing across
device reboots, but must be strictly increasing between calls to
OEMCrypto_Initialize and OEMCrypto_Terminate. This does not need to be time
since epoch. This does not need to start at 0 on boot.
If clock_type is not null, set *clock_type to the type of clock, as defined
in the OEMCrypto spec.
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE on failure and OEMCrypto_SUCCESS on
success.
Returns OEMCrypto_ERROR_UNKNOWN_FAILURE if time_in_s is a null pointer.
Caller retains ownership of all pointers. */
OEMCryptoResult GetSystemTime(uint64_t* time_in_s,
OEMCrypto_Clock_Security_Level* clock_type);
#ifdef __cplusplus
}
#endif
#endif /* OEMCRYPTO_TA_CLOCK_INTERFACE_H_ */

View File

@@ -0,0 +1,189 @@
/* 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. */
#ifndef OEMCRYPTO_TA_CRYPTO_INTERFACE_H_
#define OEMCRYPTO_TA_CRYPTO_INTERFACE_H_
#include "OEMCryptoCENC.h"
#include "oemcrypto_key_types.h"
typedef struct tee_key_handle* TEE_Key_Handle;
/* Creates a key handle from |size| bytes of |serialized_bytes| and the
|key_type|, and places the result in |key_handle|. Returns
OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL, size is 0,
or |key_type| is UNKNOWN_KEY_TYPE, OEMCrypto_ERROR_INVALID_RSA_KEY if the
key type is DRM_PRIVATE_RSA_KEY and |serialized_bytes| is an invalid PKCS8
RSA private key, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other
failures, and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all parameters. */
OEMCryptoResult CreateKeyHandle(const uint8_t* serialized_bytes, uint32_t size,
CryptoKeyType key_type,
TEE_Key_Handle* key_handle);
/* Creates a key handle from |size| bytes of |wrapped_key| and the
|key_type|, and places the result in |key_handle|. Returns
OEMCrypto_ERROR_INVALID_CONTEXT if any of the parameters are NULL, size is 0,
or |key_type| is UNKNOWN_KEY_TYPE, OEMCrypto_ERROR_INVALID_RSA_KEY if the
key type is DRM_PRIVATE_RSA_KEY and |serialized_bytes| is an invalid PKCS8
RSA private key, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other
failures, and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all parameters. */
OEMCryptoResult UnwrapIntoKeyHandle(const uint8_t* wrapped_key, uint32_t size,
CryptoKeyType key_type,
TEE_Key_Handle* key_handle);
/* Frees |key_handle| that was constructed from a previous call to
CreateKeyHandle. Returns OEMCrypto_ERROR_INVALID_CONTEXT if |key_handle| is
NULL, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult FreeKeyHandle(TEE_Key_Handle key_handle);
/* Wraps the key data into a buffer that can be saved to the file system. The
wrapping must be device unique. Caller retains ownership of |key| and
|buffer| and they must not be NULL. Caller ensures that buffer_length is at
least as big as the wrapped key size specified in
oemcrypto_config_macros.h. */
OEMCryptoResult WrapKey(uint8_t* wrapped_key, uint32_t size,
CryptoKeyType key_type, TEE_Key_Handle key_handle);
/* AES decryption for 1 block - 16 bytes. |key| is a handle to the AES key used
for decryption. It is used to decrypt |in| and place the result in |out|.
|in| and |out| must be >= 16 bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult AESDecrypt(TEE_Key_Handle key, const uint8_t* in, uint8_t* out);
/* AES encryption for 1 block - 16 bytes. |key| is a handle to the AES key used
for encryption. It is used to encrypt |in| and place the result in |out|.
|in| and |out| must be >= 16 bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult AESEncrypt(TEE_Key_Handle key, const uint8_t* in, uint8_t* out);
/* Decrypts |in_length| bytes of |in| using AES CBC and |iv| and places the
result in |out|. |key| is a handle to the AES key used for decryption and
|key_length| determines the number of bytes to use from |key|. |out| must be
>= |in_length| bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL,
|key_length| is not either 16 or 32 bytes, |key_length| is greater than the
size of |key|, |in_length| is 0 or not a multiple of the AES block size,
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult AESCBCDecrypt(TEE_Key_Handle key, uint32_t key_length,
const uint8_t* in, uint32_t in_length,
const uint8_t* iv, uint8_t* out);
/* Encrypts |in_length| bytes of |in| using AES CBC and |iv| and places the
result in |out|. |key| is a handle to the AES key used for encryption. |out|
must be >= |in_length| bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL,
|in_length| is 0 or not a multiple of the AES block size,
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult AESCBCEncrypt(TEE_Key_Handle key, const uint8_t* in,
uint32_t in_length, const uint8_t* iv,
uint8_t* out);
/* Calculates the HMAC of |message_length| bytes of |message| using SHA256 as
the hash function and places the result in |out| and sets |out_length| to the
correct length of the HMAC. |key| is a handle to the key used in the
derivation.
|out|'s length must be >= 32 bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|message_length| is 0, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other
failures, and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult HMAC_SHA256(TEE_Key_Handle key, const uint8_t* message,
uint32_t message_length, uint8_t* out);
/* Calculates the HMAC of |message_length| bytes of |message| using SHA1 as the
hash function and places the result in |out|. |key| is a handle to the key
used in the derivation.
|out|'s length must be >= 20 bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|message_length| is 0, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other
failures, and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult HMAC_SHA1(TEE_Key_Handle key, const uint8_t* message,
uint32_t message_length, uint8_t* out);
/* Sign |message_length| bytes of |message| with the given RSA key handle using
the given |padding scheme| and place the result in |signature|.
|key| is a handle to the RSA key used for signing.
Returns OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if
|signature| is NULL, in which case it sets |signature_length| to the
appropriate length. Returns OEMCrypto_ERROR_INVALID_CONTEXT if
|message_length| is 0 or if any of the pointers except |signature| are NULL,
OEMCrypto_ERROR_INVALID_RSA_KEY if the padding_scheme provided is not
supported, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures,
and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult RSASign(TEE_Key_Handle key, const uint8_t* message,
uint32_t message_length, uint8_t* signature,
uint32_t* signature_length,
RSA_Padding_Scheme padding_scheme);
/* Decrypts |in_length| bytes of |in| and places it in |out|. The padding scheme
shall only be PKCS1 OAEP. |key| is a handle to the RSA key used for
decryption.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|in_length| is 0, OEMCrypto_ERROR_SHORT_BUFFER if |out_length| is too small,
in which case it sets |out_length| to the appropriate length,
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult RSADecrypt(TEE_Key_Handle key, const uint8_t* in,
uint32_t in_length, uint8_t* out,
uint32_t* out_length);
/* The device key is used to wrap and unwrap the DRM RSA key. It should be
separate from the key tied to the Widevine root of trust if possible.
Derives a 16 byte key from the device key and |context_length| bytes of
|context| using AES-128-CMAC. Prepends |counter| to context in the
derivation. Modifies |out| to the correct value.
|out| must be >= 16 bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|context_length| is 0, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other
failures, and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult DeriveKeyFromDeviceKey(uint8_t counter, const uint8_t* context,
uint32_t context_length, uint8_t* out);
/* Identical to the previous function, except that it uses |key| to derive a
key. */
OEMCryptoResult DeriveKeyFromKeyHandle(TEE_Key_Handle key, uint8_t counter,
const uint8_t* context,
uint32_t context_length, uint8_t* out);
/* Generates |size| random bytes and places them in |out|. Returns
OEMCrypto_ERROR_BUFFER_TOO_LARGE if |size| is too big,
OEMCrypto_ERROR_UNKNOWN_FAILURE or any other failures, and OEMCrypto_SUCCESS
otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult RandomBytes(uint8_t* out, uint32_t size);
/* Initializes the 32-bit |initial_hash| to the starting CRC-32 value.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if |initial_hash| is NULL and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult Crc32Init(uint32_t* initial_hash);
/* Calculates the new crc-32 value given |in_length| bytes of |in| and the
previous CRC-32 value, |prev_crc|. Places the result in |new_crc|.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any pointers are NULL or
|in_length| is 0 and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult Crc32Cont(const uint8_t* in, uint32_t in_length,
uint32_t prev_crc, uint32_t* new_crc);
#endif /* OEMCRYPTO_TA_CRYPTO_INTERFACE_H_ */

View File

@@ -0,0 +1,64 @@
/* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
source code may only be used and distributed under the Widevine Master
License Agreement. */
#ifndef OEMCRYPTO_TA_GENERATION_NUMBER_INTERFACE_H_
#define OEMCRYPTO_TA_GENERATION_NUMBER_INTERFACE_H_
#include <stdint.h>
#include "OEMCryptoCENC.h"
/**
* Prepare to load the generation number. If the generation number is loaded
* asynchronously, this should initialize that process so that the next call to
* TEE_LoadGenerationNumber does not block for too long.
*
* TODO(b/160022428): define and document "too long".
*
* The generation number should be stored in secure persistent storage. By
* *persistent* we mean that the generation number should not be changed by
* shutting down and later restarting the system. By *secure* we mean that the
* user should not be able to modify the generation number. In particular, the
* user should not be able to revert the generation number to a previous value.
*
* Returns true on success. On failure, initialization of the TA will fail.
*/
bool TEE_PrepareGenerationNumber(void);
/**
* Load the usage table generation number. This is called once, on first use of
* the usage table. This is expected to block until a value is available. It is
* the porting interface's responsibility to fail if this blocks too
* long. Returns true on success. A return value of false will fail OEMCrypto
* initialization. If the generation number has never been saved before, a
* random number should be generated -- this is NOT an error.
*/
bool TEE_LoadGenerationNumber(uint64_t* value);
/**
* Save the generation number.
*
* If the generation number is saved asynchronously, then it is OK for the first
* call to this function to begin a save process and then return true.
* Subsequent calls will verify that the previous save has completed
* successfully. If the previous save resulted in an error, then this call will
* return false. If the previous call has not completed, then this call should
* block until the previous save finishes before initializing a new save. If the
* previous call was successful, then this call will initialize a new save and
* return true. It is the porting interface's responsibility to fail if this
* blocks too long.
*
* If the generation number is saved synchronously, then this function should
* attempt to save the generation number and return true if the save was
* successful.
*
* Returns true on success. A return value of false will fail the current
* attempt to save the usage table. If a failure actually indicates that the
* previous save had failed, then the usage table will be saved with a
* generation number skew of 1. When the usage table is loaded with a generation
* skew of 1, it will result in a warning, but not a failure.
*/
bool TEE_SaveGenerationNumber(uint64_t value);
#endif /* OEMCRYPTO_TA_GENERATION_NUMBER_INTERFACE_H_ */

View File

@@ -0,0 +1,18 @@
/* 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. */
#ifndef OEMCRYPTO_TA_INITIALIZE_TERMINATE_INTERFACE_H_
#define OEMCRYPTO_TA_INITIALIZE_TERMINATE_INTERFACE_H_
/* Set up for any work needed for initializing the TA-specific components of the
TEE. Returns 0 on success and any other int, which will be logged, on
failure. */
int Initialize(void);
/* Set up for any work needed for terminating the TA-specific components of the
TEE. Returns 0 on success and any other int, which will be logged, on
failure. */
int Terminate(void);
#endif /* OEMCRYPTO_TA_INITIALIZE_TERMINATE_INTERFACE_H_ */

View File

@@ -0,0 +1,21 @@
/* 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. */
#ifndef OEMCRYPTO_TA_LOGGING_INTERFACE_H_
#define OEMCRYPTO_TA_LOGGING_INTERFACE_H_
typedef enum LogPriority {
LOG_ERROR = 0x1098fa73,
LOG_DEBUG = 0x2b898c5a,
} LogPriority;
extern LogPriority g_cutoff;
void Log(const char* file, const char* function, int line, LogPriority level,
const char* fmt, ...);
#define LOGE(...) Log(__FILE__, __func__, __LINE__, LOG_ERROR, __VA_ARGS__)
#define LOGD(...) Log(__FILE__, __func__, __LINE__, LOG_DEBUG, __VA_ARGS__)
#endif /* OEMCRYPTO_TA_LOGGING_INTERFACE_H_ */

View File

@@ -0,0 +1,84 @@
/* 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. */
#ifndef OEMCRYPTO_TA_OEMCRYPTO_CONFIG_INTERFACE_H_
#define OEMCRYPTO_TA_OEMCRYPTO_CONFIG_INTERFACE_H_
#include "OEMCryptoCENC.h"
#include "oemcrypto_config_macros.h"
/* Returns the provisioning method configured for this TA. */
OEMCrypto_ProvisioningMethod GetProvisioningMethod(void);
/* Returns the resource rating tier associated with this device. */
uint32_t GetResourceRatingTier(void);
/* Returns an xor of all the padding schemes allowed by this device. */
uint32_t GetRSAPaddingSchemes(void);
/* Gets the current supported version of SRM for the device and sets the
|srm_version|. Returns OEMCrypto_SUCCESS if it was able to be fetched,
OEMCrypto_ERROR_INVALID_CONTEXT if |srm_version| is NULL, any
OEMCrypto_ERROR_UNKNOWN_FAILURE otherwise. */
OEMCryptoResult GetCurrentSRMVersion(uint32_t* srm_version);
/* Returns whether the device has hardware protection preventing rollback of the
usage table. */
bool IsAntiRollbackHWPresent(void);
/* Returns whether or not the device was able to apply the CGMS protection for
the device. The |cgms_field| correlates to those under the Key Control Block
description in the OEMCrypto doc. If the cgms_field is invalid, return
OEMCrypto_ERROR_UNKNOWN_FAILURE. Even if this function is not called, the
device should attempt best effort for CGMS. */
OEMCryptoResult ApplyCGMS(uint8_t cgms_field);
/* Returns whether CGMS is enabled for analog output for this device. */
bool IsCGMS_AActive(void);
/* Returns whether this device is capable of supporting 2-bit CGMS-A. */
bool SupportsCGMS_A(void);
/* Returns whether the device is capable of analog display. */
bool HasAnalogDisplay(void);
/* Returns whether analog display is enabled for this display. */
bool IsAnalogDisplayActive(void);
/* Returns whether the analog display is capable of being disabled. If this
device doesn't have analog display, return false. */
bool CanDisableAnalogDisplay(void);
/* Turn off analog display and return whether it was successful. If this device
doesn't have analog display, return false. */
bool DisableAnalogDisplay(void);
/* Returns the max buffer size/max subsample size in bytes allowed for
DecryptCENC. If there is no restriction, returns 0. */
uint32_t MaxBufferSizeForDecrypt(void);
/* Returns the max output size in bytes allowed for DecryptCENC and CopyBuffer.
If there is no restriction, returns 0. */
uint32_t MaxOutputSizeForDecrypt(void);
/* A closed platform can use clear buffers during decryption. */
/* TODO(b/145245387): define what constitutes a closed platform. */
bool IsClosedPlatform(void);
/* Returns the current and maximum HDCP capabilities of the device. Look at the
OEMCrypto integration guide for full details on what the current and maximum
capabilities entail. */
OEMCrypto_HDCP_Capability CurrentHDCPCapability(void);
OEMCrypto_HDCP_Capability MaxHDCPCapability(void);
/* Returns the max buffer size allowed for OEMCrypto_Generic_*.
If there is no restriction, returns 0. */
uint32_t MaxBufferSizeForGenericCrypto(void);
/* Returns the type of certificates this device can support. See
OEMCrypto_SupportedCertificates in the integration guide for details on
return value. */
uint32_t SupportedCertificates(void);
#endif /* OEMCRYPTO_TA_OEMCRYPTO_CONFIG_INTERFACE_H_ */

View File

@@ -0,0 +1,114 @@
/* 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. */
#ifndef OEMCRYPTO_TA_ROOT_OF_TRUST_INTERFACE_H_
#define OEMCRYPTO_TA_ROOT_OF_TRUST_INTERFACE_H_
#include "stddef.h"
#include "stdint.h"
#include "OEMCryptoCENC.h"
/* Sets the length of the OEM public certificate. Returns
OEMCrypto_ERROR_INVALID_CONTEXT if |public_cert| is NULL,
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other issues, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of |public_cert|. */
OEMCryptoResult GetOEMPublicCertificateLength(uint32_t* public_cert_length);
/* Sets the OEM public certificate. Returns OEMCrypto_ERROR_INVALID_CONTEXT
if |public_cert| is NULL, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any
other issues, and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of |public_cert|. */
OEMCryptoResult GetOEMPublicCertificate(uint8_t* public_cert);
/* Calls to the crypto engine to sign |message_length| bytes of |message| using
padding scheme RSASSA-PSS with SHA1 and places the result in |signature| and
modifies |signature_length| to the appropriate value.
Returns OEMCrypto_ERROR_SHORT_BUFFER if |signature_length| is too small or if
|signature| is NULL, in which case it sets |signature_length| to the
appropriate length. Returns OEMCrypto_ERROR_INVALID_CONTEXT if
|message_length| is 0 or if any of the pointers except |signature| are NULL,
OEMCrypto_ERROR_INVALID_RSA_KEY if the OEM RSA key is invalid,
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise. */
OEMCryptoResult SignMessageWithOEMPrivateKey(const uint8_t* message,
uint32_t message_length,
uint8_t* signature,
uint32_t* signature_length);
/* Calls to the crypto engine to decrypt |in_length| bytes of |in| and place it
in |out| using the OEM private key. The padding scheme shall only be
PKCS1-OAEP.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|in_length| is 0, OEMCrypto_ERROR_SHORT_BUFFER if *|out_length| is too small,
in which case it sets *|out_length| to the appropriate length,
OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other failures, and
OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult DecryptMessageWithOEMPrivateKey(const uint8_t* in,
uint32_t in_length,
uint8_t* out,
uint32_t* out_length);
/* Validates the OEM private key stored on the device.
Returns OEMCrypto_ERROR_INVALID_RSA_KEY if the key is not a valid RSA key,
OEMCrypto_ERROR_UNKNOWN_FAILURE on any other failures, and OEMCrypto_SUCCESS
otherwise. */
OEMCryptoResult ValidateOEMPrivateKey(void);
/* For devices that use Provisioning 3.0 and want to provide a custom device id
instead of using the OEM cert as the unique identifier.
Returns OEMCrypto_ERROR_SHORT_BUFFER if |device_id_length| is too small,
OEMCrypto_ERROR_INVALID_CONTEXT if |device_id| is NULL,
OEMCrypto_ERROR_NOT_IMPLEMENTED if the OEM cert should be used,
OEMCrypto_ERROR_UNKNOWN_FAILURE on any other failures, and OEMCrypto_SUCCESS
otherwise.
Caller retains ownership of all pointers and |device_id_length| must not be
NULL. */
OEMCryptoResult GetDeviceIDForOEMCert(uint8_t* device_id,
uint32_t* device_id_length);
/* Attempt to validate the current keybox loaded.
Returns OEMCrypto_ERROR_BAD_MAGIC if magic field is not "kbox",
OEMCrypto_ERROR_BAD_CRC if computed CRC is not equivalent to stored CRC,
and OEMCrypto_SUCCESS otherwise. */
OEMCryptoResult ValidateKeybox(void);
/* Get the 72 byte encrypted key data from the current keybox.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if |key_data| is NULL and
OEMCrypto_SUCCESS otherwise.
In order to avoid buffer overflow attacks, we recommend partners to keep this
separate from the device key in the keybox, so that when this accessed, the
device key is not exposed.
|key_data| must be >= 72 bytes. */
OEMCryptoResult GetKeyDataFromKeybox(uint8_t* key_data);
/* Get the 32 byte device id from the current keybox.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if |device_id| is NULL and
OEMCrypto_SUCCESS otherwise.
In order to avoid buffer overflow attacks, we recommend partners to keep this
separate from the device key in the keybox, so that when this accessed, the
device key is not exposed.
|device_id| must be >= 32 bytes. */
OEMCryptoResult GetDeviceIDFromKeybox(uint8_t* device_id);
/* Load a test keybox to be used until the next OEMCrypto_Terminate call.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if |test_keybox| is NULL and
OEMCrypto_SUCCESS otherwise.
|test_keybox| must be >= 128 bytes. */
OEMCryptoResult LoadTestKeybox(const uint8_t* test_keybox);
/* Derives a 16 byte key from the keybox key and |context_length| bytes of
|context| using AES-128-CMAC. Prepends |counter| to context in the
derivation. Modifies |out| to the correct value.
|out| must be >= 16 bytes.
Returns OEMCrypto_ERROR_INVALID_CONTEXT if any of the pointers are NULL or
|context_length| is 0, OEMCrypto_ERROR_UNKNOWN_FAILURE if there are any other
failures, and OEMCrypto_SUCCESS otherwise.
Caller retains ownership of all pointers. */
OEMCryptoResult DeriveKeyFromKeybox(uint8_t counter, const uint8_t* context,
uint32_t context_length, uint8_t* out);
#endif /* OEMCRYPTO_TA_ROOT_OF_TRUST_INTERFACE_H_ */

File diff suppressed because it is too large Load Diff

123
serialization/api_support.c Normal file
View File

@@ -0,0 +1,123 @@
/*
* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
/*
* Support functions for the OEMCrypto API functions, related to
* message handling
*/
#include "OEMCryptoCENC.h"
#include "api_support.h"
#include "bump_allocator.h"
#include "message.h"
#include "shared_memory_allocator.h"
#include "shared_memory_interface.h"
#include "special_cases.h"
#include "transport_interface.h"
/*
* If true, the system has been invalidated due to a communications
* breakdown with the TEE. This is a persistent condition and will
* cause OEMCrypto_ERROR_SYSTEM_INVALIDATED to be returned from any
* function that returns an OEMCryptoResult until the transport
* interface is reinitialized.
*/
static bool system_invalidated;
/*
* This is the result code to be returned from any function that
* returns OEMCryptoResult code.
*/
OEMCryptoResult api_result;
/*
* odkitee OEMCrypto API is single threaded
*/
pthread_mutex_t api_lock;
/*
* Called at the beginning of every API function. Checks configuration
* state and if it is okay allocates a request message. Sets
* api_result based on the status of the operations, which will be
* returned from any API function that returns an OEMCryptoResult
* code. On exit, if api_result is not OEMCrypto_SUCCESS or NULL is
* returned, then any allocated messages will have been deallocated
* prior to returning.
*/
Message *API_InitializeRequest(void) {
api_result = OEMCrypto_SUCCESS;
SharedMemory_Reset();
BumpAllocator_Reset();
Message* request = ODK_Transport_AllocateMessage();
if (request == NULL) {
api_result = OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
} else if (GetStatus(request) != MESSAGE_STATUS_OK) {
/* The transport allocator must return initialized messages */
api_result = OEMCrypto_ERROR_SYSTEM_INVALIDATED;
}
if (api_result != OEMCrypto_SUCCESS) {
if (request != NULL) {
ODK_Transport_DeallocateMessage(request);
request = NULL;
}
}
return request;
}
/*
* Called to send the request message to the TEE and receive the
* response. Sets api_result based on the status of the
* operations. Returns either a valid response message or NULL. If
* NULL is returned or api_result != OEMCrypto_SUCCESS then any
* allocated messages will have been deallocated prior to returning.
*/
Message *API_Transact(Message* request) {
if (api_result != OEMCrypto_SUCCESS || request == NULL) {
return NULL;
}
ODK_Transport_Status transport_status = ODK_Transport_SendMessage(request);
Message* response = NULL;
if (transport_status == ODK_TRANSPORT_STATUS_IO_ERROR) {
api_result = OEMCrypto_ERROR_SYSTEM_INVALIDATED;
} else {
transport_status = ODK_Transport_ReceiveMessage(&response);
if (transport_status == ODK_TRANSPORT_STATUS_IO_ERROR || response == NULL) {
api_result = OEMCrypto_ERROR_SYSTEM_INVALIDATED;
} else if (GetStatus(response) != MESSAGE_STATUS_OK) {
api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
if (api_result != OEMCrypto_SUCCESS) {
if (response) {
ODK_Transport_DeallocateMessage(response);
response = NULL;
}
}
return response;
}
/*
* Called at the end of every API function. Sets system_invalidated if
* the local api_result indicates a failure in the current
* function. Once system_invalidated is set, it will persist until
* reset by the next OEMCrypto_Inititalize/OEMCrypto_Terminate.
*/
OEMCryptoResult API_CheckResult(OEMCryptoResult unpacked_result) {
if (api_result == OEMCrypto_ERROR_SYSTEM_INVALIDATED) {
system_invalidated = true;
} else if (api_result == OEMCrypto_SUCCESS) {
api_result = unpacked_result;
}
return api_result;
}
/*
* Called by OEMCrypto_Terminate
*/
void API_Terminate(void) {
system_invalidated = false;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
/*
* Support functions for the OEMCrypto API functions, related to
* message handling
*/
#include <pthread.h>
#include "OEMCryptoCENC.h"
#include "bump_allocator.h"
#include "deserializer.h"
#include "marshaller_base.h"
#include "serializer.h"
#include "shared_memory_allocator.h"
#include "shared_memory_interface.h"
#include "special_cases.h"
#include "transport_interface.h"
#ifndef ODKITEE_API_SUPPORT_H_
#define ODKITEE_API_SUPPORT_H_
#ifdef __cplusplus
extern "C" {
#endif
#define OEMCRYPTO_API __attribute__((visibility("default")))
extern pthread_mutex_t api_lock;
extern OEMCryptoResult api_result;
Message *API_InitializeRequest();
Message *API_Transact(Message *request);
OEMCryptoResult API_CheckResult(OEMCryptoResult unpacked_result);
void API_Terminate();
#ifdef __cplusplus
}
#endif
#endif /* ODKITEE_API_SUPOPRT_H_ */

View File

@@ -0,0 +1,61 @@
/*
* 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 "bump_allocator.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITIAL_ALLOCATOR_BUFFER_SIZE (8 * 1024)
static uint8_t buffer[INITIAL_ALLOCATOR_BUFFER_SIZE];
static uint8_t* ptr = buffer;
static size_t buffer_size = sizeof(buffer);
static size_t buffer_offset = 0;
uint8_t* BumpAllocate(size_t nbytes) {
size_t new_offset = 0;
if(__builtin_add_overflow(buffer_offset, nbytes, &new_offset) ||
(new_offset > buffer_size)) {
/*
* The bump allocator buffer should be large enough that a malloc
* is never required. But allow a malloc if the buffer overflows.
*/
fprintf(stderr, "Warning: bump allocator memory size exceeded,"
" size=%zd, requested=%zd\n", buffer_size, new_offset);
size_t new_size = 2 * buffer_size;
uint8_t* new_ptr = malloc(new_size);
if (new_ptr == NULL) {
fprintf(stderr, "Fatal: bump allocator could not malloc %zd bytes\n",
new_size);
abort();
}
memcpy(new_ptr, ptr, buffer_size);
memset(new_ptr + buffer_size, 0, buffer_size);
buffer_size = new_size;
if (ptr != buffer) {
free(ptr);
}
ptr = new_ptr;
}
uint8_t* result = ptr + buffer_offset;
memset(result, 0, nbytes);
buffer_offset = new_offset;
return result;
}
void BumpAllocator_Reset(void) {
buffer_offset = 0;
if (ptr != buffer) {
free(ptr);
}
ptr = buffer;
buffer_size = sizeof(buffer);
memset(buffer, 0, sizeof(buffer));
}

View File

@@ -0,0 +1,29 @@
/*
* 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.
*/
#ifndef ODKITEE_BUMP_ALLOCATOR_H_
#define ODKITEE_BUMP_ALLOCATOR_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/*
* Simple bump allocator. Allocate memory chunks from a fixed region
* at consecutively increasing offsets. The memory is all released
* when BumpAllocator_Reset is called.
*/
uint8_t* BumpAllocate(size_t size);
void BumpAllocator_Reset(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //ODKITEE_BUMP_ALLOCATOR_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,383 @@
/*
* 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.
*/
/*
* This code is auto-generated, do not edit
*/
#ifndef ODKITEE_DESERIALIZER_H_
#define ODKITEE_DESERIALIZER_H_
#include "deserializer.h"
#include "serialization_base.h"
#include "serializer.h"
#ifdef __cplusplus
extern "C" {
#endif
bool Is_Valid_OEMCryptoResult(uint32_t value);
bool Is_Valid_OEMCryptoBufferType(uint32_t value);
bool Is_Valid_OEMCryptoCipherMode(uint32_t value);
bool Is_Valid_OEMCrypto_LicenseType(uint32_t value);
bool Is_Valid_OEMCrypto_Algorithm(uint32_t value);
bool Is_Valid_OEMCrypto_Usage_Entry_Status(uint32_t value);
bool Is_Valid_OEMCrypto_Clock_Security_Level(uint32_t value);
bool Is_Valid_OEMCrypto_HDCP_Capability(uint32_t value);
bool Is_Valid_OEMCrypto_ProvisioningMethod(uint32_t value);
void ODK_Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
void ODK_Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj);
void ODK_Unpack_OEMCrypto_EntitledContentKeyObject(
Message* msg, OEMCrypto_EntitledContentKeyObject* obj);
void ODK_Unpack_OEMCrypto_KeyRefreshObject(Message* msg,
OEMCrypto_KeyRefreshObject* obj);
void ODK_Unpack_OEMCrypto_CENCEncryptPatternDesc(
Message* msg, OEMCrypto_CENCEncryptPatternDesc* obj);
void ODK_Unpack_SecurityLevel_Request(Message* msg);
void ODK_Unpack_SecurityLevel_Response(Message* msg, char** result);
void ODK_Unpack_BuildInformation_Request(Message* msg);
void ODK_Unpack_BuildInformation_Response(Message* msg, char** result);
void ODK_Unpack_SetSandbox_Request(Message* msg, uint8_t** sandbox_id,
size_t* sandbox_id_length);
void ODK_Unpack_SetSandbox_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_Initialize_Request(Message* msg);
void ODK_Unpack_Initialize_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_Terminate_Request(Message* msg);
void ODK_Unpack_Terminate_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_OpenSession_Request(Message* msg, OEMCrypto_SESSION** session);
void ODK_Unpack_OpenSession_Response(Message* msg, OEMCryptoResult* result,
OEMCrypto_SESSION** session);
void ODK_Unpack_CloseSession_Request(Message* msg, OEMCrypto_SESSION* session);
void ODK_Unpack_CloseSession_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_GenerateDerivedKeys_Request(Message* msg,
OEMCrypto_SESSION* session,
SharedMemory** mac_key_context,
uint32_t* mac_key_context_length,
SharedMemory** enc_key_context,
uint32_t* enc_key_context_length);
void ODK_Unpack_GenerateDerivedKeys_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_DeriveKeysFromSessionKey_Request(
Message* msg, OEMCrypto_SESSION* session, uint8_t** enc_session_key,
size_t* enc_session_key_length, SharedMemory** mac_key_context,
size_t* mac_key_context_length, SharedMemory** enc_key_context,
size_t* enc_key_context_length);
void ODK_Unpack_DeriveKeysFromSessionKey_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_GenerateNonce_Request(Message* msg, OEMCrypto_SESSION* session,
uint32_t** nonce);
void ODK_Unpack_GenerateNonce_Response(Message* msg, OEMCryptoResult* result,
uint32_t** nonce);
void ODK_Unpack_GenerateSignature_Request(
Message* msg, OEMCrypto_SESSION* session, SharedMemory** message,
size_t* message_length, uint8_t** signature, size_t** signature_length);
void ODK_Unpack_GenerateSignature_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** signature,
size_t** signature_length);
void ODK_Unpack_LoadSRM_Request(Message* msg, uint8_t** buffer,
size_t* buffer_length);
void ODK_Unpack_LoadSRM_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_LoadKeys_Request(
Message* msg, OEMCrypto_SESSION* session, SharedMemory** message,
size_t* message_length, SharedMemory** signature, size_t* signature_length,
OEMCrypto_Substring* enc_mac_keys_iv, OEMCrypto_Substring* enc_mac_keys,
size_t* key_array_length, OEMCrypto_KeyObject** key_array,
OEMCrypto_Substring* pst, OEMCrypto_Substring* srm_restriction_data,
OEMCrypto_LicenseType* license_type);
void ODK_Unpack_LoadKeys_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_LoadEntitledContentKeys_Request(
Message* msg, OEMCrypto_SESSION* session, SharedMemory** message,
size_t* message_length, size_t* key_array_length,
OEMCrypto_EntitledContentKeyObject** key_array);
void ODK_Unpack_LoadEntitledContentKeys_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_RefreshKeys_Request(
Message* msg, OEMCrypto_SESSION* session, SharedMemory** message,
size_t* message_length, SharedMemory** signature, size_t* signature_length,
size_t* key_array_length, OEMCrypto_KeyRefreshObject** key_array);
void ODK_Unpack_RefreshKeys_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_QueryKeyControl_Request(Message* msg,
OEMCrypto_SESSION* session,
uint8_t** content_key_id,
size_t* content_key_id_length,
uint8_t** key_control_block,
size_t** key_control_block_length);
void ODK_Unpack_QueryKeyControl_Response(Message* msg, OEMCryptoResult* result,
uint8_t** key_control_block,
size_t** key_control_block_length);
void ODK_Unpack_SelectKey_Request(Message* msg, OEMCrypto_SESSION* session,
uint8_t** content_key_id,
size_t* content_key_id_length,
OEMCryptoCipherMode* cipher_mode);
void ODK_Unpack_SelectKey_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_DecryptCENC_Request(
Message* msg, OEMCrypto_SESSION* session, SharedMemory** data_addr,
size_t* data_addr_length, bool* is_encrypted, uint8_t* iv,
size_t* block_offset, OEMCrypto_DestBufferDesc** out_buffer,
OEMCrypto_CENCEncryptPatternDesc** pattern, uint8_t* subsample_flags);
void ODK_Unpack_DecryptCENC_Response(Message* msg, OEMCryptoResult* result,
OEMCrypto_DestBufferDesc** out_buffer);
void ODK_Unpack_CopyBuffer_Request(Message* msg, OEMCrypto_SESSION* session,
SharedMemory** data_addr,
size_t* data_addr_length,
OEMCrypto_DestBufferDesc** out_buffer,
uint8_t* subsample_flags);
void ODK_Unpack_CopyBuffer_Response(Message* msg, OEMCryptoResult* result,
OEMCrypto_DestBufferDesc** out_buffer);
void ODK_Unpack_Generic_Encrypt_Request(Message* msg,
OEMCrypto_SESSION* session,
SharedMemory** in_buffer,
size_t* in_buffer_length, uint8_t* iv,
OEMCrypto_Algorithm* algorithm,
uint8_t** out_buffer);
void ODK_Unpack_Generic_Encrypt_Response(Message* msg, OEMCryptoResult* result,
size_t* in_buffer_length,
uint8_t** out_buffer);
void ODK_Unpack_Generic_Decrypt_Request(Message* msg,
OEMCrypto_SESSION* session,
SharedMemory** in_buffer,
size_t* in_buffer_length, uint8_t* iv,
OEMCrypto_Algorithm* algorithm,
uint8_t** out_buffer);
void ODK_Unpack_Generic_Decrypt_Response(Message* msg, OEMCryptoResult* result,
size_t* in_buffer_length,
uint8_t** out_buffer);
void ODK_Unpack_Generic_Sign_Request(Message* msg, OEMCrypto_SESSION* session,
SharedMemory** buffer,
size_t* buffer_length,
OEMCrypto_Algorithm* algorithm,
uint8_t** signature,
size_t** signature_length);
void ODK_Unpack_Generic_Sign_Response(Message* msg, OEMCryptoResult* result,
uint8_t** signature,
size_t** signature_length);
void ODK_Unpack_Generic_Verify_Request(Message* msg, OEMCrypto_SESSION* session,
SharedMemory** buffer,
size_t* buffer_length,
OEMCrypto_Algorithm* algorithm,
SharedMemory** signature,
size_t* signature_length);
void ODK_Unpack_Generic_Verify_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_UpdateUsageTable_Request(Message* msg);
void ODK_Unpack_UpdateUsageTable_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_WrapKeyboxOrOEMCert_Request(Message* msg, uint8_t** rot,
size_t* rotLength,
uint8_t** wrappedRot,
size_t** wrappedRotLength,
uint8_t** transportKey,
size_t* transportKeyLength);
void ODK_Unpack_WrapKeyboxOrOEMCert_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** wrappedRot,
size_t** wrappedRotLength);
void ODK_Unpack_InstallKeyboxOrOEMCert_Request(Message* msg, uint8_t** rot,
size_t* rotLength);
void ODK_Unpack_InstallKeyboxOrOEMCert_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_GetProvisioningMethod_Request(Message* msg);
void ODK_Unpack_GetProvisioningMethod_Response(
Message* msg, OEMCrypto_ProvisioningMethod* result);
void ODK_Unpack_IsKeyboxOrOEMCertValid_Request(Message* msg);
void ODK_Unpack_IsKeyboxOrOEMCertValid_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_GetDeviceID_Request(Message* msg, uint8_t** device_id,
size_t** device_id_length);
void ODK_Unpack_GetDeviceID_Response(Message* msg, OEMCryptoResult* result,
uint8_t** device_id,
size_t** device_id_length);
void ODK_Unpack_GetKeyData_Request(Message* msg, uint8_t** keyData,
size_t** keyDataLength);
void ODK_Unpack_GetKeyData_Response(Message* msg, OEMCryptoResult* result,
uint8_t** keyData, size_t** keyDataLength);
void ODK_Unpack_LoadTestKeybox_Request(Message* msg, uint8_t** buffer,
size_t* buffer_length);
void ODK_Unpack_LoadTestKeybox_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_GetOEMPublicCertificate_Request(Message* msg,
OEMCrypto_SESSION* session,
uint8_t** public_cert,
size_t** public_cert_length);
void ODK_Unpack_GetOEMPublicCertificate_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** public_cert,
size_t** public_cert_length);
void ODK_Unpack_GetRandom_Request(Message* msg, uint8_t** random_data,
size_t* random_data_length);
void ODK_Unpack_GetRandom_Response(Message* msg, OEMCryptoResult* result,
uint8_t** random_data,
size_t* random_data_length);
void ODK_Unpack_APIVersion_Request(Message* msg);
void ODK_Unpack_APIVersion_Response(Message* msg, uint32_t* result);
void ODK_Unpack_Security_Patch_Level_Request(Message* msg);
void ODK_Unpack_Security_Patch_Level_Response(Message* msg, uint8_t* result);
void ODK_Unpack_GetHDCPCapability_Request(Message* msg,
OEMCrypto_HDCP_Capability** current,
OEMCrypto_HDCP_Capability** maximum);
void ODK_Unpack_GetHDCPCapability_Response(Message* msg,
OEMCryptoResult* result,
OEMCrypto_HDCP_Capability** current,
OEMCrypto_HDCP_Capability** maximum);
void ODK_Unpack_SupportsUsageTable_Request(Message* msg);
void ODK_Unpack_SupportsUsageTable_Response(Message* msg, bool* result);
void ODK_Unpack_IsAntiRollbackHwPresent_Request(Message* msg);
void ODK_Unpack_IsAntiRollbackHwPresent_Response(Message* msg, bool* result);
void ODK_Unpack_GetNumberOfOpenSessions_Request(Message* msg, size_t** count);
void ODK_Unpack_GetNumberOfOpenSessions_Response(Message* msg,
OEMCryptoResult* result,
size_t** count);
void ODK_Unpack_GetMaxNumberOfSessions_Request(Message* msg, size_t** max);
void ODK_Unpack_GetMaxNumberOfSessions_Response(Message* msg,
OEMCryptoResult* result,
size_t** max);
void ODK_Unpack_SupportedCertificates_Request(Message* msg);
void ODK_Unpack_SupportedCertificates_Response(Message* msg, uint32_t* result);
void ODK_Unpack_IsSRMUpdateSupported_Request(Message* msg);
void ODK_Unpack_IsSRMUpdateSupported_Response(Message* msg, bool* result);
void ODK_Unpack_GetCurrentSRMVersion_Request(Message* msg, uint16_t** version);
void ODK_Unpack_GetCurrentSRMVersion_Response(Message* msg,
OEMCryptoResult* result,
uint16_t** version);
void ODK_Unpack_GetAnalogOutputFlags_Request(Message* msg);
void ODK_Unpack_GetAnalogOutputFlags_Response(Message* msg, uint32_t* result);
void ODK_Unpack_ResourceRatingTier_Request(Message* msg);
void ODK_Unpack_ResourceRatingTier_Response(Message* msg, uint32_t* result);
void ODK_Unpack_RewrapDeviceRSAKey30_Request(
Message* msg, OEMCrypto_SESSION* session, uint32_t** unaligned_nonce,
SharedMemory** encrypted_message_key, size_t* encrypted_message_key_length,
SharedMemory** enc_rsa_key, size_t* enc_rsa_key_length,
uint8_t** enc_rsa_key_iv, uint8_t** wrapped_rsa_key,
size_t** wrapped_rsa_key_length);
void ODK_Unpack_RewrapDeviceRSAKey30_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** wrapped_rsa_key,
size_t** wrapped_rsa_key_length);
void ODK_Unpack_RewrapDeviceRSAKey_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** wrapped_rsa_key,
size_t** wrapped_rsa_key_length);
void ODK_Unpack_LoadDeviceRSAKey_Request(Message* msg,
OEMCrypto_SESSION* session,
SharedMemory** wrapped_rsa_key,
size_t* wrapped_rsa_key_length);
void ODK_Unpack_LoadDeviceRSAKey_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_LoadTestRSAKey_Request(Message* msg);
void ODK_Unpack_LoadTestRSAKey_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_GenerateRSASignature_Request(
Message* msg, OEMCrypto_SESSION* session, SharedMemory** message,
size_t* message_length, uint8_t** signature, size_t** signature_length,
RSA_Padding_Scheme* padding_scheme);
void ODK_Unpack_GenerateRSASignature_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** signature,
size_t** signature_length);
void ODK_Unpack_CreateUsageTableHeader_Request(Message* msg,
uint8_t** header_buffer,
size_t** header_buffer_length);
void ODK_Unpack_CreateUsageTableHeader_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** header_buffer,
size_t** header_buffer_length);
void ODK_Unpack_LoadUsageTableHeader_Request(Message* msg, uint8_t** buffer,
size_t* buffer_length);
void ODK_Unpack_LoadUsageTableHeader_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_CreateNewUsageEntry_Request(Message* msg,
OEMCrypto_SESSION* session,
uint32_t** usage_entry_number);
void ODK_Unpack_CreateNewUsageEntry_Response(Message* msg,
OEMCryptoResult* result,
uint32_t** usage_entry_number);
void ODK_Unpack_LoadUsageEntry_Request(Message* msg, OEMCrypto_SESSION* session,
uint32_t* usage_entry_number,
uint8_t** buffer, size_t* buffer_length);
void ODK_Unpack_LoadUsageEntry_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_UpdateUsageEntry_Request(Message* msg,
OEMCrypto_SESSION* session,
SharedMemory** header_buffer,
size_t** header_buffer_length,
SharedMemory** entry_buffer,
size_t** entry_buffer_length);
void ODK_Unpack_UpdateUsageEntry_Response(Message* msg, OEMCryptoResult* result,
SharedMemory** header_buffer,
size_t** header_buffer_length,
SharedMemory** entry_buffer,
size_t** entry_buffer_length);
void ODK_Unpack_DeactivateUsageEntry_Request(Message* msg,
OEMCrypto_SESSION* session,
uint8_t** pst, size_t* pst_length);
void ODK_Unpack_DeactivateUsageEntry_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_ReportUsage_Request(Message* msg, OEMCrypto_SESSION* session,
uint8_t** pst, size_t* pst_length,
uint8_t** buffer, size_t** buffer_length);
void ODK_Unpack_ReportUsage_Response(Message* msg, OEMCryptoResult* result,
uint8_t** buffer, size_t** buffer_length);
void ODK_Unpack_DeleteUsageEntry_Request(
Message* msg, OEMCrypto_SESSION* session, uint8_t** pst, size_t* pst_length,
uint8_t** message, size_t* message_length, uint8_t** signature,
size_t* signature_length);
void ODK_Unpack_DeleteUsageEntry_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_ForceDeleteUsageEntry_Request(Message* msg, uint8_t** pst,
size_t* pst_length);
void ODK_Unpack_ForceDeleteUsageEntry_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_MoveEntry_Request(Message* msg, OEMCrypto_SESSION* session,
uint32_t* new_index);
void ODK_Unpack_MoveEntry_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_ShrinkUsageTableHeader_Request(Message* msg,
uint32_t* new_entry_count,
uint8_t** header_buffer,
size_t** header_buffer_length);
void ODK_Unpack_ShrinkUsageTableHeader_Response(Message* msg,
OEMCryptoResult* result,
uint8_t** header_buffer,
size_t** header_buffer_length);
void ODK_Unpack_CopyOldUsageEntry_Request(Message* msg,
OEMCrypto_SESSION* session,
uint8_t** pst, size_t* pst_length);
void ODK_Unpack_CopyOldUsageEntry_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_DeleteOldUsageTable_Request(Message* msg);
void ODK_Unpack_DeleteOldUsageTable_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_RemoveSRM_Request(Message* msg);
void ODK_Unpack_RemoveSRM_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_CreateOldUsageEntry_Request(
Message* msg, 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, uint8_t** pst, size_t* pst_length);
void ODK_Unpack_CreateOldUsageEntry_Response(Message* msg,
OEMCryptoResult* result);
void ODK_Unpack_SupportsDecryptHash_Request(Message* msg);
void ODK_Unpack_SupportsDecryptHash_Response(Message* msg, uint32_t* result);
void ODK_Unpack_SetDecryptHash_Request(Message* msg, OEMCrypto_SESSION* session,
uint32_t* frame_number, uint8_t** hash,
size_t* hash_length);
void ODK_Unpack_SetDecryptHash_Response(Message* msg, OEMCryptoResult* result);
void ODK_Unpack_GetHashErrorCode_Request(Message* msg,
OEMCrypto_SESSION* session,
uint32_t** failed_frame_number);
void ODK_Unpack_GetHashErrorCode_Response(Message* msg, OEMCryptoResult* result,
uint32_t** failed_frame_number);
void ODK_UnpackNullable_c_str(Message* msg, char** value);
void ODK_UnpackAlloc_c_str(Message* msg, char** value);
void ODK_UnpackNullable_uint32_t(Message* msg, OEMCrypto_SESSION** value);
void ODK_UnpackAlloc_uint32_t(Message* msg, OEMCrypto_SESSION** value);
void ODK_UnpackNullable_size_t(Message* msg, size_t** value);
void ODK_UnpackNullable_OEMCrypto_DestBufferDesc(
Message* msg, OEMCrypto_DestBufferDesc** value);
void ODK_UnpackNullable_OEMCrypto_CENCEncryptPatternDesc(
Message* msg, OEMCrypto_CENCEncryptPatternDesc** value);
void ODK_UnpackAlloc_size_t(Message* msg, size_t** value);
void ODK_UnpackNullable_uint16_t(Message* msg, uint16_t** value);
void ODK_UnpackAlloc_uint16_t(Message* msg, uint16_t** value);
void ODK_UnpackNullable_uint8_t(Message* msg, uint8_t** value);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* ODKITEE_DESERIALIZER_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,369 @@
/*
* 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.
*/
/*
* This code is auto-generated, do not edit
*/
#ifndef ODKITEE_SERIALIZER_H_
#define ODKITEE_SERIALIZER_H_
#include "deserializer.h"
#include "serialization_base.h"
#include "serializer.h"
#ifdef __cplusplus
extern "C" {
#endif
bool SuccessResult(OEMCryptoResult result);
void ODK_Pack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring const* obj);
void ODK_Pack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject const* obj);
void ODK_Pack_OEMCrypto_EntitledContentKeyObject(
Message* msg, OEMCrypto_EntitledContentKeyObject const* obj);
void ODK_Pack_OEMCrypto_KeyRefreshObject(Message* msg,
OEMCrypto_KeyRefreshObject const* obj);
void ODK_Pack_OEMCrypto_CENCEncryptPatternDesc(
Message* msg, OEMCrypto_CENCEncryptPatternDesc const* obj);
void ODK_Pack_SecurityLevel_Request(Message* msg);
void ODK_Pack_SecurityLevel_Response(Message* msg, const char* result);
void ODK_Pack_BuildInformation_Request(Message* msg);
void ODK_Pack_BuildInformation_Response(Message* msg, const char* result);
void ODK_Pack_SetSandbox_Request(Message* msg, const uint8_t* sandbox_id,
size_t sandbox_id_length);
void ODK_Pack_SetSandbox_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_Initialize_Request(Message* msg);
void ODK_Pack_Initialize_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_Terminate_Request(Message* msg);
void ODK_Pack_Terminate_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_OpenSession_Request(Message* msg,
const OEMCrypto_SESSION* session);
void ODK_Pack_OpenSession_Response(Message* msg, OEMCryptoResult result,
const OEMCrypto_SESSION* session);
void ODK_Pack_CloseSession_Request(Message* msg, OEMCrypto_SESSION session);
void ODK_Pack_CloseSession_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_GenerateDerivedKeys_Request(Message* msg,
OEMCrypto_SESSION session,
const SharedMemory* mac_key_context,
uint32_t mac_key_context_length,
const SharedMemory* enc_key_context,
uint32_t enc_key_context_length);
void ODK_Pack_GenerateDerivedKeys_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_DeriveKeysFromSessionKey_Request(
Message* msg, OEMCrypto_SESSION session, const uint8_t* enc_session_key,
size_t enc_session_key_length, const SharedMemory* mac_key_context,
size_t mac_key_context_length, const SharedMemory* enc_key_context,
size_t enc_key_context_length);
void ODK_Pack_DeriveKeysFromSessionKey_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_GenerateNonce_Request(Message* msg, OEMCrypto_SESSION session,
const uint32_t* nonce);
void ODK_Pack_GenerateNonce_Response(Message* msg, OEMCryptoResult result,
const uint32_t* nonce);
void ODK_Pack_GenerateSignature_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* message,
size_t message_length,
const uint8_t* signature,
const size_t* signature_length);
void ODK_Pack_GenerateSignature_Response(Message* msg, OEMCryptoResult result,
const uint8_t* signature,
const size_t* signature_length);
void ODK_Pack_LoadSRM_Request(Message* msg, const uint8_t* buffer,
size_t buffer_length);
void ODK_Pack_LoadSRM_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_LoadKeys_Request(
Message* msg, OEMCrypto_SESSION session, const SharedMemory* message,
size_t message_length, const SharedMemory* 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);
void ODK_Pack_LoadKeys_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_LoadEntitledContentKeys_Request(
Message* msg, OEMCrypto_SESSION session, const SharedMemory* message,
size_t message_length, size_t key_array_length,
const OEMCrypto_EntitledContentKeyObject* key_array);
void ODK_Pack_LoadEntitledContentKeys_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_RefreshKeys_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* message,
size_t message_length,
const SharedMemory* signature,
size_t signature_length,
size_t key_array_length,
const OEMCrypto_KeyRefreshObject* key_array);
void ODK_Pack_RefreshKeys_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_QueryKeyControl_Request(Message* msg, OEMCrypto_SESSION session,
const uint8_t* content_key_id,
size_t content_key_id_length,
const uint8_t* key_control_block,
const size_t* key_control_block_length);
void ODK_Pack_QueryKeyControl_Response(Message* msg, OEMCryptoResult result,
const uint8_t* key_control_block,
const size_t* key_control_block_length);
void ODK_Pack_SelectKey_Request(Message* msg, OEMCrypto_SESSION session,
const uint8_t* content_key_id,
size_t content_key_id_length,
OEMCryptoCipherMode cipher_mode);
void ODK_Pack_SelectKey_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_DecryptCENC_Request(
Message* msg, OEMCrypto_SESSION session, const SharedMemory* data_addr,
size_t data_addr_length, bool is_encrypted, const uint8_t* iv,
size_t block_offset, const OEMCrypto_DestBufferDesc* out_buffer,
const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags);
void ODK_Pack_DecryptCENC_Response(Message* msg, OEMCryptoResult result,
const OEMCrypto_DestBufferDesc* out_buffer);
void ODK_Pack_CopyBuffer_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* data_addr,
size_t data_addr_length,
const OEMCrypto_DestBufferDesc* out_buffer,
uint8_t subsample_flags);
void ODK_Pack_CopyBuffer_Response(Message* msg, OEMCryptoResult result,
const OEMCrypto_DestBufferDesc* out_buffer);
void ODK_Pack_Generic_Encrypt_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* in_buffer,
size_t in_buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
const uint8_t* out_buffer);
void ODK_Pack_Generic_Encrypt_Response(Message* msg, OEMCryptoResult result,
size_t in_buffer_length,
const uint8_t* out_buffer);
void ODK_Pack_Generic_Decrypt_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* in_buffer,
size_t in_buffer_length,
const uint8_t* iv,
OEMCrypto_Algorithm algorithm,
const uint8_t* out_buffer);
void ODK_Pack_Generic_Decrypt_Response(Message* msg, OEMCryptoResult result,
size_t in_buffer_length,
const uint8_t* out_buffer);
void ODK_Pack_Generic_Sign_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const uint8_t* signature,
const size_t* signature_length);
void ODK_Pack_Generic_Sign_Response(Message* msg, OEMCryptoResult result,
const uint8_t* signature,
const size_t* signature_length);
void ODK_Pack_Generic_Verify_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* buffer,
size_t buffer_length,
OEMCrypto_Algorithm algorithm,
const SharedMemory* signature,
size_t signature_length);
void ODK_Pack_Generic_Verify_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_UpdateUsageTable_Request(Message* msg);
void ODK_Pack_UpdateUsageTable_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_WrapKeyboxOrOEMCert_Request(Message* msg, const uint8_t* rot,
size_t rotLength,
const uint8_t* wrappedRot,
const size_t* wrappedRotLength,
const uint8_t* transportKey,
size_t transportKeyLength);
void ODK_Pack_WrapKeyboxOrOEMCert_Response(Message* msg, OEMCryptoResult result,
const uint8_t* wrappedRot,
const size_t* wrappedRotLength);
void ODK_Pack_InstallKeyboxOrOEMCert_Request(Message* msg, const uint8_t* rot,
size_t rotLength);
void ODK_Pack_InstallKeyboxOrOEMCert_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_GetProvisioningMethod_Request(Message* msg);
void ODK_Pack_GetProvisioningMethod_Response(
Message* msg, OEMCrypto_ProvisioningMethod result);
void ODK_Pack_IsKeyboxOrOEMCertValid_Request(Message* msg);
void ODK_Pack_IsKeyboxOrOEMCertValid_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_GetDeviceID_Request(Message* msg, const uint8_t* device_id,
const size_t* device_id_length);
void ODK_Pack_GetDeviceID_Response(Message* msg, OEMCryptoResult result,
const uint8_t* device_id,
const size_t* device_id_length);
void ODK_Pack_GetKeyData_Request(Message* msg, const uint8_t* keyData,
const size_t* keyDataLength);
void ODK_Pack_GetKeyData_Response(Message* msg, OEMCryptoResult result,
const uint8_t* keyData,
const size_t* keyDataLength);
void ODK_Pack_LoadTestKeybox_Request(Message* msg, const uint8_t* buffer,
size_t buffer_length);
void ODK_Pack_LoadTestKeybox_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_GetOEMPublicCertificate_Request(Message* msg,
OEMCrypto_SESSION session,
const uint8_t* public_cert,
const size_t* public_cert_length);
void ODK_Pack_GetOEMPublicCertificate_Response(
Message* msg, OEMCryptoResult result, const uint8_t* public_cert,
const size_t* public_cert_length);
void ODK_Pack_GetRandom_Request(Message* msg, const uint8_t* random_data,
size_t random_data_length);
void ODK_Pack_GetRandom_Response(Message* msg, OEMCryptoResult result,
const uint8_t* random_data,
size_t random_data_length);
void ODK_Pack_APIVersion_Request(Message* msg);
void ODK_Pack_APIVersion_Response(Message* msg, uint32_t result);
void ODK_Pack_Security_Patch_Level_Request(Message* msg);
void ODK_Pack_Security_Patch_Level_Response(Message* msg, uint8_t result);
void ODK_Pack_GetHDCPCapability_Request(
Message* msg, const OEMCrypto_HDCP_Capability* current,
const OEMCrypto_HDCP_Capability* maximum);
void ODK_Pack_GetHDCPCapability_Response(
Message* msg, OEMCryptoResult result,
const OEMCrypto_HDCP_Capability* current,
const OEMCrypto_HDCP_Capability* maximum);
void ODK_Pack_SupportsUsageTable_Request(Message* msg);
void ODK_Pack_SupportsUsageTable_Response(Message* msg, bool result);
void ODK_Pack_IsAntiRollbackHwPresent_Request(Message* msg);
void ODK_Pack_IsAntiRollbackHwPresent_Response(Message* msg, bool result);
void ODK_Pack_GetNumberOfOpenSessions_Request(Message* msg,
const size_t* count);
void ODK_Pack_GetNumberOfOpenSessions_Response(Message* msg,
OEMCryptoResult result,
const size_t* count);
void ODK_Pack_GetMaxNumberOfSessions_Request(Message* msg, const size_t* max);
void ODK_Pack_GetMaxNumberOfSessions_Response(Message* msg,
OEMCryptoResult result,
const size_t* max);
void ODK_Pack_SupportedCertificates_Request(Message* msg);
void ODK_Pack_SupportedCertificates_Response(Message* msg, uint32_t result);
void ODK_Pack_IsSRMUpdateSupported_Request(Message* msg);
void ODK_Pack_IsSRMUpdateSupported_Response(Message* msg, bool result);
void ODK_Pack_GetCurrentSRMVersion_Request(Message* msg,
const uint16_t* version);
void ODK_Pack_GetCurrentSRMVersion_Response(Message* msg,
OEMCryptoResult result,
const uint16_t* version);
void ODK_Pack_GetAnalogOutputFlags_Request(Message* msg);
void ODK_Pack_GetAnalogOutputFlags_Response(Message* msg, uint32_t result);
void ODK_Pack_ResourceRatingTier_Request(Message* msg);
void ODK_Pack_ResourceRatingTier_Response(Message* msg, uint32_t result);
void ODK_Pack_RewrapDeviceRSAKey30_Request(
Message* msg, OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
const SharedMemory* encrypted_message_key,
size_t encrypted_message_key_length, const SharedMemory* enc_rsa_key,
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
const uint8_t* wrapped_rsa_key, const size_t* wrapped_rsa_key_length);
void ODK_Pack_RewrapDeviceRSAKey30_Response(
Message* msg, OEMCryptoResult result, const uint8_t* wrapped_rsa_key,
const size_t* wrapped_rsa_key_length);
void ODK_Pack_RewrapDeviceRSAKey_Response(Message* msg, OEMCryptoResult result,
const uint8_t* wrapped_rsa_key,
const size_t* wrapped_rsa_key_length);
void ODK_Pack_LoadDeviceRSAKey_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* wrapped_rsa_key,
size_t wrapped_rsa_key_length);
void ODK_Pack_LoadDeviceRSAKey_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_LoadTestRSAKey_Request(Message* msg);
void ODK_Pack_LoadTestRSAKey_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_GenerateRSASignature_Request(
Message* msg, OEMCrypto_SESSION session, const SharedMemory* message,
size_t message_length, const uint8_t* signature,
const size_t* signature_length, RSA_Padding_Scheme padding_scheme);
void ODK_Pack_GenerateRSASignature_Response(Message* msg,
OEMCryptoResult result,
const uint8_t* signature,
const size_t* signature_length);
void ODK_Pack_CreateUsageTableHeader_Request(
Message* msg, const uint8_t* header_buffer,
const size_t* header_buffer_length);
void ODK_Pack_CreateUsageTableHeader_Response(
Message* msg, OEMCryptoResult result, const uint8_t* header_buffer,
const size_t* header_buffer_length);
void ODK_Pack_LoadUsageTableHeader_Request(Message* msg, const uint8_t* buffer,
size_t buffer_length);
void ODK_Pack_LoadUsageTableHeader_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_CreateNewUsageEntry_Request(Message* msg,
OEMCrypto_SESSION session,
const uint32_t* usage_entry_number);
void ODK_Pack_CreateNewUsageEntry_Response(Message* msg, OEMCryptoResult result,
const uint32_t* usage_entry_number);
void ODK_Pack_LoadUsageEntry_Request(Message* msg, OEMCrypto_SESSION session,
uint32_t usage_entry_number,
const uint8_t* buffer,
size_t buffer_length);
void ODK_Pack_LoadUsageEntry_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_UpdateUsageEntry_Request(Message* msg, OEMCrypto_SESSION session,
const SharedMemory* header_buffer,
const size_t* header_buffer_length,
const SharedMemory* entry_buffer,
const size_t* entry_buffer_length);
void ODK_Pack_UpdateUsageEntry_Response(Message* msg, OEMCryptoResult result,
const SharedMemory* header_buffer,
const size_t* header_buffer_length,
const SharedMemory* entry_buffer,
const size_t* entry_buffer_length);
void ODK_Pack_DeactivateUsageEntry_Request(Message* msg,
OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length);
void ODK_Pack_DeactivateUsageEntry_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_ReportUsage_Request(Message* msg, OEMCrypto_SESSION session,
const uint8_t* pst, size_t pst_length,
const uint8_t* buffer,
const size_t* buffer_length);
void ODK_Pack_ReportUsage_Response(Message* msg, OEMCryptoResult result,
const uint8_t* buffer,
const size_t* buffer_length);
void ODK_Pack_DeleteUsageEntry_Request(Message* msg, 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);
void ODK_Pack_DeleteUsageEntry_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_ForceDeleteUsageEntry_Request(Message* msg, const uint8_t* pst,
size_t pst_length);
void ODK_Pack_ForceDeleteUsageEntry_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_MoveEntry_Request(Message* msg, OEMCrypto_SESSION session,
uint32_t new_index);
void ODK_Pack_MoveEntry_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_ShrinkUsageTableHeader_Request(
Message* msg, uint32_t new_entry_count, const uint8_t* header_buffer,
const size_t* header_buffer_length);
void ODK_Pack_ShrinkUsageTableHeader_Response(
Message* msg, OEMCryptoResult result, const uint8_t* header_buffer,
const size_t* header_buffer_length);
void ODK_Pack_CopyOldUsageEntry_Request(Message* msg, OEMCrypto_SESSION session,
const uint8_t* pst, size_t pst_length);
void ODK_Pack_CopyOldUsageEntry_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_DeleteOldUsageTable_Request(Message* msg);
void ODK_Pack_DeleteOldUsageTable_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_RemoveSRM_Request(Message* msg);
void ODK_Pack_RemoveSRM_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_CreateOldUsageEntry_Request(
Message* msg, uint64_t time_since_license_received,
uint64_t time_since_first_decrypt, uint64_t time_since_last_decrypt,
OEMCrypto_Usage_Entry_Status status, const uint8_t* server_mac_key,
const uint8_t* client_mac_key, const uint8_t* pst, size_t pst_length);
void ODK_Pack_CreateOldUsageEntry_Response(Message* msg,
OEMCryptoResult result);
void ODK_Pack_SupportsDecryptHash_Request(Message* msg);
void ODK_Pack_SupportsDecryptHash_Response(Message* msg, uint32_t result);
void ODK_Pack_SetDecryptHash_Request(Message* msg, OEMCrypto_SESSION session,
uint32_t frame_number, const uint8_t* hash,
size_t hash_length);
void ODK_Pack_SetDecryptHash_Response(Message* msg, OEMCryptoResult result);
void ODK_Pack_GetHashErrorCode_Request(Message* msg, OEMCrypto_SESSION session,
const uint32_t* failed_frame_number);
void ODK_Pack_GetHashErrorCode_Response(Message* msg, OEMCryptoResult result,
const uint32_t* failed_frame_number);
void ODK_PackNullable_c_str(Message* msg, const char* value);
void ODK_PackNullable_uint32_t(Message* msg, const OEMCrypto_SESSION* value);
void ODK_PackNullable_size_t(Message* msg, const size_t* value);
void ODK_PackNullable_OEMCrypto_DestBufferDesc(
Message* msg, const OEMCrypto_DestBufferDesc* value);
void ODK_PackNullable_OEMCrypto_CENCEncryptPatternDesc(
Message* msg, const OEMCrypto_CENCEncryptPatternDesc* value);
void ODK_PackNullable_uint16_t(Message* msg, const uint16_t* value);
void ODK_PackNullable_uint8_t(Message* msg, const uint8_t* value);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* ODKITEE_SERIALIZER_H_ */

View File

@@ -0,0 +1,95 @@
/*
* 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 "bump_allocator.h"
#include "marshaller_base.h"
static void InitBytes(uint8_t* ptr, size_t count) {
if (ptr && count) {
memset(ptr, 0, count);
}
}
/*
* The functions here are declared weak so they can
* be overriden by test functions that inject
* various initialized values for testing
*/
__attribute__((weak)) void Init_bool(bool* value) {
if (value) {
*value = false;
}
}
__attribute__((weak)) void Init_size_t(size_t* value) {
if (value) {
*value = 0;
}
}
__attribute__((weak)) void Init_c_str(char** value) {
if (value) {
*value = NULL;
}
}
__attribute__((weak)) void Init_uint8_t(uint8_t* value) {
if (value) {
*value = 0;
}
}
__attribute__((weak)) void Init_uint16_t(uint16_t* value) {
if (value) {
*value = 0;
}
}
__attribute__((weak)) void Init_uint32_t(uint32_t* value) {
if (value) {
*value = 0;
}
}
__attribute__((weak)) void Init_uint64_t(uint64_t* value) {
if (value) {
*value = 0;
}
}
__attribute__((weak)) void InitMemory(uint8_t* address, size_t length)
{
if (address && length) {
InitBytes(address, length);
}
}
__attribute__((weak)) void InitPointer(uint8_t** ptr) {
if (ptr) {
*ptr = NULL;
}
}
/*
* Allocate memory for a variable from the bump allocator, used for
* DeclarePackVar, DeclareUnpackVar, for some pointer types
*/
__attribute__((weak)) uint8_t* VarAlloc(size_t size) {
return BumpAllocate(size);
}
/*
* Special cases due to union & shared memory
*/
__attribute__((weak)) void Init_OEMCrypto_DestBufferDesc(OEMCrypto_DestBufferDesc* d) {
if (d) {
d->type = OEMCrypto_BufferType_Clear;
d->buffer.clear.address = NULL;
d->buffer.clear.max_length = 0;
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.
*/
#ifndef ODKITEE_MARSHALLER_BASE_H_
#define ODKITEE_MARSHALLER_BASE_H_
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "OEMCryptoCENC.h"
#include "serialization_base.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _Message Message;
/*
* When packing a pointer to size_t, and the pointer is null, pass
* this special value of size_t instead
*/
#define SZ_NULL (size_t)~0
void Init_bool(bool* value);
void Init_size_t(size_t* value);
void Init_c_str(char** value);
void Init_uint8_t(uint8_t* value);
void Init_uint16_t(uint16_t* value);
void Init_uint32_t(uint32_t* value);
void Init_uint64_t(uint64_t* value);
void InitMemory(uint8_t* addr, size_t length);
void InitPointer(uint8_t** addr);
/*
* Allocate memory for a variable from the bump allocator, used for
* DeclarePackVar, DeclareUnpackVar, for some pointer types
*/
uint8_t* VarAlloc(size_t size);
/*
* Special cases due to union & shared memory
*/
void Init_OEMCrypto_DestBufferDesc(OEMCrypto_DestBufferDesc* desc);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ODKITEE_MARSHALLER_BASE_H_

101
serialization/message.c Normal file
View File

@@ -0,0 +1,101 @@
/*
* 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 "message.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* Round up address to the nearest 8 byte boundary */
#define align_8(addr) (((uintptr_t)(addr) + 7) & ~(uintptr_t)7)
struct _Message {
uint8_t* base;
size_t capacity;
size_t size;
size_t read_offset;
MessageStatus status;
};
/*
* The message structure, which is separate from the buffer,
* is initialized to reference the buffer
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity) {
assert(message != NULL);
assert(buffer != NULL);
message->base = buffer;
message->capacity = capacity;
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
}
/*
* Cast pointer of Message from the input buffer. Since Message* is 8-byte
* aligned on a 64-bit system, or 4-byte aligned on a 32-bit system, the input
* buffer needs to be adjusted to 8-byte alignment before pointer casting, in
* order to avoid misaligned pointer
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size) {
assert(buffer != NULL);
uint8_t* const buffer_aligned = (uint8_t* const)align_8(buffer);
assert(buffer_size >= sizeof(Message) + (size_t)(buffer_aligned - buffer));
Message* const message = (Message* const)buffer_aligned;
message->base = buffer_aligned + sizeof(Message);
message->capacity =
buffer_size - sizeof(Message) - (size_t)(buffer_aligned - buffer);
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
return message;
}
void ClearMessage(Message* message) {
assert(message != NULL);
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
}
void ResetMessage(Message* message) {
assert(message != NULL);
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
}
uint8_t* GetBase(Message* message) {
assert(message != NULL);
return message->base;
}
size_t GetCapacity(Message* message) {
assert(message != NULL);
return message->capacity;
}
size_t GetSize(Message* message) {
assert(message != NULL);
return message->size;
}
MessageStatus GetStatus(Message* message) {
assert(message != NULL);
return message->status;
}
void SetStatus(Message* message, MessageStatus status) {
assert(message != NULL);
message->status = status;
}
void SetSize(Message* message, size_t size) {
assert(message != NULL);
message->size = size;
}

103
serialization/message.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* 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.
*/
#ifndef ODKITEE_MESSAGE_H_
#define ODKITEE_MESSAGE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
typedef struct _Message Message;
/*
* TODO: there is a dangerous duplication of the MessageStatus
* enum with odk/src/serialization_base.h. The two need to
* be converged. Removing the random enum values here, since
* a proper fix is outside the scope of the current CL. Opened
* b/158603784.
*/
typedef enum {
MESSAGE_STATUS_OK,
MESSAGE_STATUS_UNKNOWN_ERROR,
MESSAGE_STATUS_OVERFLOW_ERROR,
MESSAGE_STATUS_UNDERFLOW_ERROR,
MESSAGE_STATUS_PARSE_ERROR,
MESSAGE_STATUS_NULL_POINTER_ERROR,
MESSAGE_STATUS_API_VALUE_ERROR,
MESSAGE_STATUS_INVALID_TAG_ERROR,
MESSAGE_STATUS_END_OF_MESSAGE_ERROR,
MESSAGE_STATUS_INVALID_ENUM_VALUE
} MessageStatus;
/*
* Create a message from a buffer. The message structure consumes the first
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring
* that the buffer remains allocated for the lifetime of the message.
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size);
/*
* Initialize a message structure to reference a separate buffer. The caller
* is responsible for ensuring that the buffer remains allocated for the
* lifetime of the message.
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity);
/*
* Erase the contents of the message, set it
* to an empty state.
*/
void ClearMessage(Message* message);
/*
* Reset read pointer to the beginning of the
* message and clear status
*/
void ResetMessage(Message* message);
/*
* The message base is the start of the payload
*/
uint8_t* GetBase(Message* message);
/*
* Get the maximum number of bytes the message
* can hold.
*/
size_t GetCapacity(Message* message);
/*
* Get the number of bytes currently in the
* message
*/
size_t GetSize(Message* message);
/*
* Return the status of the message
*/
MessageStatus GetStatus(Message* message);
/*
* Set the message status to a specific value
*/
void SetStatus(Message* message, MessageStatus status);
/*
* Set the size of the message to a value. This
* may be needed after writing data into the payload.
*/
void SetSize(Message*message, size_t size);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ODKITEE_MESSAGE_H_

View File

@@ -0,0 +1,642 @@
/*
* 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 "serialization_base.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "OEMCryptoCENC.h"
#include "bump_allocator.h"
#include "marshaller_base.h"
#include "shared_memory_allocator.h"
#include "shared_memory_interface.h"
struct _Message {
uint8_t* base;
size_t capacity;
size_t size;
size_t read_offset;
MessageStatus status;
};
typedef enum {
TAG_INVALID,
TAG_BOOL,
TAG_SIZE_T,
TAG_UINT8,
TAG_UINT16,
TAG_UINT32,
TAG_UINT64,
TAG_MEMORY,
TAG_SHARED_MEMORY,
TAG_EOM
} TagType;
static bool ValidMessage(Message* message) {
if (message == NULL) {
return false;
}
if (message->status != MESSAGE_STATUS_OK) {
return false;
}
if (message->base == NULL) {
message->status = MESSAGE_STATUS_NULL_POINTER_ERROR;
return false;
}
if (message->read_offset > message->capacity ||
message->size > message->capacity ||
message->read_offset > message->size) {
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
return false;
}
return true;
}
static bool NullCheck(Message* message, const void* ptr) {
if (!ptr) {
message->status = MESSAGE_STATUS_NULL_POINTER_ERROR;
return true;
}
return false;
}
/* Callers are expected to have validated the message already */
static void PackBytes(Message* message, const uint8_t* ptr, size_t count) {
if (!NullCheck(message, ptr)) {
if (count <= message->capacity - message->size) {
memcpy(message->base + message->size, ptr, count);
message->size += count;
} else {
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
}
}
}
static void PackTag(Message* message, TagType tag) {
if (!ValidMessage(message)) return;
uint8_t byte = (uint8_t)tag;
PackBytes(message, &byte, sizeof(byte));
}
LengthType LengthFromUint32T(uint32_t length) {
LengthType length_type = {.tag = LENGTH_TYPE_UINT32_T,
.type.uint32_t_value = length};
return length_type;
}
LengthType LengthFromSizeT(size_t length) {
LengthType length_type = {.tag = LENGTH_TYPE_SIZE_T,
.type.size_t_value = length};
return length_type;
}
LengthType LengthFromUint32TPointer(const uint32_t* length) {
LengthType length_type = {.tag = LENGTH_TYPE_UINT32_T_POINTER,
.type.uint32_t_pointer = length};
return length_type;
}
LengthType LengthFromSizeTPointer(const size_t* length) {
LengthType length_type = {.tag = LENGTH_TYPE_SIZE_T_POINTER,
.type.size_t_pointer = length};
return length_type;
}
LengthType LengthFromUint32TDoublePointer(uint32_t** length) {
LengthType length_type = {.tag = LENGTH_TYPE_UINT32_T_DOUBLE_POINTER,
.type.uint32_t_double_pointer = length};
return length_type;
}
LengthType LengthFromSizeTDoublePointer(size_t** length) {
LengthType length_type = {.tag = LENGTH_TYPE_SIZE_T_DOUBLE_POINTER,
.type.size_t_double_pointer = length};
return length_type;
}
size_t LengthAsSizeT(LengthType length) {
size_t result = 0;
switch(length.tag) {
case LENGTH_TYPE_SIZE_T:
result = length.type.size_t_value;
break;
case LENGTH_TYPE_UINT32_T:
result = (size_t)length.type.uint32_t_value;
break;
case LENGTH_TYPE_SIZE_T_POINTER:
if (length.type.size_t_pointer) {
result = *length.type.size_t_pointer;
}
break;
case LENGTH_TYPE_UINT32_T_POINTER:
if (length.type.uint32_t_pointer) {
result = (size_t)*length.type.uint32_t_pointer;
}
break;
case LENGTH_TYPE_SIZE_T_DOUBLE_POINTER:
if (length.type.size_t_double_pointer && *length.type.size_t_double_pointer) {
result = **length.type.size_t_double_pointer;
}
break;
case LENGTH_TYPE_UINT32_T_DOUBLE_POINTER:
if (length.type.uint32_t_double_pointer && *length.type.uint32_t_double_pointer) {
result = (size_t)**length.type.uint32_t_double_pointer;
}
break;
}
return result;
}
bool LengthIsNull(LengthType length) {
switch(length.tag) {
case LENGTH_TYPE_SIZE_T:
case LENGTH_TYPE_UINT32_T:
return false;
case LENGTH_TYPE_SIZE_T_POINTER:
return length.type.size_t_pointer == NULL;
case LENGTH_TYPE_UINT32_T_POINTER:
return length.type.uint32_t_pointer == NULL;
case LENGTH_TYPE_SIZE_T_DOUBLE_POINTER:
return length.type.size_t_double_pointer == NULL ||
*length.type.size_t_double_pointer == NULL;
case LENGTH_TYPE_UINT32_T_DOUBLE_POINTER:
return length.type.uint32_t_double_pointer == NULL ||
*length.type.uint32_t_double_pointer == NULL;
}
assert(false);
return true;
}
void ODK_Pack_bool(Message* message, const bool* value) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_BOOL);
uint8_t b = (*value) ? 1 : 0;
PackBytes(message, &b, sizeof(b));
}
void ODK_Pack_size_t(Message* message, const size_t* value) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_SIZE_T);
uint64_t u64 = *value;
uint8_t buf[sizeof(uint64_t)];
buf[0] = (uint8_t)u64;
buf[1] = (uint8_t)(u64 >> 8);
buf[2] = (uint8_t)(u64 >> 16);
buf[3] = (uint8_t)(u64 >> 24);
buf[4] = (uint8_t)(u64 >> 32);
buf[5] = (uint8_t)(u64 >> 40);
buf[6] = (uint8_t)(u64 >> 48);
buf[7] = (uint8_t)(u64 >> 56);
PackBytes(message, buf, sizeof(buf));
}
void ODK_Pack_c_str(Message* message, const char* value) {
if (!ValidMessage(message) || NullCheck(message, value)) return;
size_t length = strlen(value) + 1;
ODK_PackMemory(message, (const uint8_t*)value, LengthFromSizeT(length));
}
void ODK_Pack_uint8_t(Message* message, const uint8_t* value) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_UINT8);
PackBytes(message, (const uint8_t*)value, sizeof(*value));
}
void ODK_Pack_uint16_t(Message* message, const uint16_t* value) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_UINT16);
uint8_t buf[sizeof(uint16_t)];
buf[0] = (uint8_t)(*value);
buf[1] = (uint8_t)(*value >> 8);
PackBytes(message, buf, sizeof(buf));
}
void ODK_Pack_uint32_t(Message* message, const uint32_t* value) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_UINT32);
uint8_t buf[sizeof(uint32_t)];
buf[0] = (uint8_t)(*value);
buf[1] = (uint8_t)(*value >> 8);
buf[2] = (uint8_t)(*value >> 16);
buf[3] = (uint8_t)(*value >> 24);
PackBytes(message, buf, sizeof(buf));
}
void ODK_Pack_uint64_t(Message* message, const uint64_t* value) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_UINT64);
uint8_t buf[sizeof(uint64_t)];
buf[0] = (uint8_t)(*value);
buf[1] = (uint8_t)(*value >> 8);
buf[2] = (uint8_t)(*value >> 16);
buf[3] = (uint8_t)(*value >> 24);
buf[4] = (uint8_t)(*value >> 32);
buf[5] = (uint8_t)(*value >> 40);
buf[6] = (uint8_t)(*value >> 48);
buf[7] = (uint8_t)(*value >> 56);
PackBytes(message, buf, sizeof(buf));
}
/*
* Convenience function used by the serializer to
* pack a bool without having to allocate a local
* variable.
*/
bool ODK_PackBoolValue(Message* message, bool value) {
ODK_Pack_bool(message, (const bool*)&value);
return value;
}
/*
* Pack a boolean indicating if the pointer value is NULL
*/
bool ODK_PackIsNull(Message* message, const void* pointer) {
return ODK_PackBoolValue(message, pointer == NULL);
}
/*
* Pack a boolean indicating if the address value is NULL. It's
* effectivey the same as PackIsNull but provided for symmetry with
* UnpackAlloc.
*/
void ODK_PackAlloc(Message* message, const void* addr) {
ODK_PackIsNull(message, addr);
}
/*
* Pack a range of memory given by address and length. First pack a
* bool indicating if the address is NULL. If the address is non-null
* and length is non-null then pack the length and |length| bytes from
* memory beginning at |address|.
*/
void ODK_PackMemory(Message* message, const uint8_t* address,
LengthType length) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_MEMORY);
ODK_PackBoolValue(message, address == NULL || LengthIsNull(length));
if (address && !LengthIsNull(length)) {
size_t count = LengthAsSizeT(length);
ODK_Pack_size_t(message, &count);
if (LengthAsSizeT(length) > 0) {
PackBytes(message, address, LengthAsSizeT(length));
}
}
}
/*
* Pack memory representing an array of fundamental types, given by
* address and length.
*/
void ODK_PackArray(Message* message, const uint8_t* address, size_t length) {
ODK_PackMemory(message, address, LengthFromSizeT(length));
}
/*
* Pack a variable length array of objects at the base address given
* by |address|, with |length| elements of size |size|. The ObjPacker is
* a pack function able to pack elements of the array.
*/
void ODK_PackObjArray(Message* message, const uint8_t* objs, LengthType length,
size_t size, ObjPacker packer) {
bool is_null = objs == NULL || LengthIsNull(length);
if (!ODK_PackBoolValue(message, is_null)) {
for (size_t i = 0; i < LengthAsSizeT(length); i++) {
(*packer)(message, objs + i * size);
}
}
}
/*
* On the REE side, if address and length are not null, map the shared
* memory segment specified by index into our address space using the
* shared memory bump allocator and pack fields into the message so
* that the receiver can map the corresponding segment if needed. Copy
* the input data into the shared segment.
*
* Parameters:
* message - The message to pack into
* index - the index that identifies which segment to map
* address - base address of the local memory buffer. If address is
* null, shared memory will not be allocated, which is indicated
* by a packed bool value so the receiver will also know not to
* allocate the segment.
* length - the length of the segment. A segment of this size will
* be mapped if length is non-null and *length is non-zero.
* otherwise the segment will not be mapped.
*/
void ODK_PackSharedInputBuffer(Message* message, uint16_t index,
const uint8_t* address, LengthType length) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_SHARED_MEMORY);
ODK_PackBoolValue(message, address == NULL || LengthIsNull(length));
if (address && !LengthIsNull(length)) {
uint8_t* shared_address = SharedMemory_Allocate(index, LengthAsSizeT(length));
if (shared_address) {
memcpy(shared_address, address, LengthAsSizeT(length));
}
}
}
/*
* Pass information to the receiver indicating whether shared memory
* needs to be allocated for the buffer indicated by |address| and |length|.
*
* Parameters:
* message - The message to pack into
* address - base address of the local memory buffer. If null, a shared
* memory segment will not need to be mapped by the receiver.
* length - the length of the segment. If null, or if the length is 0,
* a shared memory segment will not need to be mapped by the receiver.
*
* Returns:
* void
*/
void ODK_PackSharedOutputBuffer(Message* message, const uint8_t* address,
LengthType length) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_SHARED_MEMORY);
ODK_PackBoolValue(message, address == NULL || LengthIsNull(length));
}
void ODK_PackEOM(Message* message) {
if (!ValidMessage(message)) return;
PackTag(message, TAG_EOM);
}
/**************************** Unpack Functions *******************************/
static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) {
if (!ValidMessage(message)) return;
if (count <= message->size - message->read_offset) {
if (ptr) {
memcpy(ptr, message->base + message->read_offset, count);
}
message->read_offset += count;
} else {
message->status = MESSAGE_STATUS_UNDERFLOW_ERROR;
}
}
static bool CheckTag(Message* message, TagType tag) {
uint8_t byte = TAG_INVALID;
UnpackBytes(message, &byte, sizeof(byte));
if (tag != (TagType)byte) {
message->status = MESSAGE_STATUS_INVALID_TAG_ERROR;
return false;
}
return true;
}
void ODK_Unpack_bool(Message* message, bool* value) {
if (!ValidMessage(message)) return;
if (!CheckTag(message, TAG_BOOL)) return;
uint8_t b = 0;
UnpackBytes(message, &b, sizeof(b));
*value = b ? true : false;
}
void ODK_Unpack_size_t(Message* message, size_t* value) {
if (!ValidMessage(message)) return;
if (!CheckTag(message, TAG_SIZE_T)) return;
uint8_t buf[sizeof(uint64_t)];
UnpackBytes(message, buf, sizeof(buf));
uint64_t u64 = *value;
u64 = buf[0];
u64 |= (uint64_t)buf[1] << 8;
u64 |= (uint64_t)buf[2] << 16;
u64 |= (uint64_t)buf[3] << 24;
u64 |= (uint64_t)buf[4] << 32;
u64 |= (uint64_t)buf[5] << 40;
u64 |= (uint64_t)buf[6] << 48;
u64 |= (uint64_t)buf[7] << 56;
*value = u64;
}
void ODK_Unpack_c_str(Message* message, char** value) {
if (!ValidMessage(message) || NullCheck(message, value)) return;
*value = NULL;
uint8_t* ptr = NULL;
ODK_UnpackPointerToMemory(message, &ptr);
if (ptr) {
/* calculate length, be careful to stay within length of message */
size_t length = 0;
uint8_t* p = ptr;
while (*p && p < message->base + message->size) {
length++;
p++;
}
void* buffer = BumpAllocate(length + 1);
if (buffer) {
memcpy(buffer, ptr, length);
*value = buffer;
}
}
}
void ODK_Unpack_uint8_t(Message* message, uint8_t* value) {
if (!ValidMessage(message)) return;
if (!CheckTag(message, TAG_UINT8)) return;
UnpackBytes(message, (uint8_t*)value, sizeof(*value));
}
void ODK_Unpack_uint16_t(Message* message, uint16_t* value) {
if (!ValidMessage(message)) return;
if (!CheckTag(message, TAG_UINT16)) return;
uint8_t buf[sizeof(uint16_t)];
UnpackBytes(message, buf, sizeof(buf));
*value = buf[0];
*value |= (uint16_t)buf[1] << 8;
}
void ODK_Unpack_uint32_t(Message* message, uint32_t* value) {
if (!ValidMessage(message)) return;
if (!CheckTag(message, TAG_UINT32)) return;
uint8_t buf[sizeof(uint32_t)];
UnpackBytes(message, buf, sizeof(buf));
*value = buf[0];
*value |= (uint32_t)buf[1] << 8;
*value |= (uint32_t)buf[2] << 16;
*value |= (uint32_t)buf[3] << 24;
}
void ODK_Unpack_uint64_t(Message* message, uint64_t* value) {
if (!ValidMessage(message)) return;
if (!CheckTag(message, TAG_UINT64)) return;
uint8_t buf[sizeof(uint64_t)];
UnpackBytes(message, buf, sizeof(buf));
*value = buf[0];
*value |= (uint64_t)buf[1] << 8;
*value |= (uint64_t)buf[2] << 16;
*value |= (uint64_t)buf[3] << 24;
*value |= (uint64_t)buf[4] << 32;
*value |= (uint64_t)buf[5] << 40;
*value |= (uint64_t)buf[6] << 48;
*value |= (uint64_t)buf[7] << 56;
}
bool ODK_UnpackBoolValue(Message* message) {
bool value = false;
ODK_Unpack_bool(message, &value);
return value;
}
/* Return true if the pointer was packed as NULL */
bool ODK_UnpackIsNull(Message* message) { return ODK_UnpackBoolValue(message); }
/*
* If the pointer was packed as NULL, return NULL. Otherwise
* return the number of bytes of memory from the bump allocator
* requested by |size|
*/
uint8_t* ODK_UnpackAlloc(Message* message, size_t size) {
return ODK_UnpackIsNull(message) ? NULL : BumpAllocate(size);
}
/*
* If the pointer was packed as NULL, return NULL. Otherwise
* return the number of bytes of memory from the bump allocator
* requested by |size|
*/
uint8_t* ODK_UnpackAllocBuffer(Message* message, LengthType length, size_t size) {
uint8_t* buffer = NULL;
if (!ODK_UnpackIsNull(message) && !LengthIsNull(length)) {
buffer = BumpAllocate(LengthAsSizeT(length) * size);
}
return buffer;
}
/*
* If the pointer was packed as NULL, return NULL. Otherwise unpack
* the array of objects into memory allocated from the bump allocator.
*/
void ODK_UnpackObjArray(Message* message, uint8_t** address, LengthType count,
size_t size, ObjUnpacker unpacker) {
if (address) {
*address = NULL;
}
if (!ODK_UnpackIsNull(message)) {
if (address && !LengthIsNull(count)) {
*address = BumpAllocate(LengthAsSizeT(count) * size);
if (*address) {
for (size_t i = 0; i < LengthAsSizeT(count); i++) {
(*unpacker)(message, (*address) + size * i);
}
}
}
}
}
/*
* Unpack a range of memory representing an array of fundamental types
* given a pointer to a buffer and a length. The caller is responsible
* for ensuring that the buffer is large enough to handle the number
* of bytes specified by |length|. First unpack a bool that indicates
* if the pointer to the memory is NULL and if not unpack the
* requested number of bytes into the address specified by |address|.
*/
void ODK_UnpackArray(Message* message, uint8_t* address, size_t length) {
if (!ValidMessage(message) || NullCheck(message, address)) return;
uint8_t* array = NULL;
ODK_UnpackPointerToMemory(message, &array);
if (array) {
memcpy(address, array, length);
}
}
/*
* Unpack a pointer to memory within the received message and return
* it. First unpack a bool that indicates if the address was passed in
* as NULL. If so set *address to NULL and return. Otherwise set
* *|address| to the current message read offset and unpack the length
* of the memory range. Then increment the message read offset by that
* amount.
*/
void ODK_UnpackPointerToMemory(Message* message, uint8_t** address) {
if (!ValidMessage(message) || NullCheck(message, address)) return;
if (!CheckTag(message, TAG_MEMORY)) return;
bool is_null = true;
ODK_Unpack_bool(message, &is_null);
if (is_null) {
*address = NULL;
return;
} else {
size_t length;
ODK_Unpack_size_t(message, &length);
size_t new_offset;
if (__builtin_add_overflow(message->read_offset, length, &new_offset) ||
new_offset > message->size) {
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
} else {
*address = message->base + message->read_offset;
message->read_offset = new_offset;
}
}
}
/*
* Unpack fields from |message| and use them to map a shared memory
* segment using the shared memory bump allocator. If the packed bool
* indicates that the output buffer is non-null, allocate a shared
* memory segment identified by |index| of the size indicated by
* |length|.
*
* Parameters:
* message - the message to unpack from
* index - the index that identifies which segment to map
* length - the length of the segment
* Returns:
* The address of the mapped segment
*/
uint8_t* ODK_UnpackSharedBuffer(Message* message, uint16_t index,
LengthType length) {
if (!ValidMessage(message)) return NULL;
if (!CheckTag(message, TAG_SHARED_MEMORY)) return NULL;
if (!ODK_UnpackIsNull(message)) {
return SharedMemory_Allocate(index, LengthAsSizeT(length));
}
return NULL;
}
/*
* In the REE, unpack fields from |message| and use them to map a
* shared memory segment using the shared memory bump allocator. If
* the packed bool indicates that the output buffer is non-null,
* allocate a shared memory segment identified by |index| of the size
* indicated by |length|. Copy the buffer from shared memory to
* the OEMCrypto API parameter.
*
* Parameters:
* message - the message to unpack from
* address - the OEMCrypto API parameter for the output buffer
* index - the index that identifies which segment to map
* length - the length of the segment
*/
void ODK_UnpackSharedOutputBuffer(Message* message, uint16_t index,
uint8_t** address, LengthType length) {
if (address && !LengthIsNull(length)) {
uint8_t* shared_address = ODK_UnpackSharedBuffer(message, index, length);
if (*address && LengthAsSizeT(length) > 0) {
memcpy(*address, shared_address, LengthAsSizeT(length));
}
}
}
/* end of message */
void ODK_UnpackEOM(Message* message) {
if (!ValidMessage(message)) return;
if (!CheckTag(message, TAG_EOM)) return;
if (message->read_offset != message->size) {
message->status = MESSAGE_STATUS_END_OF_MESSAGE_ERROR;
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.
*/
#ifndef ODKITEE_SERIALIZATION_BASE_H_
#define ODKITEE_SERIALIZATION_BASE_H_
#include <stddef.h>
#include <stdint.h>
#include "OEMCryptoCENC.h"
#include "message.h"
#include "shared_memory_interface.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef char* c_str;
/*
* In the OEMCrypto API, the lengths of variable length buffers can be
* of various types: base types of size_t or uint32_t and [in] values
* or [in/out] pointers. The LengthType struct is used to pass
* lengths in a consistent way to the primitives that take lengths as
* arguments.
*/
typedef enum {
LENGTH_TYPE_SIZE_T,
LENGTH_TYPE_UINT32_T,
LENGTH_TYPE_SIZE_T_POINTER,
LENGTH_TYPE_UINT32_T_POINTER,
LENGTH_TYPE_SIZE_T_DOUBLE_POINTER,
LENGTH_TYPE_UINT32_T_DOUBLE_POINTER
} LengthTypeTag;
typedef struct {
LengthTypeTag tag;
union {
size_t size_t_value;
uint32_t uint32_t_value;
const size_t* size_t_pointer;
const uint32_t* uint32_t_pointer;
size_t** size_t_double_pointer;
uint32_t** uint32_t_double_pointer;
} type;
} LengthType;
LengthType LengthFromSizeT(size_t length);
LengthType LengthFromUint32T(uint32_t length);
LengthType LengthFromSizeTPointer(const size_t* length);
LengthType LengthFromUint32TPointer(const uint32_t* length);
LengthType LengthFromSizeTDoublePointer(size_t** length);
LengthType LengthFromUint32TDoublePointer(uint32_t** length);
size_t LengthAsSizeT(LengthType length_type);
bool LengthIsNull(LengthType length_type);
void ODK_Pack_bool(Message* message, const bool* value);
void ODK_Pack_size_t(Message* message, const size_t* value);
void ODK_Pack_c_str(Message* message, const char* value);
void ODK_Pack_uint8_t(Message* message, const uint8_t* value);
void ODK_Pack_uint16_t(Message* message, const uint16_t* value);
void ODK_Pack_uint32_t(Message* message, const uint32_t* value);
void ODK_Pack_uint64_t(Message* message, const uint64_t* value);
bool ODK_PackBoolValue(Message* message, bool value);
bool ODK_PackIsNull(Message* message, const void* value);
void ODK_PackAlloc(Message* message, const void* address);
void ODK_PackMemory(Message* message, const uint8_t* addr,
LengthType length);
void ODK_PackArray(Message* message, const uint8_t* addr, size_t length);
typedef void (*ObjPacker)(Message* message, const uint8_t* obj);
void ODK_PackObjArray(Message* message, const uint8_t* address,
LengthType length, size_t size, ObjPacker packer);
void ODK_PackEOM(Message* message); /* end of message */
void ODK_PackSharedInputBuffer(Message* message, uint16_t index,
const uint8_t* address, const LengthType);
void ODK_PackSharedOutputBuffer(Message* message, const uint8_t* address,
LengthType length);
void ODK_Unpack_bool(Message* message, bool* value);
void ODK_Unpack_size_t(Message* message, size_t* value);
void ODK_Unpack_c_str(Message* message, char** value);
void ODK_Unpack_uint8_t(Message* message, uint8_t* value);
void ODK_Unpack_uint16_t(Message* message, uint16_t* value);
void ODK_Unpack_uint32_t(Message* message, uint32_t* value);
void ODK_Unpack_uint64_t(Message* message, uint64_t* value);
bool ODK_UnpackBoolValue(Message* message);
bool ODK_UnpackIsNull(Message* message);
uint8_t* ODK_UnpackAlloc(Message* message, size_t size);
uint8_t* ODK_UnpackAllocBuffer(Message* message, LengthType length,
size_t size);
void ODK_UnpackArray(Message* message, uint8_t* addr, size_t length);
typedef void (*ObjUnpacker)(Message* message, uint8_t* obj);
void ODK_UnpackObjArray(Message* message, uint8_t** address,
LengthType length, size_t size,
ObjUnpacker unpacker);
void ODK_UnpackPointerToMemory(Message* message, uint8_t** addr);
uint8_t* ODK_UnpackSharedBuffer(Message* message, uint16_t index,
LengthType length);
void ODK_UnpackSharedInputBuffer(Message* message, uint16_t index,
uint8_t** address, LengthType length);
void ODK_UnpackSharedOutputBuffer(Message* message, uint16_t index,
uint8_t** address, LengthType length);
void ODK_UnpackEOM(Message* message);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ODKITEE_SERIALIZATION_BASE_H_

View File

@@ -0,0 +1,260 @@
/*
* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#include <string.h>
#include "shared_memory_allocator.h"
#include "shared_memory_interface.h"
#include "oemcrypto_overflow.h"
/*
* This file implements the allocator for shared memory. Shared memory
* is managed using two different schemes. There is a memory region,
* called the "pool" that is allocated once at initialization and kept
* open for the lifetime of OEMCrypto, and discrete allocations that
* are made on each OEMCrypto call where additional shared memory is
* required.
*
* Every shared memory allocation is identified by an index number,
* which is provided by the caller. Allowing the caller to specify the
* index allows the code generator to refer to specific regions
* explicitly, which is needed to correlate segments across
* serialization endpoints. The index numbers start from 0. Given an
* index number, the address and size of the allocation can be
* returned. The code generator requires that the allocation algorithm
* be deterministic and repeatable, i.e. that a specific sequence of
* allocations when replayed after a reset will result in the same
* shared memory regions being mapped to the same indexes.
*
* The memory pool is managed with a bump allocator. The pool is
* allocated from the porting layer's ODK_SharedMemory interface. The
* pool is divided into "segments" which are allocated consecutively
* from the pool by AllocateSegment. The allocator is reset before
* each api call, which removes all segments.
*
* Shared memory allocations that are too large to put in the pool are
* allocated directly from ODK_SharedMemory and are released at the
* end of each OEMCrypto call. A threshold is defined (e.g. pool
* size/4) that determines whether allocations are from the pool or
* from the discrete regions.
*/
/* Region ID for the shared memory pool */
#define SHARED_MEMORY_POOL_REGION_ID 0
/* The ID of the next discrete region to allocate */
static size_t next_discrete_region_id_ = SHARED_MEMORY_POOL_REGION_ID + 1;
/*
* Descriptor for a segment allocated from the pool.
*/
typedef struct {
uint32_t size;
uint32_t offset;
} Segment;
/*
* Descriptor for an allocation, which may be either from the pool
* or from a discrete region.
*/
typedef enum {FREE, POOL_SEGMENT, DISCRETE_REGION} AllocationType;
typedef struct {
AllocationType type;
union {
Segment segment;
ODK_SharedHandle* handle;
} u;
} Allocation;
/*
* Maximum number of allocations allowed. Indexes go from 0 to
* MAX_ALLOCATIONS - 1
*/
#define MAX_ALLOCATIONS 16
static Allocation allocations_[MAX_ALLOCATIONS];
/*
* Shared memory handle for the bump allocator pool
*/
static ODK_SharedHandle *pool_handle_;
/*
* The offset from pool_address of the next segment that will be
* allocated.
*/
static size_t next_segment_offset_ = 0;
/*
* Initial size of the memory pool
*/
static size_t pool_size_ = 0;
/*
* Initialize shared memory state variables. Set the initial pool size
* to the specified value.
*/
void SharedMemory_Initialize(size_t pool_size) {
ODK_SharedMemory_Initialize();
memset(&allocations_[0], 0, sizeof(allocations_));
pool_size_ = pool_size;
pool_handle_ = NULL;
next_discrete_region_id_ = SHARED_MEMORY_POOL_REGION_ID + 1;
}
/*
* Release all shared memory resources from the current process.
*/
void SharedMemory_Terminate(void) {
SharedMemory_Reset();
ODK_SharedMemory_Free(pool_handle_);
pool_handle_ = NULL;
ODK_SharedMemory_Terminate();
}
/*
* Allocate shared memory as a discrete region
*/
static uint8_t* AllocateAsRegion(uint16_t index, size_t size) {
ODK_SharedHandle* handle =
ODK_SharedMemory_Allocate(next_discrete_region_id_, size);
if (handle) {
Allocation* alloc = &allocations_[index];
alloc->type = DISCRETE_REGION;
alloc->u.handle = handle;
next_discrete_region_id_++;
}
return ODK_SharedMemory_GetAddress(handle);
}
/*
* Allocate a segment of shared memory from the pool
*/
static uint8_t* AllocateFromPool(uint16_t index, size_t size) {
if (!pool_handle_) {
pool_handle_ = ODK_SharedMemory_Allocate(SHARED_MEMORY_POOL_REGION_ID,
pool_size_);
if (!pool_handle_) {
return NULL;
}
next_segment_offset_ = 0;
}
size_t new_offset = 0;
if (AddOverflowUX(next_segment_offset_, size, &new_offset)) {
return NULL;
}
if (new_offset > ODK_SharedMemory_GetSize(pool_handle_)) {
return AllocateAsRegion(index, size);
}
uint8_t* segment_address =
ODK_SharedMemory_GetAddress(pool_handle_) + next_segment_offset_;
Allocation* alloc = &allocations_[index];
alloc->type = POOL_SEGMENT;
Segment* segment = &alloc->u.segment;
segment->size = size;
segment->offset = next_segment_offset_;
next_segment_offset_ = new_offset;
return segment_address;
}
/*
* Allocate shared memory from either the pool or a discrete region.
* If the requested memory size is larger than 1/4 of the pool size, or
* if the pool is full, allocate as a discrete region.
*
* Parameters:
* index - the index number of the allocation, assigned by the caller.
* indexes start at 0. The index cannot exceed MAX_ALLOCATIONS.
*
* Returns:
* The address of the allocation or NULL if the index has already
* been allocated or allocation fails.
*/
uint8_t* SharedMemory_Allocate(uint16_t index, size_t size) {
if (index >= MAX_ALLOCATIONS || allocations_[index].type != FREE) {
return NULL;
}
if (size <= pool_size_ / 4) {
return AllocateFromPool(index, size);
} else {
return AllocateAsRegion(index, size);
}
}
/*
* Get the size of a shared memory segment.
*
* Parameters:
* index - identifies the allocation. Valid values are
* 0..MAX_ALLOCATIONS-1
*
* Returns:
* The size of the allocation indicated by |index| or 0 if the
* index is invalid or no matching allocation was found
*/
size_t SharedMemory_GetSize(uint16_t index) {
if (index >= MAX_ALLOCATIONS) {
return 0;
}
Allocation* alloc = &allocations_[index];
switch (alloc->type) {
case DISCRETE_REGION:
return ODK_SharedMemory_GetSize(alloc->u.handle);
case POOL_SEGMENT:
return alloc->u.segment.size;
default:
break;
}
return 0;
}
/*
* Get the address of a shared memory segment.
*
* Parameters:
* index - identifies the allocation. Valid values are
* 0..MAX_ALLOCATIONS-1
*
* Returns:
* The address of the allocation indicated by |index| or NULL if the
* index is invalid or no matching allocation was found
*/
uint8_t* SharedMemory_GetAddress(uint16_t index) {
if (index >= MAX_ALLOCATIONS) {
return NULL;
}
Allocation* alloc = &allocations_[index];
switch (alloc->type) {
case DISCRETE_REGION:
return ODK_SharedMemory_GetAddress(alloc->u.handle);
case POOL_SEGMENT:
if (!pool_handle_) {
return NULL;
}
return ODK_SharedMemory_GetAddress(pool_handle_) +
alloc->u.segment.offset;
default:
break;
}
return NULL;
}
/*
* Set the allocator to its initial state, where there are no segments
* allocated from the pool and there are no discrete allocations.
*/
void SharedMemory_Reset(void) {
next_segment_offset_ = 0;
for (size_t i = 0; i < MAX_ALLOCATIONS; i++) {
Allocation* alloc = &allocations_[i];
if (alloc->type == DISCRETE_REGION) {
ODK_SharedMemory_Free(alloc->u.handle);
}
}
memset(&allocations_[0], 0, sizeof(allocations_));
next_discrete_region_id_ = SHARED_MEMORY_POOL_REGION_ID + 1;
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#ifndef ODKITEE_SHARED_MEMORY_ALLOCATOR_H_
#define ODKITEE_SHARED_MEMORY_ALLOCATOR_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/*
* An allocator for shared memory
*/
void SharedMemory_Initialize(size_t initial_pool_size);
uint8_t* SharedMemory_Allocate(uint16_t index, size_t size);
size_t SharedMemory_GetSize(uint16_t index);
uint8_t* SharedMemory_GetAddress(uint16_t index);
void SharedMemory_Reset(void);
void SharedMemory_Terminate(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //ODKITEE_SHARED_MEMORY_ALLOCATOR_H_

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
* source code may only be used and distributed under the Widevine Master
* License Agreement.
*/
#ifndef ODKITEE_SHARED_MEMORY_INTERFACE_H_
#define ODKITEE_SHARED_MEMORY_INTERFACE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/*
* This is the interface to shared memory that must be implemented in
* a port for a specific trusted OS. The interface defines functions
* to allocate, access and free regions of shared memory.
*
* Shared memory regions are defined by an address and a size. Regions
* are allocated by specifying an integer |region_id| that is used to
* correlate remote and local mappings. When a region is created, the
* implementation returns an ODK_SharedHandle which is used by callers
* when accessing the region.
*
* Each shared memory implementation defines its own _ODK_SharedHandle
* struct to hold the data needed to represent a shared memory region.
*/
typedef struct _ODK_SharedHandle ODK_SharedHandle;
/*
* Shared memory regions are identified with an id that correlates the
* remote and local mappings.
*/
typedef uint16_t ODK_SharedRegionId;
/*
* Note that the shared memory implementation must be initialized
* prior to calling any OEMCrypto API functions because it needs to be
* available when communicating with the TEE. Typically it would be
* initialized on service startup.
*/
/*
* Initialize the shared memory implementation, must be called prior
* to using any other ODK_SharedMemory functions. This function will
* be called by the dispatcher on the TEE side at startup. On the REE
* side it must be called by the service that is calling OEMCrypto
* because the communication with the TEE needs to be established
* before any OEMCrypto calls are made.
*/
void ODK_SharedMemory_Initialize(void);
/*
* Terminate the shared memory implementation, releasing any resources
* in use.
*/
void ODK_SharedMemory_Terminate(void);
/*
* Allocate a region of shared memory of |size| bytes. Regions are
* allocated based on a specified id that is used to correlate remote
* and local mappings. The shared memory region must remain mapped
* until released by ODK_SharedMemory_Free.
*
* Parameters:
* region_id - identifies the region to allocate
* size - the size of the memory region to allocate
*
* Returns:
* A pointer to an ODK_SharedHandle which refers to the allocated
* shared memory, or NULL if allocation fails. The memory for the
* handle is owned by the implementation. The handle must remain
* valid for as long as the shared memory region is allocated.
*/
ODK_SharedHandle* ODK_SharedMemory_Allocate(ODK_SharedRegionId region_id,
size_t size);
/*
* Return the address that a shared memory handle is mapped to.
*
* Parameters:
* handle - the shared handle for which an address is requested
*
* Returns:
* The address of the shared memory region refered by |handle|
*/
uint8_t* ODK_SharedMemory_GetAddress(ODK_SharedHandle* handle);
/*
* Return the size of a shared memory region
*
* Parameters:
* handle - the shared handle for which the region size is requested
*
* Returns:
* The size of the shared memory region refered by |handle|
*/
size_t ODK_SharedMemory_GetSize(ODK_SharedHandle* handle);
/*
* Release a handle for a previously allocated region of shared memory.
* The memory is unmapped and any associated resources are released.
*
* Parameters:
* address - the address of the shared memory region to release
*/
void ODK_SharedMemory_Free(ODK_SharedHandle* handle);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //ODKITEE_SHARED_MEMORY_INTERFACE_H_

View File

@@ -0,0 +1,131 @@
/*
* 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 "special_cases.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "api_support.h"
#include "deserializer.h"
#include "OEMCryptoCENC.h"
#include "serialization_base.h"
#include "shared_memory_allocator.h"
#include "shared_memory_interface.h"
#include "serializer.h"
/*
* Special case API functions. DecryptCENC and CopyBuffer have the
* |out_buffer| parameter of type OEMCrypt_DestBufferDesc. If the
* destination is to non-secure memory, the output data needs to be
* copied out from shared memory to the provided destination buffer.
* The length of the data is given by the seemingly unrelated input
* parameter data_addr_length so it's difficult to auto-generate these
* functions.
*
* To update these functions when the api generator changes, remove them
* from special_case_config.cpp, build, then copy the generated code
* from oemcrypto_api.c and apply the manual edits.
*/
OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC(
OEMCrypto_SESSION session, const SharedMemory *data_addr,
size_t data_addr_length, bool is_encrypted, const uint8_t *iv,
size_t block_offset, OEMCrypto_DestBufferDesc *out_buffer,
const OEMCrypto_CENCEncryptPatternDesc *pattern, uint8_t subsample_flags) {
OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
pthread_mutex_lock(&api_lock);
Message *request = API_InitializeRequest();
Message *response = NULL;
if (!request) {
goto cleanup_and_return;
}
ODK_Pack_DecryptCENC_Request(request, session, data_addr, data_addr_length, is_encrypted,
iv, block_offset, out_buffer, pattern, subsample_flags);
if (GetStatus(request) != MESSAGE_STATUS_OK) {
api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup_and_return;
}
response = API_Transact(request);
if (!response) {
goto cleanup_and_return;
}
ODK_Unpack_DecryptCENC_Response(response, &result, &out_buffer);
/* Only this block is hand coded */
if (result == OEMCrypto_SUCCESS) {
if (out_buffer->type == OEMCrypto_BufferType_Clear) {
uint8_t* shared_address = SharedMemory_GetAddress(DEST_BUFFER_INDEX);
if (data_addr_length <= SharedMemory_GetSize(DEST_BUFFER_INDEX)) {
memcpy(out_buffer->buffer.clear.address, shared_address, data_addr_length);
} else {
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup_and_return;
}
}
}
if (GetStatus(response) != MESSAGE_STATUS_OK) {
api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup_and_return;
}
cleanup_and_return:
if (request) {
ODK_Transport_DeallocateMessage(request);
}
if (response) {
ODK_Transport_DeallocateMessage(response);
}
pthread_mutex_unlock(&api_lock);
return API_CheckResult(result);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
OEMCrypto_SESSION session, const SharedMemory *data_addr,
size_t data_addr_length, OEMCrypto_DestBufferDesc *out_buffer,
uint8_t subsample_flags) {
OEMCryptoResult result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
pthread_mutex_lock(&api_lock);
Message *request = API_InitializeRequest();
Message *response = NULL;
if (!request) {
goto cleanup_and_return;
}
ODK_Pack_CopyBuffer_Request(request, session, data_addr, data_addr_length,
out_buffer, subsample_flags);
if (GetStatus(request) != MESSAGE_STATUS_OK) {
api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup_and_return;
}
response = API_Transact(request);
if (!response) {
goto cleanup_and_return;
}
ODK_Unpack_CopyBuffer_Response(response, &result, &out_buffer);
if (GetStatus(response) != MESSAGE_STATUS_OK) {
api_result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
goto cleanup_and_return;
}
/* Only this block is hand coded */
if (result == OEMCrypto_SUCCESS) {
if (out_buffer->type == OEMCrypto_BufferType_Clear) {
uint8_t* shared_address = SharedMemory_GetAddress(DEST_BUFFER_INDEX);
memcpy(out_buffer->buffer.clear.address, shared_address, data_addr_length);
}
}
cleanup_and_return:
if (request) {
ODK_Transport_DeallocateMessage(request);
}
if (response) {
ODK_Transport_DeallocateMessage(response);
}
pthread_mutex_unlock(&api_lock);
return API_CheckResult(result);
}

View File

@@ -0,0 +1,202 @@
/*
* 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 "special_cases.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "deserializer.h"
#include "OEMCryptoCENC.h"
#include "serialization_base.h"
#include "shared_memory_allocator.h"
#include "shared_memory_interface.h"
#include "serializer.h"
/*
* Special cases due to union & shared memory
*/
/*
* Pack the destination buffer parameter to OEMCrypto_DecryptCENC and
* OEMCrypto_CopyBuffer
*/
void ODK_Pack_OEMCrypto_DestBufferDesc(Message* message,
const OEMCrypto_DestBufferDesc* obj) {
if (obj == NULL) {
SetStatus(message, MESSAGE_STATUS_NULL_POINTER_ERROR);
return;
}
ODK_Pack_uint32_t(message, (uint32_t*)&obj->type);
switch (obj->type) {
case OEMCrypto_BufferType_Clear: {
ODK_Pack_size_t(message, &obj->buffer.clear.max_length);
ODK_PackSharedOutputBuffer(message, obj->buffer.clear.address,
LengthFromSizeT(obj->buffer.clear.max_length));
break;
}
case OEMCrypto_BufferType_Secure:
/* secure memory - pass handle, length, offset */
ODK_Pack_uint64_t(message, (uint64_t*)&obj->buffer.secure.handle);
ODK_Pack_size_t(message, &obj->buffer.secure.max_length);
ODK_Pack_size_t(message, &obj->buffer.secure.offset);
break;
case OEMCrypto_BufferType_Direct:
ODK_Pack_bool(message, &obj->buffer.direct.is_video);
break;
}
}
/*
* Unpack the destination buffer parameter to OEMCrypto_DecryptCENC and
* OEMCrypto_CopyBuffer
*/
void ODK_Unpack_OEMCrypto_DestBufferDesc(Message* message,
OEMCrypto_DestBufferDesc* obj) {
if (obj == NULL) {
SetStatus(message, MESSAGE_STATUS_NULL_POINTER_ERROR);
return;
}
ODK_Unpack_uint32_t(message, (uint32_t*)&obj->type);
switch (obj->type) {
case OEMCrypto_BufferType_Clear: {
ODK_Unpack_size_t(message, &obj->buffer.clear.max_length);
uint8_t* shared_address =
ODK_UnpackSharedBuffer(message, DEST_BUFFER_INDEX,
LengthFromSizeT(obj->buffer.clear.max_length));
if (!obj->buffer.clear.address) {
// unpacking request in TEE - set the address to shared memory
obj->buffer.clear.address = shared_address;
}
break;
}
case OEMCrypto_BufferType_Secure:
/* secure memory handle - deal with later */
ODK_Unpack_uint64_t(message, (uint64_t*)&obj->buffer.secure.handle);
ODK_Unpack_size_t(message, &obj->buffer.secure.max_length);
ODK_Unpack_size_t(message, &obj->buffer.secure.offset);
break;
case OEMCrypto_BufferType_Direct:
ODK_Unpack_bool(message, &obj->buffer.direct.is_video);
break;
}
}
/*
* Special serialization cases due to some parameters being defined as
* pointers into other parameters.
*/
void ODK_Pack_RewrapDeviceRSAKey_Request(
Message* msg, 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,
const uint8_t* wrapped_rsa_key,
const size_t* wrapped_rsa_key_length) {
uint32_t api_value = 18; /* from _oecc18 */
ODK_Pack_uint32_t(msg, &api_value);
ODK_Pack_size_t(msg, &message_length);
ODK_Pack_size_t(msg, &signature_length);
ODK_Pack_size_t(msg, &enc_rsa_key_length);
ODK_PackNullable_size_t(msg, wrapped_rsa_key_length);
ODK_Pack_uint32_t(msg, &session);
ODK_PackSharedInputBuffer(msg, 0, message, LengthFromSizeT(message_length));
ODK_PackSharedInputBuffer(msg, 1, signature, LengthFromSizeT(signature_length));
size_t unaligned_nonce_offset = 0;
if (__builtin_sub_overflow((uintptr_t)unaligned_nonce, (uintptr_t)message,
&unaligned_nonce_offset)) {
SetStatus(msg, MESSAGE_STATUS_PARSE_ERROR);
return;
}
ODK_Pack_size_t(msg, &unaligned_nonce_offset);
size_t enc_rsa_key_offset = 0;
if (__builtin_sub_overflow((uintptr_t)enc_rsa_key, (uintptr_t)message,
&enc_rsa_key_offset)) {
SetStatus(msg, MESSAGE_STATUS_PARSE_ERROR);
return;
}
ODK_Pack_size_t(msg, &enc_rsa_key_offset);
size_t enc_rsa_key_iv_offset = 0;
if (__builtin_sub_overflow((uintptr_t)enc_rsa_key_iv, (uintptr_t)message,
&enc_rsa_key_iv_offset)) {
SetStatus(msg, MESSAGE_STATUS_PARSE_ERROR);
return;
}
ODK_Pack_size_t(msg, &enc_rsa_key_iv_offset);
ODK_PackAlloc(msg, wrapped_rsa_key);
ODK_PackEOM(msg);
}
void ODK_Unpack_RewrapDeviceRSAKey_Request(
Message* msg, OEMCrypto_SESSION* session,
SharedMemory** message,
size_t* message_length,
SharedMemory** signature,
size_t* signature_length,
uint32_t** unaligned_nonce,
uint8_t** enc_rsa_key,
size_t* enc_rsa_key_length,
uint8_t** enc_rsa_key_iv,
uint8_t** wrapped_rsa_key,
size_t** wrapped_rsa_key_length) {
uint32_t api_value = ~(uint32_t)0;
ODK_Unpack_uint32_t(msg, &api_value);
if (api_value != 18) SetStatus(msg, MESSAGE_STATUS_API_VALUE_ERROR);
ODK_Unpack_size_t(msg, message_length);
ODK_Unpack_size_t(msg, signature_length);
ODK_Unpack_size_t(msg, enc_rsa_key_length);
ODK_UnpackNullable_size_t(msg, wrapped_rsa_key_length);
ODK_Unpack_uint32_t(msg, session);
ODK_UnpackSharedBuffer(msg, 0, LengthFromSizeTPointer(message_length));
ODK_UnpackSharedBuffer(msg, 1, LengthFromSizeTPointer(signature_length));
*message = SharedMemory_GetAddress(0);
*signature = SharedMemory_GetAddress(1);
size_t unaligned_nonce_offset = 0;
uintptr_t unaligned_nonce_ptr = 0;
ODK_Unpack_size_t(msg, &unaligned_nonce_offset);
if (__builtin_add_overflow((uintptr_t)*message, unaligned_nonce_offset,
&unaligned_nonce_ptr)) {
SetStatus(msg, MESSAGE_STATUS_PARSE_ERROR);
return;
}
*unaligned_nonce = (uint32_t*)unaligned_nonce_ptr;
size_t enc_rsa_key_offset = 0;
uintptr_t enc_rsa_key_ptr = 0;
ODK_Unpack_size_t(msg, &enc_rsa_key_offset);
if (__builtin_add_overflow((uintptr_t)*message, enc_rsa_key_offset,
&enc_rsa_key_ptr)) {
SetStatus(msg, MESSAGE_STATUS_PARSE_ERROR);
return;
}
*enc_rsa_key = (uint8_t*)enc_rsa_key_ptr;
size_t enc_rsa_key_iv_offset = 0;
uintptr_t enc_rsa_key_iv_ptr = 0;
ODK_Unpack_size_t(msg, &enc_rsa_key_iv_offset);
if (__builtin_add_overflow((uintptr_t)*message, enc_rsa_key_iv_offset,
&enc_rsa_key_iv_ptr)) {
SetStatus(msg, MESSAGE_STATUS_PARSE_ERROR);
return;
}
*enc_rsa_key_iv = (uint8_t*)enc_rsa_key_iv_ptr;
*wrapped_rsa_key = (uint8_t*)ODK_UnpackAllocBuffer(
msg, LengthFromSizeTDoublePointer(wrapped_rsa_key_length),
sizeof(uint8_t));
ODK_UnpackEOM(msg);
}

View File

@@ -0,0 +1,64 @@
/*
* 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.
*/
#ifndef ODKITEE_SPECIAL_CASES_H_
#define ODKITEE_SPECIAL_CASES_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include "OEMCryptoCENC.h"
#include "message.h"
/* shared memory index used for destination buffers */
#define DEST_BUFFER_INDEX 3
/*
* Special cases due to union & shared memory
*/
void ODK_Pack_OEMCrypto_DestBufferDesc(Message* msg,
const OEMCrypto_DestBufferDesc* obj);
void ODK_Unpack_OEMCrypto_DestBufferDesc(Message* msg,
OEMCrypto_DestBufferDesc* obj);
/*
* Special cases due to parameters defined as having pointers into other parameters
*/
void ODK_Pack_RewrapDeviceRSAKey_Request(
Message* msg, 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,
const uint8_t* wrapped_rsa_key,
const size_t* wrapped_rsa_key_length);
void ODK_Unpack_RewrapDeviceRSAKey_Request(
Message* msg, OEMCrypto_SESSION* session,
SharedMemory** message,
size_t* message_length,
SharedMemory** signature,
size_t* signature_length,
uint32_t** unaligned_nonce,
uint8_t** enc_rsa_key,
size_t* enc_rsa_key_length,
uint8_t** enc_rsa_key_iv,
uint8_t** wrapped_rsa_key,
size_t** wrapped_rsa_key_length);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ODKITEE_SPECIAL_CASES_H_

View File

@@ -0,0 +1,116 @@
/*
* 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.
*/
#ifndef ODKITEE_TRANSPORT_INTERFACE_H_
#define ODKITEE_TRANSPORT_INTERFACE_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* The Transport Interface is used by the oemcrypto library running
* on the REE side/HLOS. It connects liboemcrypto to the trusted
* OS's method of transporting data between the REE and the TEE.
*
* The trusted OS must provide primitives to transport data segments
* between the REE and TEE. The maximum size of the data segments may
* be fixed and predetermined by the trusted OS. However, the
* transport implementation must be able to append data segments
* sequentially to transport arbitrarily sized messages. For example
* if the maximum block size is 2 KiB and a message is serialized that
* requires 6 KiB, the transport layer must be able to extend the
* messsage to three logically consecutive 2 KiB segments during
* serialization to contain the message. Messages may be initially
* allocated at any block size. The serialization layer will call back
* into the transport layer to request that additional blocks be added
* to the message as needed. The serialization layer will attempt to
* minimize the actual size of messages by passing larger parameters
* in shared memory, if supported by the trusted OS.
*
* Functions need to be provided to allocate, extend and deallocate
* messages, and send and receive messages between the REE and the
* TEE.
*/
typedef enum {
/*
* ODK_TRANSPORT_STATUS_OK must be returned from transport functions
* if the requested operation completed succesfully.
*/
ODK_TRANSPORT_STATUS_OK,
/*
* ODK_TRANSPORT_STATUS_ALLOC_FAILED must be returned from
* ODK_Transport_ExtendMessage if there is insufficient memory. This
* is a fatal failure that will cause OEMCrypto to return
* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES.
*/
ODK_TRANSPORT_STATUS_ALLOC_FAILED,
/*
* ODK_TRANSPORT_STATUS_IO_ERROR must be returned from
* ODK_Transport_SendMessage or ODK_Transport_ReceiveMessage if the
* transport interface was unable to deliver or receive a message
* for any reason. The transport implementation should be designed
* to be robust against communication failures, e.g. by providing
* logic to retry delivery or other techniques if possible. The
* odkitee library does not make any attempts to improve
* communcation reliability using these techniques.
*
* This return code indicates a fatal failure of the current command
* which will result in OEMCrypto returning
* OEMCrypto_ERROR_SYSTEM_INVALIDATED. This will cause the app to be
* notified that the drm system must be reinitialized.
*/
ODK_TRANSPORT_STATUS_IO_ERROR,
} ODK_Transport_Status;
/*
* Allocate a new message of a size that is determined by the
* transport implementation, which is most likely the maximum data
* segment transport size of the trusted OS. The message will be
* subsequently extended if it is too small for the message currently
* being serialized. Must return NULL if the transport implementation
* is unable to allocate a message of any size. Only two messages will
* be simultaneously allocated at any time.
*/
Message* ODK_Transport_AllocateMessage(void);
/*
* Extend an existing message to a new size. The new size is the total
* required size of the message including the currently allocation
* portion. Return ODK_TRANSPORT_STATUS_ALLOC_FAILED if the transport
* interface is unable to extend the message to the new size.
*/
ODK_Transport_Status ODK_Transport_ExtendMessage(Message* message, size_t new_size);
/*
* Request that a message be delivered from the REE to the TEE. If the
* delivery is successful ODK_TRANSPORT_STATUS_OK must be returned.
* Otherwise return ODK_TRANSPORT_STATUS_IO_ERROR.
*/
ODK_Transport_Status ODK_Transport_SendMessage(Message* message);
/*
* Request that a message be received by the REE from the TEE. If
* the receipt is successful ODK_TRANSPORT_STATUS_OK must be returned.
* Otherwise return ODK_TRANSPORT_STATUS_IO_ERROR.
*/
ODK_Transport_Status ODK_Transport_ReceiveMessage(Message** message);
/*
* Return a message that is not longer in use to the transport
* interface.
*/
void ODK_Transport_DeallocateMessage(Message* message);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* ODKITEE_TRANSPORT_INTERFACE_H_ */