blob: 224be60e57d87658a2939f3574407f74e4919bfa [file] [log] [blame]
Joakim Bech730a0da2017-11-21 15:19:09 +01001/*
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 */
27static uint8_t K[MAX_KEY_SIZE];
28static uint32_t K_len;
29
30/* The counter as defined by RFC4226. */
31static 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 */
42static TEE_Result hmac_sha1(const uint8_t *key, const size_t keylen,
43 const uint8_t *in, const size_t inlen,
Joakim Bechf206ea12017-11-22 16:57:03 +010044 uint8_t *out, uint32_t *outlen)
Joakim Bech730a0da2017-11-21 15:19:09 +010045{
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);
104exit:
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 */
117static 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
129static 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
152static 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 Bechf206ea12017-11-22 16:57:03 +0100157 uint32_t mac_len = sizeof(mac);
Joakim Bech730a0da2017-11-21 15:19:09 +0100158 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 ******************************************************************************/
188TEE_Result TA_CreateEntryPoint(void)
189{
190 return TEE_SUCCESS;
191}
192
193void TA_DestroyEntryPoint(void)
194{
195}
196
197TEE_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
211void TA_CloseSessionEntryPoint(void __unused *sess_ctx)
212{
213}
214
215TEE_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}