]> Git Repo - J-u-boot.git/blob - test/py/tests/test_tpm2.py
Merge tag 'u-boot-imx-master-20250127' of https://gitlab.denx.de/u-boot/custodians...
[J-u-boot.git] / test / py / tests / test_tpm2.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2018, Bootlin
3 # Author: Miquel Raynal <[email protected]>
4
5 import os.path
6 import pytest
7 import u_boot_utils
8 import re
9 import time
10
11 """
12 Test the TPMv2.x related commands. You must have a working hardware setup in
13 order to do these tests.
14
15 Notes:
16 * These tests will prove the password mechanism. The TPM chip must be cleared of
17 any password.
18 * Commands like pcr_setauthpolicy and pcr_resetauthpolicy are not implemented
19 here because they would fail the tests in most cases (TPMs do not implement them
20 and return an error).
21
22
23 Note:
24 This test doesn't rely on boardenv_* configuration value but can change test
25 behavior.
26
27 * Setup env__tpm_device_test_skip to True if tests with TPM devices should be
28 skipped.
29
30 """
31
32 updates = 0
33
34 def force_init(u_boot_console, force=False):
35     """When a test fails, U-Boot is reset. Because TPM stack must be initialized
36     after each reboot, we must ensure these lines are always executed before
37     trying any command or they will fail with no reason. Executing 'tpm init'
38     twice will spawn an error used to detect that the TPM was not reset and no
39     initialization code should be run.
40     """
41     skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
42     if skip_test:
43         pytest.skip('skip TPM device test')
44     output = u_boot_console.run_command('tpm2 autostart')
45     if force or not 'Error' in output:
46         u_boot_console.run_command('echo --- start of init ---')
47         u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
48         output = u_boot_console.run_command('echo $?')
49         if not output.endswith('0'):
50             u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
51         u_boot_console.run_command('echo --- end of init ---')
52
53 def is_sandbox(cons):
54     # Array slice removes leading/trailing quotes.
55     sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
56     return sys_arch == 'sandbox'
57
58 @pytest.mark.buildconfigspec('cmd_tpm_v2')
59 def test_tpm2_init(u_boot_console):
60     """Init the software stack to use TPMv2 commands."""
61     skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
62     if skip_test:
63         pytest.skip('skip TPM device test')
64     u_boot_console.run_command('tpm2 autostart')
65     output = u_boot_console.run_command('echo $?')
66     assert output.endswith('0')
67
68 @pytest.mark.buildconfigspec('cmd_tpm_v2')
69 def test_tpm2_startup(u_boot_console):
70     """Execute a TPM2_Startup command.
71
72     Initiate the TPM internal state machine.
73     """
74     skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
75     if skip_test:
76         pytest.skip('skip TPM device test')
77     u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
78     output = u_boot_console.run_command('echo $?')
79     assert output.endswith('0')
80
81 def tpm2_sandbox_init(u_boot_console):
82     """Put sandbox back into a known state so we can run a test
83
84     This allows all tests to run in parallel, since no test depends on another.
85     """
86     u_boot_console.restart_uboot()
87     u_boot_console.run_command('tpm2 autostart')
88     output = u_boot_console.run_command('echo $?')
89     assert output.endswith('0')
90
91     skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
92     if skip_test:
93         pytest.skip('skip TPM device test')
94
95 @pytest.mark.buildconfigspec('cmd_tpm_v2')
96 def test_tpm2_sandbox_self_test_full(u_boot_console):
97     """Execute a TPM2_SelfTest (full) command.
98
99     Ask the TPM to perform all self tests to also enable full capabilities.
100     """
101     if is_sandbox(u_boot_console):
102         u_boot_console.restart_uboot()
103         u_boot_console.run_command('tpm2 autostart')
104         output = u_boot_console.run_command('echo $?')
105         assert output.endswith('0')
106
107         u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
108         output = u_boot_console.run_command('echo $?')
109         assert output.endswith('0')
110
111     skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
112     if skip_test:
113         pytest.skip('skip TPM device test')
114     u_boot_console.run_command('tpm2 self_test full')
115     output = u_boot_console.run_command('echo $?')
116     assert output.endswith('0')
117
118 @pytest.mark.buildconfigspec('cmd_tpm_v2')
119 def test_tpm2_continue_self_test(u_boot_console):
120     """Execute a TPM2_SelfTest (continued) command.
121
122     Ask the TPM to finish its self tests (alternative to the full test) in order
123     to enter a fully operational state.
124     """
125
126     skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
127     if skip_test:
128         pytest.skip('skip TPM device test')
129     if is_sandbox(u_boot_console):
130         tpm2_sandbox_init(u_boot_console)
131     u_boot_console.run_command('tpm2 self_test continue')
132     output = u_boot_console.run_command('echo $?')
133     assert output.endswith('0')
134
135 @pytest.mark.buildconfigspec('cmd_tpm_v2')
136 def test_tpm2_clear(u_boot_console):
137     """Execute a TPM2_Clear command.
138
139     Ask the TPM to reset entirely its internal state (including internal
140     configuration, passwords, counters and DAM parameters). This is half of the
141     TAKE_OWNERSHIP command from TPMv1.
142
143     Use the LOCKOUT hierarchy for this. The LOCKOUT/PLATFORM hierarchies must
144     not have a password set, otherwise this test will fail. ENDORSEMENT and
145     PLATFORM hierarchies are also available.
146     """
147     if is_sandbox(u_boot_console):
148         tpm2_sandbox_init(u_boot_console)
149
150     skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
151     if skip_test:
152         pytest.skip('skip TPM device test')
153     u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
154     output = u_boot_console.run_command('echo $?')
155     assert output.endswith('0')
156
157     u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
158     output = u_boot_console.run_command('echo $?')
159     assert output.endswith('0')
160
161 @pytest.mark.buildconfigspec('cmd_tpm_v2')
162 def test_tpm2_change_auth(u_boot_console):
163     """Execute a TPM2_HierarchyChangeAuth command.
164
165     Ask the TPM to change the owner, ie. set a new password: 'unicorn'
166
167     Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are
168     also available.
169     """
170     if is_sandbox(u_boot_console):
171         tpm2_sandbox_init(u_boot_console)
172     force_init(u_boot_console)
173
174     u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
175     output = u_boot_console.run_command('echo $?')
176     assert output.endswith('0')
177
178     u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')
179     output = u_boot_console.run_command('echo $?')
180     u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
181     assert output.endswith('0')
182
183 @pytest.mark.buildconfigspec('sandbox')
184 @pytest.mark.buildconfigspec('cmd_tpm_v2')
185 def test_tpm2_get_capability(u_boot_console):
186     """Execute a TPM_GetCapability command.
187
188     Display one capability. In our test case, let's display the default DAM
189     lockout counter that should be 0 since the CLEAR:
190     - TPM_CAP_TPM_PROPERTIES = 0x6
191     - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14
192
193     There is no expected default values because it would depend on the chip
194     used. We can still save them in order to check they have changed later.
195     """
196     if is_sandbox(u_boot_console):
197         tpm2_sandbox_init(u_boot_console)
198
199     force_init(u_boot_console)
200     ram = u_boot_utils.find_ram_base(u_boot_console)
201
202     read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram)
203     output = u_boot_console.run_command('echo $?')
204     assert output.endswith('0')
205     assert 'Property 0x0000020e: 0x00000000' in read_cap
206
207 @pytest.mark.buildconfigspec('cmd_tpm_v2')
208 def test_tpm2_dam_parameters(u_boot_console):
209     """Execute a TPM2_DictionaryAttackParameters command.
210
211     Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change:
212     - Max number of failed authentication before lockout: 3
213     - Time before the failure counter is automatically decremented: 10 sec
214     - Time after a lockout failure before it can be attempted again: 0 sec
215
216     For an unknown reason, the DAM parameters must be changed before changing
217     the authentication, otherwise the lockout will be engaged after the first
218     failed authentication attempt.
219     """
220     if is_sandbox(u_boot_console):
221         tpm2_sandbox_init(u_boot_console)
222     force_init(u_boot_console)
223     ram = u_boot_utils.find_ram_base(u_boot_console)
224
225     # Set the DAM parameters to known values
226     u_boot_console.run_command('tpm2 dam_parameters 3 10 0')
227     output = u_boot_console.run_command('echo $?')
228     assert output.endswith('0')
229
230     # Check the values have been saved
231     read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram)
232     output = u_boot_console.run_command('echo $?')
233     assert output.endswith('0')
234     assert 'Property 0x0000020f: 0x00000003' in read_cap
235     assert 'Property 0x00000210: 0x0000000a' in read_cap
236     assert 'Property 0x00000211: 0x00000000' in read_cap
237
238 @pytest.mark.buildconfigspec('cmd_tpm_v2')
239 def test_tpm2_pcr_read(u_boot_console):
240     """Execute a TPM2_PCR_Read command.
241
242     Perform a PCR read of the 10th PCR. Must be zero.
243     """
244     if is_sandbox(u_boot_console):
245         tpm2_sandbox_init(u_boot_console)
246
247     force_init(u_boot_console)
248     ram = u_boot_utils.find_ram_base(u_boot_console)
249
250     read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % ram)
251     output = u_boot_console.run_command('echo $?')
252     assert output.endswith('0')
253
254     # Save the number of PCR updates
255     str = re.findall(r'\d+ known updates', read_pcr)[0]
256     global updates
257     updates = int(re.findall(r'\d+', str)[0])
258
259     # Check the output value
260     assert 'PCR #10 sha256 32 byte content' in read_pcr
261     assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
262
263 @pytest.mark.buildconfigspec('cmd_tpm_v2')
264 def test_tpm2_pcr_extend(u_boot_console):
265     """Execute a TPM2_PCR_Extend command.
266
267     Perform a PCR extension with a known hash in memory (zeroed since the board
268     must have been rebooted).
269
270     No authentication mechanism is used here, not protecting against packet
271     replay, yet.
272     """
273     if is_sandbox(u_boot_console):
274         tpm2_sandbox_init(u_boot_console)
275     force_init(u_boot_console)
276     ram = u_boot_utils.find_ram_base(u_boot_console)
277
278     read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
279     output = u_boot_console.run_command('echo $?')
280     assert output.endswith('0')
281     str = re.findall(r'\d+ known updates', read_pcr)[0]
282     updates = int(re.findall(r'\d+', str)[0])
283
284     u_boot_console.run_command('tpm2 pcr_extend 10 0x%x' % ram)
285     output = u_boot_console.run_command('echo $?')
286     assert output.endswith('0')
287
288     # Read the value back into a different place so we can still use 'ram' as
289     # our zero bytes
290     read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
291     output = u_boot_console.run_command('echo $?')
292     assert output.endswith('0')
293     assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr
294     assert '43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b' in read_pcr
295
296     str = re.findall(r'\d+ known updates', read_pcr)[0]
297     new_updates = int(re.findall(r'\d+', str)[0])
298     assert (updates + 1) == new_updates
299
300     u_boot_console.run_command('tpm2 pcr_extend 10 0x%x' % ram)
301     output = u_boot_console.run_command('echo $?')
302     assert output.endswith('0')
303
304     read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
305     output = u_boot_console.run_command('echo $?')
306     assert output.endswith('0')
307     assert '7a 05 01 f5 95 7b df 9c b3 a8 ff 49 66 f0 22 65' in read_pcr
308     assert 'f9 68 65 8b 7a 9c 62 64 2c ba 11 65 e8 66 42 f5' in read_pcr
309
310     str = re.findall(r'\d+ known updates', read_pcr)[0]
311     new_updates = int(re.findall(r'\d+', str)[0])
312     assert (updates + 2) == new_updates
313
314 @pytest.mark.buildconfigspec('cmd_tpm_v2')
315 def test_tpm2_cleanup(u_boot_console):
316     """Ensure the TPM is cleared from password or test related configuration."""
317
318     force_init(u_boot_console, True)
This page took 0.05292 seconds and 4 git commands to generate.