First Publicly Shared Version of ODKiTEE v15
This commit is contained in:
31
README.md
Normal file
31
README.md
Normal 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
2031
oemcrypto_ta/oemcrypto.c
Normal file
File diff suppressed because it is too large
Load Diff
194
oemcrypto_ta/oemcrypto_api_macros.h
Normal file
194
oemcrypto_ta/oemcrypto_api_macros.h
Normal 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_ */
|
||||
26
oemcrypto_ta/oemcrypto_endianness.c
Normal file
26
oemcrypto_ta/oemcrypto_endianness.c
Normal 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);
|
||||
}
|
||||
17
oemcrypto_ta/oemcrypto_endianness.h
Normal file
17
oemcrypto_ta/oemcrypto_endianness.h
Normal 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_
|
||||
146
oemcrypto_ta/oemcrypto_key.c
Normal file
146
oemcrypto_ta/oemcrypto_key.c
Normal 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;
|
||||
}
|
||||
94
oemcrypto_ta/oemcrypto_key.h
Normal file
94
oemcrypto_ta/oemcrypto_key.h
Normal 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_ */
|
||||
143
oemcrypto_ta/oemcrypto_key_table.c
Normal file
143
oemcrypto_ta/oemcrypto_key_table.c
Normal 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;
|
||||
}
|
||||
79
oemcrypto_ta/oemcrypto_key_table.h
Normal file
79
oemcrypto_ta/oemcrypto_key_table.h
Normal 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_ */
|
||||
125
oemcrypto_ta/oemcrypto_key_types.h
Normal file
125
oemcrypto_ta/oemcrypto_key_types.h
Normal 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_ */
|
||||
81
oemcrypto_ta/oemcrypto_nonce_table.c
Normal file
81
oemcrypto_ta/oemcrypto_nonce_table.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
oemcrypto_ta/oemcrypto_nonce_table.h
Normal file
41
oemcrypto_ta/oemcrypto_nonce_table.h
Normal 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_
|
||||
56
oemcrypto_ta/oemcrypto_overflow.c
Normal file
56
oemcrypto_ta/oemcrypto_overflow.c
Normal 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;
|
||||
}
|
||||
32
oemcrypto_ta/oemcrypto_overflow.h
Normal file
32
oemcrypto_ta/oemcrypto_overflow.h
Normal 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_ */
|
||||
100
oemcrypto_ta/oemcrypto_serialized_usage_table.c
Normal file
100
oemcrypto_ta/oemcrypto_serialized_usage_table.c
Normal 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;
|
||||
}
|
||||
118
oemcrypto_ta/oemcrypto_serialized_usage_table.h
Normal file
118
oemcrypto_ta/oemcrypto_serialized_usage_table.h
Normal 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_
|
||||
946
oemcrypto_ta/oemcrypto_session.c
Normal file
946
oemcrypto_ta/oemcrypto_session.c
Normal 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(¤t_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;
|
||||
}
|
||||
250
oemcrypto_ta/oemcrypto_session.h
Normal file
250
oemcrypto_ta/oemcrypto_session.h
Normal 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_ */
|
||||
59
oemcrypto_ta/oemcrypto_session_key_table.c
Normal file
59
oemcrypto_ta/oemcrypto_session_key_table.c
Normal 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;
|
||||
}
|
||||
}
|
||||
26
oemcrypto_ta/oemcrypto_session_key_table.h
Normal file
26
oemcrypto_ta/oemcrypto_session_key_table.h
Normal 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_ */
|
||||
106
oemcrypto_ta/oemcrypto_session_table.c
Normal file
106
oemcrypto_ta/oemcrypto_session_table.c
Normal 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;
|
||||
}
|
||||
59
oemcrypto_ta/oemcrypto_session_table.h
Normal file
59
oemcrypto_ta/oemcrypto_session_table.h
Normal 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_ */
|
||||
976
oemcrypto_ta/oemcrypto_usage_table.c
Normal file
976
oemcrypto_ta/oemcrypto_usage_table.c
Normal 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);
|
||||
}
|
||||
142
oemcrypto_ta/oemcrypto_usage_table.h
Normal file
142
oemcrypto_ta/oemcrypto_usage_table.h
Normal 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_ */
|
||||
19
oemcrypto_ta/tee_interfaces/assert_interface.h
Normal file
19
oemcrypto_ta/tee_interfaces/assert_interface.h
Normal 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_ */
|
||||
42
oemcrypto_ta/tee_interfaces/clock_interface.h
Normal file
42
oemcrypto_ta/tee_interfaces/clock_interface.h
Normal 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_ */
|
||||
189
oemcrypto_ta/tee_interfaces/crypto_interface.h
Normal file
189
oemcrypto_ta/tee_interfaces/crypto_interface.h
Normal 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_ */
|
||||
64
oemcrypto_ta/tee_interfaces/generation_number_interface.h
Normal file
64
oemcrypto_ta/tee_interfaces/generation_number_interface.h
Normal 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_ */
|
||||
18
oemcrypto_ta/tee_interfaces/initialize_terminate_interface.h
Normal file
18
oemcrypto_ta/tee_interfaces/initialize_terminate_interface.h
Normal 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_ */
|
||||
21
oemcrypto_ta/tee_interfaces/logging_interface.h
Normal file
21
oemcrypto_ta/tee_interfaces/logging_interface.h
Normal 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_ */
|
||||
84
oemcrypto_ta/tee_interfaces/oemcrypto_config_interface.h
Normal file
84
oemcrypto_ta/tee_interfaces/oemcrypto_config_interface.h
Normal 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_ */
|
||||
114
oemcrypto_ta/tee_interfaces/root_of_trust_interface.h
Normal file
114
oemcrypto_ta/tee_interfaces/root_of_trust_interface.h
Normal 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_ */
|
||||
4334
serialization/OEMCryptoCENC.h
Normal file
4334
serialization/OEMCryptoCENC.h
Normal file
File diff suppressed because it is too large
Load Diff
123
serialization/api_support.c
Normal file
123
serialization/api_support.c
Normal 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;
|
||||
}
|
||||
45
serialization/api_support.h
Normal file
45
serialization/api_support.h
Normal 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_ */
|
||||
61
serialization/bump_allocator.c
Normal file
61
serialization/bump_allocator.c
Normal 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));
|
||||
}
|
||||
|
||||
|
||||
29
serialization/bump_allocator.h
Normal file
29
serialization/bump_allocator.h
Normal 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_
|
||||
2116
serialization/generated_src/deserializer.c
Normal file
2116
serialization/generated_src/deserializer.c
Normal file
File diff suppressed because it is too large
Load Diff
383
serialization/generated_src/deserializer.h
Normal file
383
serialization/generated_src/deserializer.h
Normal 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_ */
|
||||
1499
serialization/generated_src/dispatcher.c
Normal file
1499
serialization/generated_src/dispatcher.c
Normal file
File diff suppressed because it is too large
Load Diff
2473
serialization/generated_src/oemcrypto_api.c
Normal file
2473
serialization/generated_src/oemcrypto_api.c
Normal file
File diff suppressed because it is too large
Load Diff
1495
serialization/generated_src/serializer.c
Normal file
1495
serialization/generated_src/serializer.c
Normal file
File diff suppressed because it is too large
Load Diff
369
serialization/generated_src/serializer.h
Normal file
369
serialization/generated_src/serializer.h
Normal 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_ */
|
||||
95
serialization/marshaller_base.c
Normal file
95
serialization/marshaller_base.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
55
serialization/marshaller_base.h
Normal file
55
serialization/marshaller_base.h
Normal 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
101
serialization/message.c
Normal 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
103
serialization/message.h
Normal 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_
|
||||
|
||||
642
serialization/serialization_base.c
Normal file
642
serialization/serialization_base.c
Normal 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;
|
||||
}
|
||||
}
|
||||
118
serialization/serialization_base.h
Normal file
118
serialization/serialization_base.h
Normal 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_
|
||||
260
serialization/shared_memory_allocator.c
Normal file
260
serialization/shared_memory_allocator.c
Normal 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;
|
||||
}
|
||||
31
serialization/shared_memory_allocator.h
Normal file
31
serialization/shared_memory_allocator.h
Normal 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_
|
||||
117
serialization/shared_memory_interface.h
Normal file
117
serialization/shared_memory_interface.h
Normal 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_
|
||||
131
serialization/special_case_apis.c
Normal file
131
serialization/special_case_apis.c
Normal 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);
|
||||
}
|
||||
202
serialization/special_cases.c
Normal file
202
serialization/special_cases.c
Normal 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);
|
||||
}
|
||||
64
serialization/special_cases.h
Normal file
64
serialization/special_cases.h
Normal 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_
|
||||
116
serialization/transport_interface.h
Normal file
116
serialization/transport_interface.h
Normal 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_ */
|
||||
Reference in New Issue
Block a user