1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 #include <assert.h>
26 #include <interface/hwbcc/hwbcc.h>
27 #include <stddef.h>
28 #include <stdint.h>
29 #include <string.h>
30 #include <test-runner-arch.h>
31 #include <trusty/hwbcc.h>
32 #include <trusty/keymaster.h>
33 #include <trusty/rpmb.h>
34 #include <trusty/secretkeeper.h>
35 #include <trusty/trusty_dev.h>
36 #include <trusty/trusty_ipc.h>
37 #include <utils.h>
38 #include <virtio-console.h>
39 #include <virtio-rpmb.h>
40
41 enum test_message_header {
42 TEST_PASSED = 0,
43 TEST_FAILED = 1,
44 TEST_MESSAGE = 2,
45 };
46
starts_with(const char * str1,const char * str2,size_t str2_len)47 bool starts_with(const char* str1, const char* str2, size_t str2_len) {
48 for (size_t i = 0; i < str2_len; i++) {
49 if (str1[i] == '\0') {
50 return true;
51 }
52 if (str1[i] != str2[i]) {
53 return false;
54 }
55 }
56 return false;
57 }
58
59 /*
60 * Any return from this function indicates an internal error. The caller is
61 * responsible for reporting the error. It currently returns to the host with
62 * 2 as the exit code.
63 * No attempt is made to clean up before returning.
64 */
boot(int cpu)65 void boot(int cpu) {
66 int ret;
67 int chan;
68 int status;
69 char cmdline[256];
70 size_t cmdline_len;
71 const char* port;
72 const char boottest_cmd[] = "boottest ";
73 char test_result[256];
74 struct trusty_ipc_iovec iovec = {
75 .base = test_result,
76 .len = sizeof(test_result),
77 };
78 static struct trusty_dev trusty_dev;
79 struct trusty_ipc_dev* ipc_dev;
80 struct trusty_ipc_chan test_chan;
81 struct virtio_console* console;
82
83 if (cpu) {
84 ret = arch_gic_init(cpu);
85 if (ret != 0) {
86 return;
87 }
88
89 while (true) {
90 ret = trusty_dev_nop(&trusty_dev);
91 if (ret >= 0) {
92 trusty_idle(&trusty_dev, ret);
93 } else {
94 abort_msg("Secondary cpu unexpected error code\n");
95 }
96 }
97 }
98
99 /*
100 * Initialize VirtIO console device, it contains rpmb0 port for VirtIO
101 * RPMB and testrunner0 port to log message and pass test result to host
102 */
103 console = init_virtio_console();
104 if (NULL == console) {
105 return;
106 }
107
108 ret = init_log(console);
109 if (ret != 0) {
110 return;
111 }
112
113 /*
114 * Read test arguments from host (port name of test server to connect to)
115 */
116 cmdline_len = host_get_cmdline(cmdline, sizeof(cmdline));
117 if (!starts_with(boottest_cmd, cmdline, cmdline_len)) {
118 /* No test was requested, boot next operating system */
119 boot_next();
120 return;
121 }
122
123 port = cmdline + sizeof(boottest_cmd) - 1;
124
125 /* Init Trusty device */
126 ret = trusty_dev_init(&trusty_dev, NULL);
127 if (ret != 0) {
128 return;
129 }
130
131 ret = arch_gic_init(cpu);
132 if (ret != 0) {
133 return;
134 }
135
136 /* Create Trusty IPC device */
137 ret = trusty_ipc_dev_create(&ipc_dev, &trusty_dev, PAGE_SIZE);
138 if (ret != 0) {
139 return;
140 }
141
142 /* If we don't have a VirtIO RPMB device, skip storage proxy */
143 if (!init_virtio_rpmb(console)) {
144 if (rpmb_storage_proxy_init(ipc_dev, NULL)) {
145 log_msg("Failed to initialize storage proxy\n");
146 return;
147 }
148 } else {
149 log_msg("Could not find serial port rpmb0, skipping storage proxy.\n");
150 }
151
152 /*
153 * Check that keymaster can at least be connected to.
154 * TODO: Use in full boot path with typical calls.
155 */
156 ret = km_tipc_init(ipc_dev);
157 if (ret != 0) {
158 log_msg("km_tipc_init failed\n");
159 return;
160 }
161 ret = trusty_set_boot_params(0, 0, KM_VERIFIED_BOOT_UNVERIFIED, false, NULL,
162 0, NULL, 0);
163 if (ret != 0) {
164 log_msg("trusty_set_boot_params failed\n");
165 return;
166 }
167 km_tipc_shutdown();
168
169 /**
170 * Check that HWBCC can be connected to.
171 */
172 ret = hwbcc_tipc_init(ipc_dev);
173 if (ret != 0) {
174 log_msg("hwbcc_tipc_init failed.\n");
175 return;
176 }
177 uint8_t dice_artifacts[HWBCC_MAX_RESP_PAYLOAD_SIZE];
178 size_t resp_payload_size = 0;
179 ret = hwbcc_get_dice_artifacts(0, dice_artifacts, sizeof(dice_artifacts),
180 &resp_payload_size);
181 if (ret != 0) {
182 log_msg("hwbcc_get_dice_artifacts failed.\n");
183 }
184
185 /**
186 * dice_artifacts expects the following CBOR encoded structure.
187 * Since the implementation of hwbcc_get_dice_artifacts serves only the
188 * non-secure world, Bcc is not present in the returned dice_artifacts.
189 * We calculate the expected size, including CBOR header sizes.
190 * BccHandover = {
191 * 1 : bstr .size 32, // CDI_Attest
192 * 2 : bstr .size 32, // CDI_Seal
193 * ? 3 : Bcc, // Cert_Chain
194 * }
195 * Bcc = [
196 * PubKeyEd25519, // UDS (Unique Device Secret)
197 * + BccEntry, // Root -> leaf
198 * ]
199 */
200 size_t DICE_CDI_SIZE = 32;
201 size_t bcc_handover_size = 2 * DICE_CDI_SIZE + 7 /*CBOR tags*/;
202
203 if (resp_payload_size != bcc_handover_size) {
204 log_msg("hwbcc_get_dice_artifacts failed with incorrect response size.\n");
205 }
206
207 /**
208 * Note: In ABL, `hwbcc_ns_deprivilege` needs to be called
209 * after retrieving dice artifacts.
210 */
211 ret = hwbcc_ns_deprivilege();
212
213 if (ret != 0) {
214 log_msg("hwbcc_ns_deprivilege failed.\n");
215 }
216
217 /* Close the firt connection and try to connect again, which should fail due
218 * to the deprivilege call. */
219 hwbcc_tipc_shutdown();
220
221 ret = hwbcc_tipc_init(ipc_dev);
222 if (ret != 0) {
223 log_msg("hwbcc_tipc_init failed.\n");
224 return;
225 }
226 memset(dice_artifacts, 0, sizeof(dice_artifacts));
227 resp_payload_size = 0;
228 ret = hwbcc_get_dice_artifacts(0, dice_artifacts, sizeof(dice_artifacts),
229 &resp_payload_size);
230
231 if (ret == 0) {
232 log_msg("hwbcc_ns_deprivilege is broken.\n");
233 }
234
235 hwbcc_tipc_shutdown();
236
237 /**
238 * Check that Secretkeeper can be connected to.
239 */
240 ret = secretkeeper_tipc_init(ipc_dev);
241 if (ret != 0) {
242 log_msg("secretkeeper_tipc_init failed.\n");
243 return;
244 }
245
246 /**
247 * Check that we can retrieve the public key and that it has the expected
248 * form.
249 */
250 uint8_t sk_identity[100];
251 size_t sk_identity_size = 0;
252 ret = secretkeeper_get_identity(sizeof(sk_identity), sk_identity,
253 &sk_identity_size);
254 if (ret != 0) {
255 log_msg("secretkeeper_get_identity failed.\n");
256 return;
257 }
258
259 const size_t ed25519_key_size = 32;
260 const uint8_t expected_cbor_prefix[] = {
261 // Map with 4 entries (see PubKeyEd25519)
262 0xa4,
263 // Key type: 1: 1
264 0x01, 0x01,
265 // Algorithm: 3: -8
266 0x03, 0x27,
267 // Curve: -1: 6
268 0x20, 0x06,
269 // Public key: -2, 32 byte bstr (which follows)
270 0x21, 0x58, 0x20};
271 const size_t expected_identity_size =
272 sizeof(expected_cbor_prefix) + ed25519_key_size;
273
274 if (sk_identity_size != expected_identity_size) {
275 log_msg("Unexpected sk_identity_size\n");
276 return;
277 }
278
279 if (memcmp(sk_identity, expected_cbor_prefix,
280 sizeof(expected_cbor_prefix))) {
281 log_msg("sk_identity does not start with expected prefix\n");
282 return;
283 }
284
285 secretkeeper_tipc_shutdown();
286
287 ret = arch_start_secondary_cpus();
288 if (ret) {
289 log_msg("Failed to start secondary CPUs\n");
290 return;
291 }
292
293 /* Create connection to test server */
294 trusty_ipc_chan_init(&test_chan, ipc_dev);
295 chan = trusty_ipc_connect(&test_chan, port, true);
296 if (chan < 0) {
297 log_msg("Failed to connect to test server\n");
298 return;
299 }
300
301 /* Wait for tests to complete and read status */
302 while (true) {
303 ret = trusty_ipc_recv(&test_chan, &iovec, 1, /* wait = */ true);
304 if (ret <= 0 || ret >= (int)sizeof(test_result)) {
305 return;
306 }
307
308 if (test_result[0] == TEST_PASSED) {
309 break;
310 } else if (test_result[0] == TEST_FAILED) {
311 break;
312 } else if (test_result[0] == TEST_MESSAGE) {
313 log_buf(test_result + 1, ret - 1);
314 } else {
315 return;
316 }
317 }
318 status = test_result[0] != TEST_PASSED;
319
320 /* Request another read to wait for the sever to close the connection */
321 ret = trusty_ipc_recv(&test_chan, &iovec, 1, /* wait = */ true);
322 if (ret != TRUSTY_ERR_CHANNEL_CLOSED) {
323 return;
324 }
325
326 /* Return test status to host, 0: test success, 1: test failed */
327 host_exit(status);
328 }
329