blob: 1ade66a7eda4cd1fd2ddf643dfb5f073e15d4233 [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
7import u_boot_utils
8import 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
34def 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 """
T Karthik Reddyeca717c2021-07-23 06:18:26 -060041 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')
Ilias Apalodimas55c8ce52023-06-07 12:18:11 +030044 output = u_boot_console.run_command('tpm2 autostart')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020045 if force or not 'Error' in output:
46 u_boot_console.run_command('echo --- start of init ---')
Miquel Raynal6a53b822018-07-19 22:35:10 +020047 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020048 output = u_boot_console.run_command('echo $?')
49 if not output.endswith('0'):
Miquel Raynal6a53b822018-07-19 22:35:10 +020050 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020051 u_boot_console.run_command('echo --- end of init ---')
52
Simon Glass9fdddc82021-09-19 15:14:50 -060053def 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
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020058@pytest.mark.buildconfigspec('cmd_tpm_v2')
59def test_tpm2_init(u_boot_console):
60 """Init the software stack to use TPMv2 commands."""
T Karthik Reddyeca717c2021-07-23 06:18:26 -060061 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')
Miquel Raynal6a53b822018-07-19 22:35:10 +020064 u_boot_console.run_command('tpm2 init')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020065 output = u_boot_console.run_command('echo $?')
66 assert output.endswith('0')
67
68@pytest.mark.buildconfigspec('cmd_tpm_v2')
69def test_tpm2_startup(u_boot_console):
70 """Execute a TPM2_Startup command.
71
72 Initiate the TPM internal state machine.
73 """
Simon Glass9fdddc82021-09-19 15:14:50 -060074 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
75 output = u_boot_console.run_command('echo $?')
76 assert output.endswith('0')
77
78def tpm2_sandbox_init(u_boot_console):
79 """Put sandbox back into a known state so we can run a test
80
81 This allows all tests to run in parallel, since no test depends on another.
82 """
83 u_boot_console.restart_uboot()
Ilias Apalodimas55c8ce52023-06-07 12:18:11 +030084 u_boot_console.run_command('tpm2 autostart')
Simon Glass9fdddc82021-09-19 15:14:50 -060085 output = u_boot_console.run_command('echo $?')
86 assert output.endswith('0')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020087
T Karthik Reddyeca717c2021-07-23 06:18:26 -060088 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
89 if skip_test:
90 pytest.skip('skip TPM device test')
Simon Glass9fdddc82021-09-19 15:14:50 -060091
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020092@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glass9fdddc82021-09-19 15:14:50 -060093def test_tpm2_sandbox_self_test_full(u_boot_console):
Miquel Raynalcaaf40a2018-05-15 11:57:24 +020094 """Execute a TPM2_SelfTest (full) command.
95
96 Ask the TPM to perform all self tests to also enable full capabilities.
97 """
Simon Glass9fdddc82021-09-19 15:14:50 -060098 if is_sandbox(u_boot_console):
99 u_boot_console.restart_uboot()
100 u_boot_console.run_command('tpm2 init')
101 output = u_boot_console.run_command('echo $?')
102 assert output.endswith('0')
103
104 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
105 output = u_boot_console.run_command('echo $?')
106 assert output.endswith('0')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200107
T Karthik Reddyeca717c2021-07-23 06:18:26 -0600108 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
109 if skip_test:
110 pytest.skip('skip TPM device test')
Miquel Raynal6a53b822018-07-19 22:35:10 +0200111 u_boot_console.run_command('tpm2 self_test full')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200112 output = u_boot_console.run_command('echo $?')
113 assert output.endswith('0')
114
115@pytest.mark.buildconfigspec('cmd_tpm_v2')
116def test_tpm2_continue_self_test(u_boot_console):
117 """Execute a TPM2_SelfTest (continued) command.
118
119 Ask the TPM to finish its self tests (alternative to the full test) in order
120 to enter a fully operational state.
121 """
122
T Karthik Reddyeca717c2021-07-23 06:18:26 -0600123 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
124 if skip_test:
125 pytest.skip('skip TPM device test')
Simon Glass9fdddc82021-09-19 15:14:50 -0600126 if is_sandbox(u_boot_console):
127 tpm2_sandbox_init(u_boot_console)
Miquel Raynal6a53b822018-07-19 22:35:10 +0200128 u_boot_console.run_command('tpm2 self_test continue')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200129 output = u_boot_console.run_command('echo $?')
130 assert output.endswith('0')
131
132@pytest.mark.buildconfigspec('cmd_tpm_v2')
133def test_tpm2_clear(u_boot_console):
134 """Execute a TPM2_Clear command.
135
136 Ask the TPM to reset entirely its internal state (including internal
137 configuration, passwords, counters and DAM parameters). This is half of the
138 TAKE_OWNERSHIP command from TPMv1.
139
140 Use the LOCKOUT hierarchy for this. The LOCKOUT/PLATFORM hierarchies must
141 not have a password set, otherwise this test will fail. ENDORSEMENT and
142 PLATFORM hierarchies are also available.
143 """
Simon Glass9fdddc82021-09-19 15:14:50 -0600144 if is_sandbox(u_boot_console):
145 tpm2_sandbox_init(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200146
T Karthik Reddyeca717c2021-07-23 06:18:26 -0600147 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
148 if skip_test:
149 pytest.skip('skip TPM device test')
Miquel Raynal6a53b822018-07-19 22:35:10 +0200150 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200151 output = u_boot_console.run_command('echo $?')
152 assert output.endswith('0')
153
Miquel Raynal6a53b822018-07-19 22:35:10 +0200154 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200155 output = u_boot_console.run_command('echo $?')
156 assert output.endswith('0')
157
158@pytest.mark.buildconfigspec('cmd_tpm_v2')
159def test_tpm2_change_auth(u_boot_console):
160 """Execute a TPM2_HierarchyChangeAuth command.
161
162 Ask the TPM to change the owner, ie. set a new password: 'unicorn'
163
164 Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are
165 also available.
166 """
Simon Glass9fdddc82021-09-19 15:14:50 -0600167 if is_sandbox(u_boot_console):
168 tpm2_sandbox_init(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200169 force_init(u_boot_console)
170
Miquel Raynal6a53b822018-07-19 22:35:10 +0200171 u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200172 output = u_boot_console.run_command('echo $?')
173 assert output.endswith('0')
174
Miquel Raynal6a53b822018-07-19 22:35:10 +0200175 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200176 output = u_boot_console.run_command('echo $?')
Miquel Raynal6a53b822018-07-19 22:35:10 +0200177 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200178 assert output.endswith('0')
179
Heinrich Schuchardta0faaff2021-11-15 18:13:10 +0100180@pytest.mark.buildconfigspec('sandbox')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200181@pytest.mark.buildconfigspec('cmd_tpm_v2')
182def test_tpm2_get_capability(u_boot_console):
183 """Execute a TPM_GetCapability command.
184
185 Display one capability. In our test case, let's display the default DAM
186 lockout counter that should be 0 since the CLEAR:
187 - TPM_CAP_TPM_PROPERTIES = 0x6
188 - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14
189
190 There is no expected default values because it would depend on the chip
191 used. We can still save them in order to check they have changed later.
192 """
Simon Glass9fdddc82021-09-19 15:14:50 -0600193 if is_sandbox(u_boot_console):
194 tpm2_sandbox_init(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200195
196 force_init(u_boot_console)
197 ram = u_boot_utils.find_ram_base(u_boot_console)
198
Miquel Raynal6a53b822018-07-19 22:35:10 +0200199 read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200200 output = u_boot_console.run_command('echo $?')
201 assert output.endswith('0')
202 assert 'Property 0x0000020e: 0x00000000' in read_cap
203
204@pytest.mark.buildconfigspec('cmd_tpm_v2')
205def test_tpm2_dam_parameters(u_boot_console):
206 """Execute a TPM2_DictionaryAttackParameters command.
207
208 Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change:
209 - Max number of failed authentication before lockout: 3
210 - Time before the failure counter is automatically decremented: 10 sec
211 - Time after a lockout failure before it can be attempted again: 0 sec
212
213 For an unknown reason, the DAM parameters must be changed before changing
214 the authentication, otherwise the lockout will be engaged after the first
215 failed authentication attempt.
216 """
Simon Glass9fdddc82021-09-19 15:14:50 -0600217 if is_sandbox(u_boot_console):
218 tpm2_sandbox_init(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200219 force_init(u_boot_console)
220 ram = u_boot_utils.find_ram_base(u_boot_console)
221
222 # Set the DAM parameters to known values
Miquel Raynal6a53b822018-07-19 22:35:10 +0200223 u_boot_console.run_command('tpm2 dam_parameters 3 10 0')
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200224 output = u_boot_console.run_command('echo $?')
225 assert output.endswith('0')
226
227 # Check the values have been saved
Miquel Raynal6a53b822018-07-19 22:35:10 +0200228 read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200229 output = u_boot_console.run_command('echo $?')
230 assert output.endswith('0')
231 assert 'Property 0x0000020f: 0x00000003' in read_cap
232 assert 'Property 0x00000210: 0x0000000a' in read_cap
233 assert 'Property 0x00000211: 0x00000000' in read_cap
234
235@pytest.mark.buildconfigspec('cmd_tpm_v2')
236def test_tpm2_pcr_read(u_boot_console):
237 """Execute a TPM2_PCR_Read command.
238
239 Perform a PCR read of the 0th PCR. Must be zero.
240 """
Simon Glass9fdddc82021-09-19 15:14:50 -0600241 if is_sandbox(u_boot_console):
242 tpm2_sandbox_init(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200243
244 force_init(u_boot_console)
Quentin Schulzdae1aa82018-07-09 19:16:27 +0200245 ram = u_boot_utils.find_ram_base(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200246
Miquel Raynal6a53b822018-07-19 22:35:10 +0200247 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200248 output = u_boot_console.run_command('echo $?')
249 assert output.endswith('0')
250
251 # Save the number of PCR updates
252 str = re.findall(r'\d+ known updates', read_pcr)[0]
253 global updates
254 updates = int(re.findall(r'\d+', str)[0])
255
256 # Check the output value
257 assert 'PCR #0 content' in read_pcr
258 assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
259
260@pytest.mark.buildconfigspec('cmd_tpm_v2')
261def test_tpm2_pcr_extend(u_boot_console):
262 """Execute a TPM2_PCR_Extend command.
263
264 Perform a PCR extension with a known hash in memory (zeroed since the board
265 must have been rebooted).
266
267 No authentication mechanism is used here, not protecting against packet
268 replay, yet.
269 """
Simon Glass9fdddc82021-09-19 15:14:50 -0600270 if is_sandbox(u_boot_console):
271 tpm2_sandbox_init(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200272 force_init(u_boot_console)
Quentin Schulzdae1aa82018-07-09 19:16:27 +0200273 ram = u_boot_utils.find_ram_base(u_boot_console)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200274
Miquel Raynal6a53b822018-07-19 22:35:10 +0200275 u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200276 output = u_boot_console.run_command('echo $?')
277 assert output.endswith('0')
278
Simon Glass00211022021-07-18 14:18:06 -0600279 # Read the value back into a different place so we can still use 'ram' as
280 # our zero bytes
281 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200282 output = u_boot_console.run_command('echo $?')
283 assert output.endswith('0')
284 assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr
285 assert '43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b' in read_pcr
286
287 str = re.findall(r'\d+ known updates', read_pcr)[0]
288 new_updates = int(re.findall(r'\d+', str)[0])
289 assert (updates + 1) == new_updates
290
Simon Glass00211022021-07-18 14:18:06 -0600291 u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
292 output = u_boot_console.run_command('echo $?')
293 assert output.endswith('0')
294
295 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
296 output = u_boot_console.run_command('echo $?')
297 assert output.endswith('0')
298 assert '7a 05 01 f5 95 7b df 9c b3 a8 ff 49 66 f0 22 65' in read_pcr
299 assert 'f9 68 65 8b 7a 9c 62 64 2c ba 11 65 e8 66 42 f5' in read_pcr
300
301 str = re.findall(r'\d+ known updates', read_pcr)[0]
302 new_updates = int(re.findall(r'\d+', str)[0])
303 assert (updates + 2) == new_updates
304
Miquel Raynalcaaf40a2018-05-15 11:57:24 +0200305@pytest.mark.buildconfigspec('cmd_tpm_v2')
306def test_tpm2_cleanup(u_boot_console):
307 """Ensure the TPM is cleared from password or test related configuration."""
308
309 force_init(u_boot_console, True)