1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdbool.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <trusty_ipc.h>
23 #include <uapi/err.h>
24
25 #include <lib/hwkey/hwkey.h>
26 #include <lib/tipc/tipc.h>
27 #include "interface/hwkey/hwkey.h"
28
29 #define LOG_TAG "libhwkey"
30 #define TLOGE(fmt, ...) \
31 fprintf(stderr, "%s: %d: " fmt, LOG_TAG, __LINE__, ##__VA_ARGS__)
32
33 /**
34 * long hwkey_err_to_tipc_err() - translates hwkey err value to tipc/lk err
35 * value
36 * @hwkey_err: hwkey err value
37 *
38 * Returns: enum hwkey_err value
39 */
hwkey_err_to_tipc_err(enum hwkey_err hwkey_err)40 static long hwkey_err_to_tipc_err(enum hwkey_err hwkey_err) {
41 switch (hwkey_err) {
42 case HWKEY_NO_ERROR:
43 return NO_ERROR;
44 case HWKEY_ERR_NOT_VALID:
45 return ERR_NOT_VALID;
46 case HWKEY_ERR_BAD_LEN:
47 return ERR_BAD_LEN;
48 case HWKEY_ERR_NOT_IMPLEMENTED:
49 return ERR_NOT_IMPLEMENTED;
50 case HWKEY_ERR_NOT_FOUND:
51 return ERR_NOT_FOUND;
52 case HWKEY_ERR_ALREADY_EXISTS:
53 return ERR_ALREADY_EXISTS;
54 default:
55 return ERR_GENERIC;
56 }
57 }
58
59 /**
60 * long send_req() - sends request to hwkey server
61 * @session: the hwkey session handle
62 * @msg: the request header to send to the hwkey server
63 * @req_buf: the request payload to send to the hwkey server
64 * @req_buf_len: the length of the request payload @req_buf
65 * @rsp_buf: buffer in which to store the response payload
66 * @rsp_buf_len: the size of the response buffer. Inout param, set
67 * to the actual response payload length.
68 *
69 * Returns: NO_ERROR on success, negative error code on failure
70 */
send_req(hwkey_session_t session,struct hwkey_msg * msg,uint8_t * req_buf,uint32_t req_buf_len,uint8_t * rsp_buf,uint32_t * rsp_buf_len)71 static long send_req(hwkey_session_t session,
72 struct hwkey_msg* msg,
73 uint8_t* req_buf,
74 uint32_t req_buf_len,
75 uint8_t* rsp_buf,
76 uint32_t* rsp_buf_len) {
77 long rc;
78
79 struct iovec tx_iov[2] = {
80 {.iov_base = msg, .iov_len = sizeof(*msg)},
81 {.iov_base = req_buf, .iov_len = req_buf_len},
82 };
83 ipc_msg_t tx_msg = {
84 .iov = tx_iov,
85 .num_iov = 2,
86 };
87
88 rc = send_msg(session, &tx_msg);
89 if (rc < 0) {
90 goto err_send_fail;
91 }
92
93 if (((size_t)rc) != sizeof(*msg) + req_buf_len) {
94 rc = ERR_IO;
95 goto err_send_fail;
96 }
97
98 uevent_t uevt;
99 rc = wait(session, &uevt, INFINITE_TIME);
100 if (rc != NO_ERROR) {
101 goto err_send_fail;
102 }
103
104 ipc_msg_info_t inf;
105 rc = get_msg(session, &inf);
106 if (rc != NO_ERROR) {
107 TLOGE("%s: failed to get_msg (%ld)\n", __func__, rc);
108 goto err_send_fail;
109 }
110
111 if (inf.len > sizeof(*msg) + (size_t)*rsp_buf_len) {
112 TLOGE("%s: insufficient output buffer size (%zu > %zu)\n", __func__,
113 inf.len - sizeof(*msg), (size_t)*rsp_buf_len);
114 rc = ERR_TOO_BIG;
115 goto err_get_fail;
116 }
117
118 if (inf.len < sizeof(*msg)) {
119 TLOGE("%s: short buffer (%zu)\n", __func__, inf.len);
120 rc = ERR_NOT_VALID;
121 goto err_get_fail;
122 }
123
124 uint32_t cmd_sent = msg->header.cmd;
125
126 struct iovec rx_iov[2] = {
127 {.iov_base = msg, .iov_len = sizeof(*msg)},
128 {.iov_base = rsp_buf, .iov_len = *rsp_buf_len},
129 };
130 ipc_msg_t rx_msg = {
131 .iov = rx_iov,
132 .num_iov = 2,
133 };
134
135 rc = read_msg(session, inf.id, 0, &rx_msg);
136 put_msg(session, inf.id);
137 if (rc < 0) {
138 goto err_read_fail;
139 }
140
141 size_t read_len = (size_t)rc;
142 if (read_len != inf.len) {
143 // data read in does not match message length
144 TLOGE("%s: invalid read length (%zu != %zu)\n", __func__, read_len,
145 inf.len);
146 rc = ERR_IO;
147 goto err_read_fail;
148 }
149
150 if (msg->header.cmd != (cmd_sent | HWKEY_RESP_BIT)) {
151 TLOGE("%s: invalid response id (0x%x) for cmd (0x%x)\n", __func__,
152 msg->header.cmd, cmd_sent);
153 return ERR_NOT_VALID;
154 }
155
156 *rsp_buf_len = read_len - sizeof(*msg);
157 return hwkey_err_to_tipc_err(msg->header.status);
158
159 err_get_fail:
160 put_msg(session, inf.id);
161 err_send_fail:
162 err_read_fail:
163 TLOGE("%s: failed read_msg (%ld)\n", __func__, rc);
164 return rc;
165 }
166
hwkey_open(void)167 long hwkey_open(void) {
168 return connect(HWKEY_PORT, IPC_CONNECT_WAIT_FOR_PORT);
169 }
170
hwkey_get_keyslot_data(hwkey_session_t session,const char * slot_id,uint8_t * data,uint32_t * data_size)171 long hwkey_get_keyslot_data(hwkey_session_t session,
172 const char* slot_id,
173 uint8_t* data,
174 uint32_t* data_size) {
175 if (slot_id == NULL || data == NULL || data_size == NULL ||
176 *data_size == 0) {
177 return ERR_NOT_VALID;
178 }
179
180 struct hwkey_msg msg = {
181 .header.cmd = HWKEY_GET_KEYSLOT,
182 };
183
184 // TODO: remove const cast when const APIs are available
185 return send_req(session, &msg, (uint8_t*)slot_id, strlen(slot_id), data,
186 data_size);
187 }
188
hwkey_derive(hwkey_session_t session,uint32_t * kdf_version,const uint8_t * src,uint8_t * dest,uint32_t buf_size)189 long hwkey_derive(hwkey_session_t session,
190 uint32_t* kdf_version,
191 const uint8_t* src,
192 uint8_t* dest,
193 uint32_t buf_size) {
194 if (src == NULL || buf_size == 0 || dest == NULL || kdf_version == NULL) {
195 // invalid input
196 return ERR_NOT_VALID;
197 }
198
199 struct hwkey_msg msg = {
200 .header.cmd = HWKEY_DERIVE,
201 .arg1 = *kdf_version,
202 };
203
204 // TODO: remove const cast when const APIs are available
205 uint32_t stored_buf_size = buf_size;
206 long rc = send_req(session, &msg, (uint8_t*)src, buf_size, dest, &buf_size);
207
208 if (rc == NO_ERROR && stored_buf_size != buf_size) {
209 return ERR_BAD_LEN;
210 }
211
212 *kdf_version = msg.arg1;
213
214 return rc;
215 }
216
hwkey_derive_versioned(hwkey_session_t session,struct hwkey_versioned_key_options * args)217 long hwkey_derive_versioned(hwkey_session_t session,
218 struct hwkey_versioned_key_options* args) {
219 if (args == NULL) {
220 TLOGE("Args pointer is null\n");
221 return ERR_NOT_VALID;
222 }
223 if (args->context == NULL && args->context_len != 0) {
224 TLOGE("Context pointer is null with non-zero length\n");
225 return ERR_NOT_VALID;
226 }
227 if (args->context != NULL && args->context_len == 0) {
228 TLOGE("Context pointer is non-null with zero length\n");
229 return ERR_NOT_VALID;
230 }
231 if (args->key == NULL && args->key_len != 0) {
232 TLOGE("Key pointer is null with non-zero length\n");
233 return ERR_NOT_VALID;
234 }
235 if (args->key != NULL && args->key_len == 0) {
236 TLOGE("Key pointer is non-null with zero length\n");
237 return ERR_NOT_VALID;
238 }
239 if (args->os_rollback_version < 0 &&
240 args->os_rollback_version != HWKEY_ROLLBACK_VERSION_CURRENT) {
241 TLOGE("OS rollback version is invalid: %d\n",
242 args->os_rollback_version);
243 return ERR_NOT_VALID;
244 }
245
246 size_t max_payload_len =
247 HWKEY_MAX_MSG_SIZE - sizeof(struct hwkey_derive_versioned_msg);
248 if (args->context_len > max_payload_len ||
249 args->key_len > max_payload_len) {
250 return ERR_BAD_LEN;
251 }
252
253 uint32_t key_options = args->shared_key ? HWKEY_SHARED_KEY_TYPE
254 : HWKEY_DEVICE_UNIQUE_KEY_TYPE;
255
256 struct hwkey_derive_versioned_msg req_msg = {
257 .header.cmd = HWKEY_DERIVE_VERSIONED,
258 .kdf_version = args->kdf_version,
259 .rollback_version_source = args->rollback_version_source,
260 .rollback_versions[HWKEY_ROLLBACK_VERSION_OS_INDEX] =
261 args->os_rollback_version,
262 .key_options = key_options,
263 .key_len = args->key_len,
264 };
265
266 int rc = tipc_send2(session, &req_msg, sizeof(req_msg), args->context,
267 args->context_len);
268 if (rc < 0) {
269 return rc;
270 }
271
272 if (((size_t)rc) != sizeof(req_msg) + args->context_len) {
273 TLOGE("%s: failed to send entire message\n", __func__);
274 return ERR_IO;
275 }
276
277 uevent_t uevt;
278 rc = wait(session, &uevt, INFINITE_TIME);
279 if (rc != NO_ERROR) {
280 return rc;
281 }
282
283 struct hwkey_derive_versioned_msg resp_msg = {0};
284
285 rc = tipc_recv2(session, sizeof(struct hwkey_msg_header), &resp_msg,
286 sizeof(resp_msg), args->key, args->key_len);
287 if (rc < 0) {
288 return rc;
289 }
290
291 if (resp_msg.header.cmd != (req_msg.header.cmd | HWKEY_RESP_BIT)) {
292 TLOGE("%s: invalid response id (0x%x) for cmd (0x%x)\n", __func__,
293 resp_msg.header.cmd, req_msg.header.cmd);
294 return ERR_NOT_VALID;
295 }
296
297 if (resp_msg.header.status == HWKEY_NO_ERROR &&
298 (size_t)rc != sizeof(resp_msg) + args->key_len) {
299 TLOGE("%s: unexpected response length (%zu != %zu)\n", __func__,
300 (size_t)rc, sizeof(resp_msg) + args->key_len);
301 return ERR_BAD_LEN;
302 }
303
304 if (resp_msg.header.status == HWKEY_NO_ERROR) {
305 args->kdf_version = resp_msg.kdf_version;
306 args->os_rollback_version =
307 resp_msg.rollback_versions[HWKEY_ROLLBACK_VERSION_OS_INDEX];
308 }
309
310 return hwkey_err_to_tipc_err(resp_msg.header.status);
311 }
312
hwkey_close(hwkey_session_t session)313 void hwkey_close(hwkey_session_t session) {
314 close(session);
315 }
316