Joakim Bech | 730a0da | 2017-11-21 15:19:09 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2017, Linaro Limited |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * SPDX-License-Identifier: BSD-2-Clause |
| 6 | */ |
| 7 | #include <hotp_ta.h> |
| 8 | #include <string.h> |
| 9 | #include <tee_internal_api_extensions.h> |
| 10 | #include <tee_internal_api.h> |
| 11 | |
| 12 | /* The size of a SHA1 hash in bytes. */ |
| 13 | #define SHA1_HASH_SIZE 20 |
| 14 | |
| 15 | /* GP says that for HMAC SHA-1, max is 512 bits and min 80 bits. */ |
| 16 | #define MAX_KEY_SIZE 64 /* In bytes */ |
| 17 | #define MIN_KEY_SIZE 10 /* In bytes */ |
| 18 | |
| 19 | /* Dynamic Binary Code 2 Modulo, which is 10^6 according to the spec. */ |
| 20 | #define DBC2_MODULO 1000000 |
| 21 | |
| 22 | /* |
| 23 | * Currently this only supports a single key, in the future this could be |
| 24 | * updated to support multiple users, all with different unique keys (stored |
| 25 | * using secure storage). |
| 26 | */ |
| 27 | static uint8_t K[MAX_KEY_SIZE]; |
| 28 | static uint32_t K_len; |
| 29 | |
| 30 | /* The counter as defined by RFC4226. */ |
| 31 | static uint8_t counter[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; |
| 32 | |
| 33 | /* |
| 34 | * HMAC a block of memory to produce the authentication tag |
| 35 | * @param key The secret key |
| 36 | * @param keylen The length of the secret key (bytes) |
| 37 | * @param in The data to HMAC |
| 38 | * @param inlen The length of the data to HMAC (bytes) |
| 39 | * @param out [out] Destination of the authentication tag |
| 40 | * @param outlen [in/out] Max size and resulting size of authentication tag |
| 41 | */ |
| 42 | static TEE_Result hmac_sha1(const uint8_t *key, const size_t keylen, |
| 43 | const uint8_t *in, const size_t inlen, |
Joakim Bech | f206ea1 | 2017-11-22 16:57:03 +0100 | [diff] [blame] | 44 | uint8_t *out, uint32_t *outlen) |
Joakim Bech | 730a0da | 2017-11-21 15:19:09 +0100 | [diff] [blame] | 45 | { |
| 46 | TEE_Attribute attr = { 0 }; |
| 47 | TEE_ObjectHandle key_handle = TEE_HANDLE_NULL; |
| 48 | TEE_OperationHandle op_handle = TEE_HANDLE_NULL; |
| 49 | TEE_Result res = TEE_SUCCESS; |
| 50 | |
| 51 | if (keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE) |
| 52 | return TEE_ERROR_BAD_PARAMETERS; |
| 53 | |
| 54 | if (!in || !out || !outlen) |
| 55 | return TEE_ERROR_BAD_PARAMETERS; |
| 56 | |
| 57 | /* |
| 58 | * 1. Allocate cryptographic (operation) handle for the HMAC operation. |
| 59 | * Note that the expected size here is in bits (and therefore times |
| 60 | * 8)! |
| 61 | */ |
| 62 | res = TEE_AllocateOperation(&op_handle, TEE_ALG_HMAC_SHA1, TEE_MODE_MAC, |
| 63 | keylen * 8); |
| 64 | if (res != TEE_SUCCESS) { |
| 65 | EMSG("0x%08x", res); |
| 66 | goto exit; |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | * 2. Allocate a container (key handle) for the HMAC attributes. Note |
| 71 | * that the expected size here is in bits (and therefore times 8)! |
| 72 | */ |
| 73 | res = TEE_AllocateTransientObject(TEE_TYPE_HMAC_SHA1, keylen * 8, |
| 74 | &key_handle); |
| 75 | if (res != TEE_SUCCESS) { |
| 76 | EMSG("0x%08x", res); |
| 77 | goto exit; |
| 78 | } |
| 79 | |
| 80 | /* |
| 81 | * 3. Initialize the attributes, i.e., point to the actual HMAC key. |
| 82 | * Here, the expected size is in bytes and not bits as above! |
| 83 | */ |
| 84 | TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key, keylen); |
| 85 | |
| 86 | /* 4. Populate/assign the attributes with the key object */ |
| 87 | res = TEE_PopulateTransientObject(key_handle, &attr, 1); |
| 88 | if (res != TEE_SUCCESS) { |
| 89 | EMSG("0x%08x", res); |
| 90 | goto exit; |
| 91 | } |
| 92 | |
| 93 | /* 5. Associate the key (object) with the operation */ |
| 94 | res = TEE_SetOperationKey(op_handle, key_handle); |
| 95 | if (res != TEE_SUCCESS) { |
| 96 | EMSG("0x%08x", res); |
| 97 | goto exit; |
| 98 | } |
| 99 | |
| 100 | /* 6. Do the HMAC operations */ |
| 101 | TEE_MACInit(op_handle, NULL, 0); |
| 102 | TEE_MACUpdate(op_handle, in, inlen); |
| 103 | res = TEE_MACComputeFinal(op_handle, NULL, 0, out, outlen); |
| 104 | exit: |
| 105 | if (op_handle != TEE_HANDLE_NULL) |
| 106 | TEE_FreeOperation(op_handle); |
| 107 | |
| 108 | /* It is OK to call this when key_handle is TEE_HANDLE_NULL */ |
| 109 | TEE_FreeTransientObject(key_handle); |
| 110 | |
| 111 | return res; |
| 112 | } |
| 113 | |
| 114 | /* |
| 115 | * Truncate function working as described in RFC4226. |
| 116 | */ |
| 117 | static void truncate(uint8_t *hmac_result, uint32_t *bin_code) |
| 118 | { |
| 119 | int offset = hmac_result[19] & 0xf; |
| 120 | |
| 121 | *bin_code = (hmac_result[offset] & 0x7f) << 24 | |
| 122 | (hmac_result[offset+1] & 0xff) << 16 | |
| 123 | (hmac_result[offset+2] & 0xff) << 8 | |
| 124 | (hmac_result[offset+3] & 0xff); |
| 125 | |
| 126 | *bin_code %= DBC2_MODULO; |
| 127 | } |
| 128 | |
| 129 | static TEE_Result register_shared_key(uint32_t param_types, TEE_Param params[4]) |
| 130 | { |
| 131 | TEE_Result res = TEE_SUCCESS; |
| 132 | |
| 133 | uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT, |
| 134 | TEE_PARAM_TYPE_NONE, |
| 135 | TEE_PARAM_TYPE_NONE, |
| 136 | TEE_PARAM_TYPE_NONE); |
| 137 | |
| 138 | if (param_types != exp_param_types) { |
| 139 | EMSG("Expected: 0x%x, got: 0x%x", exp_param_types, param_types); |
| 140 | return TEE_ERROR_BAD_PARAMETERS; |
| 141 | } |
| 142 | |
| 143 | memset(K, 0, sizeof(K)); |
| 144 | memcpy(K, params[0].memref.buffer, params[0].memref.size); |
| 145 | |
| 146 | K_len = params[0].memref.size; |
| 147 | DMSG("Got shared key %s (%u bytes).", K, params[0].memref.size); |
| 148 | |
| 149 | return res; |
| 150 | } |
| 151 | |
| 152 | static TEE_Result get_hotp(uint32_t param_types, TEE_Param params[4]) |
| 153 | { |
| 154 | TEE_Result res = TEE_SUCCESS; |
| 155 | uint32_t hotp_val; |
| 156 | uint8_t mac[SHA1_HASH_SIZE]; |
Joakim Bech | f206ea1 | 2017-11-22 16:57:03 +0100 | [diff] [blame] | 157 | uint32_t mac_len = sizeof(mac); |
Joakim Bech | 730a0da | 2017-11-21 15:19:09 +0100 | [diff] [blame] | 158 | int i; |
| 159 | |
| 160 | uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, |
| 161 | TEE_PARAM_TYPE_NONE, |
| 162 | TEE_PARAM_TYPE_NONE, |
| 163 | TEE_PARAM_TYPE_NONE); |
| 164 | |
| 165 | if (param_types != exp_param_types) { |
| 166 | EMSG("Expected: 0x%x, got: 0x%x", exp_param_types, param_types); |
| 167 | return TEE_ERROR_BAD_PARAMETERS; |
| 168 | } |
| 169 | |
| 170 | res = hmac_sha1(K, K_len, counter, sizeof(counter), mac, &mac_len); |
| 171 | |
| 172 | /* Increment the counter. */ |
| 173 | for (i = sizeof(counter) - 1; i >= 0; i--) { |
| 174 | if (++counter[i]) |
| 175 | break; |
| 176 | } |
| 177 | |
| 178 | truncate(mac, &hotp_val); |
| 179 | DMSG("HOTP is: %d", hotp_val); |
| 180 | params[0].value.a = hotp_val; |
| 181 | |
| 182 | return res; |
| 183 | } |
| 184 | |
| 185 | /******************************************************************************* |
| 186 | * Mandatory TA functions. |
| 187 | ******************************************************************************/ |
| 188 | TEE_Result TA_CreateEntryPoint(void) |
| 189 | { |
| 190 | return TEE_SUCCESS; |
| 191 | } |
| 192 | |
| 193 | void TA_DestroyEntryPoint(void) |
| 194 | { |
| 195 | } |
| 196 | |
| 197 | TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types, |
| 198 | TEE_Param __unused params[4], |
| 199 | void __unused **sess_ctx) |
| 200 | { |
| 201 | uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE, |
| 202 | TEE_PARAM_TYPE_NONE, |
| 203 | TEE_PARAM_TYPE_NONE, |
| 204 | TEE_PARAM_TYPE_NONE); |
| 205 | if (param_types != exp_param_types) |
| 206 | return TEE_ERROR_BAD_PARAMETERS; |
| 207 | |
| 208 | return TEE_SUCCESS; |
| 209 | } |
| 210 | |
| 211 | void TA_CloseSessionEntryPoint(void __unused *sess_ctx) |
| 212 | { |
| 213 | } |
| 214 | |
| 215 | TEE_Result TA_InvokeCommandEntryPoint(void __unused *sess_ctx, |
| 216 | uint32_t cmd_id, |
| 217 | uint32_t param_types, TEE_Param params[4]) |
| 218 | { |
| 219 | switch (cmd_id) { |
| 220 | case TA_HOTP_CMD_REGISTER_SHARED_KEY: |
| 221 | return register_shared_key(param_types, params); |
| 222 | |
| 223 | case TA_HOTP_CMD_GET_HOTP: |
| 224 | return get_hotp(param_types, params); |
| 225 | |
| 226 | default: |
| 227 | return TEE_ERROR_BAD_PARAMETERS; |
| 228 | } |
| 229 | } |