blob: 224be60e57d87658a2939f3574407f74e4919bfa [file] [log] [blame]
/*
* Copyright (c) 2017, Linaro Limited
* All rights reserved.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <hotp_ta.h>
#include <string.h>
#include <tee_internal_api_extensions.h>
#include <tee_internal_api.h>
/* The size of a SHA1 hash in bytes. */
#define SHA1_HASH_SIZE 20
/* GP says that for HMAC SHA-1, max is 512 bits and min 80 bits. */
#define MAX_KEY_SIZE 64 /* In bytes */
#define MIN_KEY_SIZE 10 /* In bytes */
/* Dynamic Binary Code 2 Modulo, which is 10^6 according to the spec. */
#define DBC2_MODULO 1000000
/*
* Currently this only supports a single key, in the future this could be
* updated to support multiple users, all with different unique keys (stored
* using secure storage).
*/
static uint8_t K[MAX_KEY_SIZE];
static uint32_t K_len;
/* The counter as defined by RFC4226. */
static uint8_t counter[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
/*
* HMAC a block of memory to produce the authentication tag
* @param key The secret key
* @param keylen The length of the secret key (bytes)
* @param in The data to HMAC
* @param inlen The length of the data to HMAC (bytes)
* @param out [out] Destination of the authentication tag
* @param outlen [in/out] Max size and resulting size of authentication tag
*/
static TEE_Result hmac_sha1(const uint8_t *key, const size_t keylen,
const uint8_t *in, const size_t inlen,
uint8_t *out, uint32_t *outlen)
{
TEE_Attribute attr = { 0 };
TEE_ObjectHandle key_handle = TEE_HANDLE_NULL;
TEE_OperationHandle op_handle = TEE_HANDLE_NULL;
TEE_Result res = TEE_SUCCESS;
if (keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)
return TEE_ERROR_BAD_PARAMETERS;
if (!in || !out || !outlen)
return TEE_ERROR_BAD_PARAMETERS;
/*
* 1. Allocate cryptographic (operation) handle for the HMAC operation.
* Note that the expected size here is in bits (and therefore times
* 8)!
*/
res = TEE_AllocateOperation(&op_handle, TEE_ALG_HMAC_SHA1, TEE_MODE_MAC,
keylen * 8);
if (res != TEE_SUCCESS) {
EMSG("0x%08x", res);
goto exit;
}
/*
* 2. Allocate a container (key handle) for the HMAC attributes. Note
* that the expected size here is in bits (and therefore times 8)!
*/
res = TEE_AllocateTransientObject(TEE_TYPE_HMAC_SHA1, keylen * 8,
&key_handle);
if (res != TEE_SUCCESS) {
EMSG("0x%08x", res);
goto exit;
}
/*
* 3. Initialize the attributes, i.e., point to the actual HMAC key.
* Here, the expected size is in bytes and not bits as above!
*/
TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key, keylen);
/* 4. Populate/assign the attributes with the key object */
res = TEE_PopulateTransientObject(key_handle, &attr, 1);
if (res != TEE_SUCCESS) {
EMSG("0x%08x", res);
goto exit;
}
/* 5. Associate the key (object) with the operation */
res = TEE_SetOperationKey(op_handle, key_handle);
if (res != TEE_SUCCESS) {
EMSG("0x%08x", res);
goto exit;
}
/* 6. Do the HMAC operations */
TEE_MACInit(op_handle, NULL, 0);
TEE_MACUpdate(op_handle, in, inlen);
res = TEE_MACComputeFinal(op_handle, NULL, 0, out, outlen);
exit:
if (op_handle != TEE_HANDLE_NULL)
TEE_FreeOperation(op_handle);
/* It is OK to call this when key_handle is TEE_HANDLE_NULL */
TEE_FreeTransientObject(key_handle);
return res;
}
/*
* Truncate function working as described in RFC4226.
*/
static void truncate(uint8_t *hmac_result, uint32_t *bin_code)
{
int offset = hmac_result[19] & 0xf;
*bin_code = (hmac_result[offset] & 0x7f) << 24 |
(hmac_result[offset+1] & 0xff) << 16 |
(hmac_result[offset+2] & 0xff) << 8 |
(hmac_result[offset+3] & 0xff);
*bin_code %= DBC2_MODULO;
}
static TEE_Result register_shared_key(uint32_t param_types, TEE_Param params[4])
{
TEE_Result res = TEE_SUCCESS;
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
if (param_types != exp_param_types) {
EMSG("Expected: 0x%x, got: 0x%x", exp_param_types, param_types);
return TEE_ERROR_BAD_PARAMETERS;
}
memset(K, 0, sizeof(K));
memcpy(K, params[0].memref.buffer, params[0].memref.size);
K_len = params[0].memref.size;
DMSG("Got shared key %s (%u bytes).", K, params[0].memref.size);
return res;
}
static TEE_Result get_hotp(uint32_t param_types, TEE_Param params[4])
{
TEE_Result res = TEE_SUCCESS;
uint32_t hotp_val;
uint8_t mac[SHA1_HASH_SIZE];
uint32_t mac_len = sizeof(mac);
int i;
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
if (param_types != exp_param_types) {
EMSG("Expected: 0x%x, got: 0x%x", exp_param_types, param_types);
return TEE_ERROR_BAD_PARAMETERS;
}
res = hmac_sha1(K, K_len, counter, sizeof(counter), mac, &mac_len);
/* Increment the counter. */
for (i = sizeof(counter) - 1; i >= 0; i--) {
if (++counter[i])
break;
}
truncate(mac, &hotp_val);
DMSG("HOTP is: %d", hotp_val);
params[0].value.a = hotp_val;
return res;
}
/*******************************************************************************
* Mandatory TA functions.
******************************************************************************/
TEE_Result TA_CreateEntryPoint(void)
{
return TEE_SUCCESS;
}
void TA_DestroyEntryPoint(void)
{
}
TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types,
TEE_Param __unused params[4],
void __unused **sess_ctx)
{
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;
return TEE_SUCCESS;
}
void TA_CloseSessionEntryPoint(void __unused *sess_ctx)
{
}
TEE_Result TA_InvokeCommandEntryPoint(void __unused *sess_ctx,
uint32_t cmd_id,
uint32_t param_types, TEE_Param params[4])
{
switch (cmd_id) {
case TA_HOTP_CMD_REGISTER_SHARED_KEY:
return register_shared_key(param_types, params);
case TA_HOTP_CMD_GET_HOTP:
return get_hotp(param_types, params);
default:
return TEE_ERROR_BAD_PARAMETERS;
}
}