blob: 064651c3e23d21df4dbdd53e3ec439f53a07f331 [file] [log] [blame]
Miquel Raynalcaaf40a2018-05-15 11:57:24 +02001# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2018, Bootlin
3# Author: Miquel Raynal <miquel.raynal@bootlin.com>
4
5import os.path
6import pytest
Simon Glassfb916372025-02-09 09:07:15 -07007import utils
Miquel Raynalcaaf40a2018-05-15 11:57:24 +02008import re
9import time
10
11"""
12Test the TPMv2.x related commands. You must have a working hardware setup in
13order to do these tests.
14
15Notes:
16* These tests will prove the password mechanism. The TPM chip must be cleared of
17any password.
18* Commands like pcr_setauthpolicy and pcr_resetauthpolicy are not implemented
19here because they would fail the tests in most cases (TPMs do not implement them
20and return an error).
T Karthik Reddyeca717c2021-07-23 06:18:26 -060021
22
23Note:
24This test doesn't rely on boardenv_* configuration value but can change test
25behavior.
26
27* Setup env__tpm_device_test_skip to True if tests with TPM devices should be
28skipped.
29
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020030"""
31
32updates = 0
33
Simon Glassddba5202025-02-09 09:07:14 -070034def force_init(ubman, force=False):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020035 """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 """
Simon Glassddba5202025-02-09 09:07:14 -070041 skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
T Karthik Reddyeca717c2021-07-23 06:18:26 -060042 if skip_test:
43 pytest.skip('skip TPM device test')
Simon Glassddba5202025-02-09 09:07:14 -070044 output = ubman.run_command('tpm2 autostart')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020045 if force or not 'Error' in output:
Simon Glassddba5202025-02-09 09:07:14 -070046 ubman.run_command('echo --- start of init ---')
47 ubman.run_command('tpm2 clear TPM2_RH_LOCKOUT')
48 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020049 if not output.endswith('0'):
Simon Glassddba5202025-02-09 09:07:14 -070050 ubman.run_command('tpm2 clear TPM2_RH_PLATFORM')
51 ubman.run_command('echo --- end of init ---')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020052
Simon Glass32701112025-02-09 09:07:17 -070053def is_sandbox(ubman):
Simon Glass9fdddc82021-09-19 15:14:50 -060054 # Array slice removes leading/trailing quotes.
Simon Glass32701112025-02-09 09:07:17 -070055 sys_arch = ubman.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
Simon Glass9fdddc82021-09-19 15:14:50 -060056 return sys_arch == 'sandbox'
57
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020058@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -070059def test_tpm2_init(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020060 """Init the software stack to use TPMv2 commands."""
Simon Glassddba5202025-02-09 09:07:14 -070061 skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
T Karthik Reddyeca717c2021-07-23 06:18:26 -060062 if skip_test:
63 pytest.skip('skip TPM device test')
Simon Glassddba5202025-02-09 09:07:14 -070064 ubman.run_command('tpm2 autostart')
65 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020066 assert output.endswith('0')
67
68@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -070069def test_tpm2_startup(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020070 """Execute a TPM2_Startup command.
71
72 Initiate the TPM internal state machine.
73 """
Simon Glassddba5202025-02-09 09:07:14 -070074 skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
Michal Simeka59a24b2023-08-30 18:36:23 +020075 if skip_test:
76 pytest.skip('skip TPM device test')
Simon Glassddba5202025-02-09 09:07:14 -070077 ubman.run_command('tpm2 startup TPM2_SU_CLEAR')
78 output = ubman.run_command('echo $?')
Simon Glass9fdddc82021-09-19 15:14:50 -060079 assert output.endswith('0')
80
Simon Glassddba5202025-02-09 09:07:14 -070081def tpm2_sandbox_init(ubman):
Simon Glass9fdddc82021-09-19 15:14:50 -060082 """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 """
Simon Glassddba5202025-02-09 09:07:14 -070086 ubman.restart_uboot()
87 ubman.run_command('tpm2 autostart')
88 output = ubman.run_command('echo $?')
Simon Glass9fdddc82021-09-19 15:14:50 -060089 assert output.endswith('0')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020090
Simon Glassddba5202025-02-09 09:07:14 -070091 skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
T Karthik Reddyeca717c2021-07-23 06:18:26 -060092 if skip_test:
93 pytest.skip('skip TPM device test')
Simon Glass9fdddc82021-09-19 15:14:50 -060094
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020095@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -070096def test_tpm2_sandbox_self_test_full(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020097 """Execute a TPM2_SelfTest (full) command.
98
99 Ask the TPM to perform all self tests to also enable full capabilities.
100 """
Simon Glassddba5202025-02-09 09:07:14 -0700101 if is_sandbox(ubman):
102 ubman.restart_uboot()
103 ubman.run_command('tpm2 autostart')
104 output = ubman.run_command('echo $?')
Simon Glass9fdddc82021-09-19 15:14:50 -0600105 assert output.endswith('0')
106
Simon Glassddba5202025-02-09 09:07:14 -0700107 ubman.run_command('tpm2 startup TPM2_SU_CLEAR')
108 output = ubman.run_command('echo $?')
Simon Glass9fdddc82021-09-19 15:14:50 -0600109 assert output.endswith('0')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200110
Simon Glassddba5202025-02-09 09:07:14 -0700111 skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
T Karthik Reddyeca717c2021-07-23 06:18:26 -0600112 if skip_test:
113 pytest.skip('skip TPM device test')
Simon Glassddba5202025-02-09 09:07:14 -0700114 ubman.run_command('tpm2 self_test full')
115 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200116 assert output.endswith('0')
117
118@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -0700119def test_tpm2_continue_self_test(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200120 """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
Simon Glassddba5202025-02-09 09:07:14 -0700126 skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
T Karthik Reddyeca717c2021-07-23 06:18:26 -0600127 if skip_test:
128 pytest.skip('skip TPM device test')
Simon Glassddba5202025-02-09 09:07:14 -0700129 if is_sandbox(ubman):
130 tpm2_sandbox_init(ubman)
131 ubman.run_command('tpm2 self_test continue')
132 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200133 assert output.endswith('0')
134
135@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -0700136def test_tpm2_clear(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200137 """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 """
Simon Glassddba5202025-02-09 09:07:14 -0700147 if is_sandbox(ubman):
148 tpm2_sandbox_init(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200149
Simon Glassddba5202025-02-09 09:07:14 -0700150 skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
T Karthik Reddyeca717c2021-07-23 06:18:26 -0600151 if skip_test:
152 pytest.skip('skip TPM device test')
Simon Glassddba5202025-02-09 09:07:14 -0700153 ubman.run_command('tpm2 clear TPM2_RH_LOCKOUT')
154 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200155 assert output.endswith('0')
156
Simon Glassddba5202025-02-09 09:07:14 -0700157 ubman.run_command('tpm2 clear TPM2_RH_PLATFORM')
158 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200159 assert output.endswith('0')
160
161@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -0700162def test_tpm2_change_auth(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200163 """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 """
Simon Glassddba5202025-02-09 09:07:14 -0700170 if is_sandbox(ubman):
171 tpm2_sandbox_init(ubman)
172 force_init(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200173
Simon Glassddba5202025-02-09 09:07:14 -0700174 ubman.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
175 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200176 assert output.endswith('0')
177
Simon Glassddba5202025-02-09 09:07:14 -0700178 ubman.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')
179 output = ubman.run_command('echo $?')
180 ubman.run_command('tpm2 clear TPM2_RH_PLATFORM')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200181 assert output.endswith('0')
182
Heinrich Schuchardta0faaff2021-11-15 18:13:10 +0100183@pytest.mark.buildconfigspec('sandbox')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200184@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -0700185def test_tpm2_get_capability(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200186 """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 """
Simon Glassddba5202025-02-09 09:07:14 -0700196 if is_sandbox(ubman):
197 tpm2_sandbox_init(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200198
Simon Glassddba5202025-02-09 09:07:14 -0700199 force_init(ubman)
Simon Glassfb916372025-02-09 09:07:15 -0700200 ram = utils.find_ram_base(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200201
Simon Glassddba5202025-02-09 09:07:14 -0700202 read_cap = ubman.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram)
203 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200204 assert output.endswith('0')
205 assert 'Property 0x0000020e: 0x00000000' in read_cap
206
207@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -0700208def test_tpm2_dam_parameters(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200209 """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 """
Simon Glassddba5202025-02-09 09:07:14 -0700220 if is_sandbox(ubman):
221 tpm2_sandbox_init(ubman)
222 force_init(ubman)
Simon Glassfb916372025-02-09 09:07:15 -0700223 ram = utils.find_ram_base(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200224
225 # Set the DAM parameters to known values
Simon Glassddba5202025-02-09 09:07:14 -0700226 ubman.run_command('tpm2 dam_parameters 3 10 0')
227 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200228 assert output.endswith('0')
229
230 # Check the values have been saved
Simon Glassddba5202025-02-09 09:07:14 -0700231 read_cap = ubman.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram)
232 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200233 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')
Simon Glassddba5202025-02-09 09:07:14 -0700239def test_tpm2_pcr_read(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200240 """Execute a TPM2_PCR_Read command.
241
Ilias Apalodimasa2cea822023-10-24 10:43:54 -0500242 Perform a PCR read of the 10th PCR. Must be zero.
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200243 """
Simon Glassddba5202025-02-09 09:07:14 -0700244 if is_sandbox(ubman):
245 tpm2_sandbox_init(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200246
Simon Glassddba5202025-02-09 09:07:14 -0700247 force_init(ubman)
Simon Glassfb916372025-02-09 09:07:15 -0700248 ram = utils.find_ram_base(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200249
Simon Glassddba5202025-02-09 09:07:14 -0700250 read_pcr = ubman.run_command('tpm2 pcr_read 10 0x%x' % ram)
251 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200252 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
Tim Harveybbfa6082024-05-25 13:00:49 -0700260 assert 'PCR #10 sha256 32 byte content' in read_pcr
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200261 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')
Simon Glassddba5202025-02-09 09:07:14 -0700264def test_tpm2_pcr_extend(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200265 """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 """
Simon Glassddba5202025-02-09 09:07:14 -0700273 if is_sandbox(ubman):
274 tpm2_sandbox_init(ubman)
275 force_init(ubman)
Simon Glassfb916372025-02-09 09:07:15 -0700276 ram = utils.find_ram_base(ubman)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200277
Simon Glassddba5202025-02-09 09:07:14 -0700278 read_pcr = ubman.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
279 output = ubman.run_command('echo $?')
Ilias Apalodimasc5967e72023-06-07 12:18:12 +0300280 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
Simon Glassddba5202025-02-09 09:07:14 -0700284 ubman.run_command('tpm2 pcr_extend 10 0x%x' % ram)
285 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200286 assert output.endswith('0')
287
Simon Glass00211022021-07-18 14:18:06 -0600288 # Read the value back into a different place so we can still use 'ram' as
289 # our zero bytes
Simon Glassddba5202025-02-09 09:07:14 -0700290 read_pcr = ubman.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
291 output = ubman.run_command('echo $?')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200292 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
Simon Glassddba5202025-02-09 09:07:14 -0700300 ubman.run_command('tpm2 pcr_extend 10 0x%x' % ram)
301 output = ubman.run_command('echo $?')
Simon Glass00211022021-07-18 14:18:06 -0600302 assert output.endswith('0')
303
Simon Glassddba5202025-02-09 09:07:14 -0700304 read_pcr = ubman.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
305 output = ubman.run_command('echo $?')
Simon Glass00211022021-07-18 14:18:06 -0600306 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
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200314@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glassddba5202025-02-09 09:07:14 -0700315def test_tpm2_cleanup(ubman):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200316 """Ensure the TPM is cleared from password or test related configuration."""
317
Simon Glassddba5202025-02-09 09:07:14 -0700318 force_init(ubman, True)