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