1# SPDX-License-Identifier:      GPL-2.0+
2# Copyright (c) 2019, Linaro Limited
3# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
4#
5# U-Boot UEFI: Signed Image Authentication Test
6
7"""
8This test verifies image authentication for signed images.
9"""
10
11import pytest
12
13
14@pytest.mark.boardspec('sandbox')
15@pytest.mark.buildconfigspec('efi_secure_boot')
16@pytest.mark.buildconfigspec('cmd_efidebug')
17@pytest.mark.buildconfigspec('cmd_fat')
18@pytest.mark.buildconfigspec('cmd_nvedit_efi')
19@pytest.mark.slow
20class TestEfiSignedImage(object):
21    def test_efi_signed_image_auth1(self, u_boot_console, efi_boot_env):
22        """
23        Test Case 1 - Secure boot is not in force
24        """
25        u_boot_console.restart_uboot()
26        disk_img = efi_boot_env
27        with u_boot_console.log.section('Test Case 1a'):
28            # Test Case 1a, run signed image if no PK
29            output = u_boot_console.run_command_list([
30                'host bind 0 %s' % disk_img,
31                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""',
32                'efidebug boot order 1',
33                'bootefi bootmgr'])
34            assert 'Hello, world!' in ''.join(output)
35
36        with u_boot_console.log.section('Test Case 1b'):
37            # Test Case 1b, run unsigned image if no PK
38            output = u_boot_console.run_command_list([
39                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""',
40                'efidebug boot order 2',
41                'bootefi bootmgr'])
42            assert 'Hello, world!' in ''.join(output)
43
44    def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env):
45        """
46        Test Case 2 - Secure boot is in force,
47                      authenticated by db (TEST_db certificate in db)
48        """
49        u_boot_console.restart_uboot()
50        disk_img = efi_boot_env
51        with u_boot_console.log.section('Test Case 2a'):
52            # Test Case 2a, db is not yet installed
53            output = u_boot_console.run_command_list([
54                'host bind 0 %s' % disk_img,
55                'fatload host 0:1 4000000 KEK.auth',
56                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
57                'fatload host 0:1 4000000 PK.auth',
58                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
59            assert 'Failed to set EFI variable' not in ''.join(output)
60            output = u_boot_console.run_command_list([
61                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""',
62                'efidebug boot order 1',
63                'efidebug test bootmgr'])
64            assert('\'HELLO1\' failed' in ''.join(output))
65            assert('efi_bootmgr_load() returned: 26' in ''.join(output))
66            output = u_boot_console.run_command_list([
67                'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""',
68                'efidebug boot order 2',
69                'efidebug test bootmgr'])
70            assert '\'HELLO2\' failed' in ''.join(output)
71            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
72
73        with u_boot_console.log.section('Test Case 2b'):
74            # Test Case 2b, authenticated by db
75            output = u_boot_console.run_command_list([
76                'fatload host 0:1 4000000 db.auth',
77                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
78            assert 'Failed to set EFI variable' not in ''.join(output)
79            output = u_boot_console.run_command_list([
80                'efidebug boot order 2',
81                'efidebug test bootmgr'])
82            assert '\'HELLO2\' failed' in ''.join(output)
83            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
84            output = u_boot_console.run_command_list([
85                'efidebug boot order 1',
86                'bootefi bootmgr'])
87            assert 'Hello, world!' in ''.join(output)
88
89    def test_efi_signed_image_auth3(self, u_boot_console, efi_boot_env):
90        """
91        Test Case 3 - rejected by dbx (TEST_db certificate in dbx)
92        """
93        u_boot_console.restart_uboot()
94        disk_img = efi_boot_env
95        with u_boot_console.log.section('Test Case 3a'):
96            # Test Case 3a, rejected by dbx
97            output = u_boot_console.run_command_list([
98                'host bind 0 %s' % disk_img,
99                'fatload host 0:1 4000000 db.auth',
100                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
101                'fatload host 0:1 4000000 KEK.auth',
102                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
103                'fatload host 0:1 4000000 PK.auth',
104                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
105            assert 'Failed to set EFI variable' not in ''.join(output)
106            output = u_boot_console.run_command_list([
107                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
108                'efidebug boot order 1',
109                'efidebug test bootmgr'])
110            assert '\'HELLO\' failed' in ''.join(output)
111            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
112
113        with u_boot_console.log.section('Test Case 3b'):
114            # Test Case 3b, rejected by dbx even if db allows
115            output = u_boot_console.run_command_list([
116                'fatload host 0:1 4000000 db.auth',
117                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
118            assert 'Failed to set EFI variable' not in ''.join(output)
119            output = u_boot_console.run_command_list([
120                'efidebug boot order 1',
121                'efidebug test bootmgr'])
122            assert '\'HELLO\' failed' in ''.join(output)
123            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
124
125    def test_efi_signed_image_auth4(self, u_boot_console, efi_boot_env):
126        """
127        Test Case 4 - revoked by dbx (digest of TEST_db certificate in dbx)
128        """
129        u_boot_console.restart_uboot()
130        disk_img = efi_boot_env
131        with u_boot_console.log.section('Test Case 4'):
132            # Test Case 4, rejected by dbx
133            output = u_boot_console.run_command_list([
134                'host bind 0 %s' % disk_img,
135                'fatload host 0:1 4000000 dbx_hash.auth',
136                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
137                'fatload host 0:1 4000000 db.auth',
138                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
139                'fatload host 0:1 4000000 KEK.auth',
140                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
141                'fatload host 0:1 4000000 PK.auth',
142                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
143            assert 'Failed to set EFI variable' not in ''.join(output)
144            output = u_boot_console.run_command_list([
145                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
146                'efidebug boot order 1',
147                'efidebug test bootmgr'])
148            assert '\'HELLO\' failed' in ''.join(output)
149            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
150
151    def test_efi_signed_image_auth5(self, u_boot_console, efi_boot_env):
152        """
153        Test Case 5 - multiple signatures
154                        one signed with TEST_db, and
155                        one signed with TEST_db1
156        """
157        u_boot_console.restart_uboot()
158        disk_img = efi_boot_env
159        with u_boot_console.log.section('Test Case 5a'):
160            # Test Case 5a, authenticated even if only one of signatures
161            # is verified
162            output = u_boot_console.run_command_list([
163                'host bind 0 %s' % disk_img,
164                'fatload host 0:1 4000000 db.auth',
165                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
166                'fatload host 0:1 4000000 KEK.auth',
167                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
168                'fatload host 0:1 4000000 PK.auth',
169                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
170            assert 'Failed to set EFI variable' not in ''.join(output)
171            output = u_boot_console.run_command_list([
172                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
173                'efidebug boot order 1',
174                'efidebug test bootmgr'])
175            assert 'Hello, world!' in ''.join(output)
176
177        with u_boot_console.log.section('Test Case 5b'):
178            # Test Case 5b, authenticated if both signatures are verified
179            output = u_boot_console.run_command_list([
180                'fatload host 0:1 4000000 db2.auth',
181                'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db'])
182            assert 'Failed to set EFI variable' not in ''.join(output)
183            output = u_boot_console.run_command_list([
184                'efidebug boot order 1',
185                'efidebug test bootmgr'])
186            assert 'Hello, world!' in ''.join(output)
187
188        with u_boot_console.log.section('Test Case 5c'):
189            # Test Case 5c, rejected if one of signatures (digest of
190            # certificate) is revoked
191            output = u_boot_console.run_command_list([
192                'fatload host 0:1 4000000 dbx_hash.auth',
193                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
194            assert 'Failed to set EFI variable' not in ''.join(output)
195            output = u_boot_console.run_command_list([
196                'efidebug boot order 1',
197                'efidebug test bootmgr'])
198            assert '\'HELLO\' failed' in ''.join(output)
199            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
200
201        with u_boot_console.log.section('Test Case 5d'):
202            # Test Case 5d, rejected if both of signatures are revoked
203            output = u_boot_console.run_command_list([
204                'fatload host 0:1 4000000 dbx_hash2.auth',
205                'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize dbx'])
206            assert 'Failed to set EFI variable' not in ''.join(output)
207            output = u_boot_console.run_command_list([
208                'efidebug boot order 1',
209                'efidebug test bootmgr'])
210            assert '\'HELLO\' failed' in ''.join(output)
211            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
212
213        # Try rejection in reverse order.
214        u_boot_console.restart_uboot()
215        with u_boot_console.log.section('Test Case 5e'):
216            # Test Case 5e, authenticated even if only one of signatures
217            # is verified. Same as before but reject dbx_hash1.auth only
218            output = u_boot_console.run_command_list([
219                'host bind 0 %s' % disk_img,
220                'fatload host 0:1 4000000 db.auth',
221                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
222                'fatload host 0:1 4000000 KEK.auth',
223                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
224                'fatload host 0:1 4000000 PK.auth',
225                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
226                'fatload host 0:1 4000000 db2.auth',
227                'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
228                'fatload host 0:1 4000000 dbx_hash1.auth',
229                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
230            assert 'Failed to set EFI variable' not in ''.join(output)
231            output = u_boot_console.run_command_list([
232                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
233                'efidebug boot order 1',
234                'efidebug test bootmgr'])
235            assert '\'HELLO\' failed' in ''.join(output)
236            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
237
238    def test_efi_signed_image_auth6(self, u_boot_console, efi_boot_env):
239        """
240        Test Case 6 - using digest of signed image in database
241        """
242        u_boot_console.restart_uboot()
243        disk_img = efi_boot_env
244        with u_boot_console.log.section('Test Case 6a'):
245            # Test Case 6a, verified by image's digest in db
246            output = u_boot_console.run_command_list([
247                'host bind 0 %s' % disk_img,
248                'fatload host 0:1 4000000 db_hello_signed.auth',
249                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
250                'fatload host 0:1 4000000 KEK.auth',
251                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
252                'fatload host 0:1 4000000 PK.auth',
253                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
254            assert 'Failed to set EFI variable' not in ''.join(output)
255            output = u_boot_console.run_command_list([
256                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
257                'efidebug boot order 1',
258                'bootefi bootmgr'])
259            assert 'Hello, world!' in ''.join(output)
260
261        with u_boot_console.log.section('Test Case 6b'):
262            # Test Case 6b, rejected by TEST_db certificate in dbx
263            output = u_boot_console.run_command_list([
264                'fatload host 0:1 4000000 dbx_db.auth',
265                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
266            assert 'Failed to set EFI variable' not in ''.join(output)
267            output = u_boot_console.run_command_list([
268                'efidebug boot order 1',
269                'efidebug test bootmgr'])
270            assert '\'HELLO\' failed' in ''.join(output)
271            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
272
273        with u_boot_console.log.section('Test Case 6c'):
274            # Test Case 6c, rejected by image's digest in dbx
275            output = u_boot_console.run_command_list([
276                'fatload host 0:1 4000000 db.auth',
277                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
278                'fatload host 0:1 4000000 dbx_hello_signed.auth',
279                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
280            assert 'Failed to set EFI variable' not in ''.join(output)
281            output = u_boot_console.run_command_list([
282                'efidebug boot order 1',
283                'efidebug test bootmgr'])
284            assert '\'HELLO\' failed' in ''.join(output)
285            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
286
287    def test_efi_signed_image_auth7(self, u_boot_console, efi_boot_env):
288        """
289        Test Case 7 - Reject images based on the sha384/512 of their x509 cert
290        """
291        # sha384 of an x509 cert in dbx
292        u_boot_console.restart_uboot()
293        disk_img = efi_boot_env
294        with u_boot_console.log.section('Test Case 7a'):
295            output = u_boot_console.run_command_list([
296                'host bind 0 %s' % disk_img,
297                'fatload host 0:1 4000000 db.auth',
298                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
299                'fatload host 0:1 4000000 KEK.auth',
300                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
301                'fatload host 0:1 4000000 PK.auth',
302                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
303                'fatload host 0:1 4000000 db2.auth',
304                'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
305                'fatload host 0:1 4000000 dbx_hash384.auth',
306                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
307            assert 'Failed to set EFI variable' not in ''.join(output)
308            output = u_boot_console.run_command_list([
309                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
310                'efidebug boot order 1',
311                'efidebug test bootmgr'])
312            assert '\'HELLO\' failed' in ''.join(output)
313            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
314
315        # sha512 of an x509 cert in dbx
316        u_boot_console.restart_uboot()
317        with u_boot_console.log.section('Test Case 7b'):
318            output = u_boot_console.run_command_list([
319                'host bind 0 %s' % disk_img,
320                'fatload host 0:1 4000000 db.auth',
321                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
322                'fatload host 0:1 4000000 KEK.auth',
323                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
324                'fatload host 0:1 4000000 PK.auth',
325                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
326                'fatload host 0:1 4000000 db2.auth',
327                'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
328                'fatload host 0:1 4000000 dbx_hash512.auth',
329                'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
330            assert 'Failed to set EFI variable' not in ''.join(output)
331            output = u_boot_console.run_command_list([
332                'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
333                'efidebug boot order 1',
334                'efidebug test bootmgr'])
335            assert '\'HELLO\' failed' in ''.join(output)
336            assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
337
338    def test_efi_signed_image_auth8(self, u_boot_console, efi_boot_env):
339        """
340        Test Case 8 - Secure boot is in force,
341                      Same as Test Case 2 but the image binary to be loaded
342                      was willfully modified (forged)
343                      Must be rejected.
344        """
345        u_boot_console.restart_uboot()
346        disk_img = efi_boot_env
347        with u_boot_console.log.section('Test Case 8a'):
348            # Test Case 8a, Secure boot is not yet forced
349            output = u_boot_console.run_command_list([
350                'host bind 0 %s' % disk_img,
351                'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld_forged.efi.signed -s ""',
352                'efidebug boot order 1',
353                'efidebug test bootmgr'])
354            assert('hELLO, world!' in ''.join(output))
355
356        with u_boot_console.log.section('Test Case 8b'):
357            # Test Case 8b, Install signature database and verify the image
358            output = u_boot_console.run_command_list([
359                'fatload host 0:1 4000000 db.auth',
360                'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
361                'fatload host 0:1 4000000 KEK.auth',
362                'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
363                'fatload host 0:1 4000000 PK.auth',
364                'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
365            assert 'Failed to set EFI variable' not in ''.join(output)
366            output = u_boot_console.run_command_list([
367                'efidebug boot order 1',
368                'efidebug test bootmgr'])
369            assert(not 'hELLO, world!' in ''.join(output))
370            assert('\'HELLO1\' failed' in ''.join(output))
371            assert('efi_bootmgr_load() returned: 26' in ''.join(output))
372