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