blob: d2ad6f9e73c015b30ff65a4d528309aaedaf2bfc [file] [log] [blame]
Miquel Raynal2dffe1c2018-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 Reddy8870daa2021-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 Raynal2dffe1c2018-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 Reddy8870daa2021-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')
Miquel Raynald87434a2018-07-19 22:35:10 +020044 output = u_boot_console.run_command('tpm2 init')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +020045 if force or not 'Error' in output:
46 u_boot_console.run_command('echo --- start of init ---')
Miquel Raynald87434a2018-07-19 22:35:10 +020047 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
48 u_boot_console.run_command('tpm2 self_test full')
49 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +020050 output = u_boot_console.run_command('echo $?')
51 if not output.endswith('0'):
Miquel Raynald87434a2018-07-19 22:35:10 +020052 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +020053 u_boot_console.run_command('echo --- end of init ---')
54
Simon Glass17d1fe12021-09-19 15:14:50 -060055def is_sandbox(cons):
56 # Array slice removes leading/trailing quotes.
57 sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
58 return sys_arch == 'sandbox'
59
Miquel Raynal2dffe1c2018-05-15 11:57:24 +020060@pytest.mark.buildconfigspec('cmd_tpm_v2')
61def test_tpm2_init(u_boot_console):
62 """Init the software stack to use TPMv2 commands."""
T Karthik Reddy8870daa2021-07-23 06:18:26 -060063 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
64 if skip_test:
65 pytest.skip('skip TPM device test')
Miquel Raynald87434a2018-07-19 22:35:10 +020066 u_boot_console.run_command('tpm2 init')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +020067 output = u_boot_console.run_command('echo $?')
68 assert output.endswith('0')
69
70@pytest.mark.buildconfigspec('cmd_tpm_v2')
71def test_tpm2_startup(u_boot_console):
72 """Execute a TPM2_Startup command.
73
74 Initiate the TPM internal state machine.
75 """
Simon Glass17d1fe12021-09-19 15:14:50 -060076 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
77 output = u_boot_console.run_command('echo $?')
78 assert output.endswith('0')
79
80def tpm2_sandbox_init(u_boot_console):
81 """Put sandbox back into a known state so we can run a test
82
83 This allows all tests to run in parallel, since no test depends on another.
84 """
85 u_boot_console.restart_uboot()
86 u_boot_console.run_command('tpm2 init')
87 output = u_boot_console.run_command('echo $?')
88 assert output.endswith('0')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +020089
T Karthik Reddy8870daa2021-07-23 06:18:26 -060090 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
91 if skip_test:
92 pytest.skip('skip TPM device test')
Miquel Raynald87434a2018-07-19 22:35:10 +020093 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +020094 output = u_boot_console.run_command('echo $?')
95 assert output.endswith('0')
96
Simon Glass17d1fe12021-09-19 15:14:50 -060097 u_boot_console.run_command('tpm2 self_test full')
98 output = u_boot_console.run_command('echo $?')
99 assert output.endswith('0')
100
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200101@pytest.mark.buildconfigspec('cmd_tpm_v2')
Simon Glass17d1fe12021-09-19 15:14:50 -0600102def test_tpm2_sandbox_self_test_full(u_boot_console):
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200103 """Execute a TPM2_SelfTest (full) command.
104
105 Ask the TPM to perform all self tests to also enable full capabilities.
106 """
Simon Glass17d1fe12021-09-19 15:14:50 -0600107 if is_sandbox(u_boot_console):
108 u_boot_console.restart_uboot()
109 u_boot_console.run_command('tpm2 init')
110 output = u_boot_console.run_command('echo $?')
111 assert output.endswith('0')
112
113 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
114 output = u_boot_console.run_command('echo $?')
115 assert output.endswith('0')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200116
T Karthik Reddy8870daa2021-07-23 06:18:26 -0600117 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
118 if skip_test:
119 pytest.skip('skip TPM device test')
Miquel Raynald87434a2018-07-19 22:35:10 +0200120 u_boot_console.run_command('tpm2 self_test full')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200121 output = u_boot_console.run_command('echo $?')
122 assert output.endswith('0')
123
124@pytest.mark.buildconfigspec('cmd_tpm_v2')
125def test_tpm2_continue_self_test(u_boot_console):
126 """Execute a TPM2_SelfTest (continued) command.
127
128 Ask the TPM to finish its self tests (alternative to the full test) in order
129 to enter a fully operational state.
130 """
131
T Karthik Reddy8870daa2021-07-23 06:18:26 -0600132 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
133 if skip_test:
134 pytest.skip('skip TPM device test')
Simon Glass17d1fe12021-09-19 15:14:50 -0600135 if is_sandbox(u_boot_console):
136 tpm2_sandbox_init(u_boot_console)
Miquel Raynald87434a2018-07-19 22:35:10 +0200137 u_boot_console.run_command('tpm2 self_test continue')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200138 output = u_boot_console.run_command('echo $?')
139 assert output.endswith('0')
140
141@pytest.mark.buildconfigspec('cmd_tpm_v2')
142def test_tpm2_clear(u_boot_console):
143 """Execute a TPM2_Clear command.
144
145 Ask the TPM to reset entirely its internal state (including internal
146 configuration, passwords, counters and DAM parameters). This is half of the
147 TAKE_OWNERSHIP command from TPMv1.
148
149 Use the LOCKOUT hierarchy for this. The LOCKOUT/PLATFORM hierarchies must
150 not have a password set, otherwise this test will fail. ENDORSEMENT and
151 PLATFORM hierarchies are also available.
152 """
Simon Glass17d1fe12021-09-19 15:14:50 -0600153 if is_sandbox(u_boot_console):
154 tpm2_sandbox_init(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200155
T Karthik Reddy8870daa2021-07-23 06:18:26 -0600156 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
157 if skip_test:
158 pytest.skip('skip TPM device test')
Miquel Raynald87434a2018-07-19 22:35:10 +0200159 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200160 output = u_boot_console.run_command('echo $?')
161 assert output.endswith('0')
162
Miquel Raynald87434a2018-07-19 22:35:10 +0200163 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200164 output = u_boot_console.run_command('echo $?')
165 assert output.endswith('0')
166
167@pytest.mark.buildconfigspec('cmd_tpm_v2')
168def test_tpm2_change_auth(u_boot_console):
169 """Execute a TPM2_HierarchyChangeAuth command.
170
171 Ask the TPM to change the owner, ie. set a new password: 'unicorn'
172
173 Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are
174 also available.
175 """
Simon Glass17d1fe12021-09-19 15:14:50 -0600176 if is_sandbox(u_boot_console):
177 tpm2_sandbox_init(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200178 force_init(u_boot_console)
179
Miquel Raynald87434a2018-07-19 22:35:10 +0200180 u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200181 output = u_boot_console.run_command('echo $?')
182 assert output.endswith('0')
183
Miquel Raynald87434a2018-07-19 22:35:10 +0200184 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200185 output = u_boot_console.run_command('echo $?')
Miquel Raynald87434a2018-07-19 22:35:10 +0200186 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200187 assert output.endswith('0')
188
Heinrich Schuchardt617270b2021-11-15 18:13:10 +0100189@pytest.mark.buildconfigspec('sandbox')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200190@pytest.mark.buildconfigspec('cmd_tpm_v2')
191def test_tpm2_get_capability(u_boot_console):
192 """Execute a TPM_GetCapability command.
193
194 Display one capability. In our test case, let's display the default DAM
195 lockout counter that should be 0 since the CLEAR:
196 - TPM_CAP_TPM_PROPERTIES = 0x6
197 - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14
198
199 There is no expected default values because it would depend on the chip
200 used. We can still save them in order to check they have changed later.
201 """
Simon Glass17d1fe12021-09-19 15:14:50 -0600202 if is_sandbox(u_boot_console):
203 tpm2_sandbox_init(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200204
205 force_init(u_boot_console)
206 ram = u_boot_utils.find_ram_base(u_boot_console)
207
Miquel Raynald87434a2018-07-19 22:35:10 +0200208 read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200209 output = u_boot_console.run_command('echo $?')
210 assert output.endswith('0')
211 assert 'Property 0x0000020e: 0x00000000' in read_cap
212
213@pytest.mark.buildconfigspec('cmd_tpm_v2')
214def test_tpm2_dam_parameters(u_boot_console):
215 """Execute a TPM2_DictionaryAttackParameters command.
216
217 Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change:
218 - Max number of failed authentication before lockout: 3
219 - Time before the failure counter is automatically decremented: 10 sec
220 - Time after a lockout failure before it can be attempted again: 0 sec
221
222 For an unknown reason, the DAM parameters must be changed before changing
223 the authentication, otherwise the lockout will be engaged after the first
224 failed authentication attempt.
225 """
Simon Glass17d1fe12021-09-19 15:14:50 -0600226 if is_sandbox(u_boot_console):
227 tpm2_sandbox_init(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200228 force_init(u_boot_console)
229 ram = u_boot_utils.find_ram_base(u_boot_console)
230
231 # Set the DAM parameters to known values
Miquel Raynald87434a2018-07-19 22:35:10 +0200232 u_boot_console.run_command('tpm2 dam_parameters 3 10 0')
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200233 output = u_boot_console.run_command('echo $?')
234 assert output.endswith('0')
235
236 # Check the values have been saved
Miquel Raynald87434a2018-07-19 22:35:10 +0200237 read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200238 output = u_boot_console.run_command('echo $?')
239 assert output.endswith('0')
240 assert 'Property 0x0000020f: 0x00000003' in read_cap
241 assert 'Property 0x00000210: 0x0000000a' in read_cap
242 assert 'Property 0x00000211: 0x00000000' in read_cap
243
244@pytest.mark.buildconfigspec('cmd_tpm_v2')
245def test_tpm2_pcr_read(u_boot_console):
246 """Execute a TPM2_PCR_Read command.
247
248 Perform a PCR read of the 0th PCR. Must be zero.
249 """
Simon Glass17d1fe12021-09-19 15:14:50 -0600250 if is_sandbox(u_boot_console):
251 tpm2_sandbox_init(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200252
253 force_init(u_boot_console)
Quentin Schulzf4eef402018-07-09 19:16:27 +0200254 ram = u_boot_utils.find_ram_base(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200255
Miquel Raynald87434a2018-07-19 22:35:10 +0200256 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200257 output = u_boot_console.run_command('echo $?')
258 assert output.endswith('0')
259
260 # Save the number of PCR updates
261 str = re.findall(r'\d+ known updates', read_pcr)[0]
262 global updates
263 updates = int(re.findall(r'\d+', str)[0])
264
265 # Check the output value
266 assert 'PCR #0 content' in read_pcr
267 assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
268
269@pytest.mark.buildconfigspec('cmd_tpm_v2')
270def test_tpm2_pcr_extend(u_boot_console):
271 """Execute a TPM2_PCR_Extend command.
272
273 Perform a PCR extension with a known hash in memory (zeroed since the board
274 must have been rebooted).
275
276 No authentication mechanism is used here, not protecting against packet
277 replay, yet.
278 """
Simon Glass17d1fe12021-09-19 15:14:50 -0600279 if is_sandbox(u_boot_console):
280 tpm2_sandbox_init(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200281 force_init(u_boot_console)
Quentin Schulzf4eef402018-07-09 19:16:27 +0200282 ram = u_boot_utils.find_ram_base(u_boot_console)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200283
Miquel Raynald87434a2018-07-19 22:35:10 +0200284 u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200285 output = u_boot_console.run_command('echo $?')
286 assert output.endswith('0')
287
Simon Glass1c6608b2021-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
290 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200291 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
Simon Glass1c6608b2021-07-18 14:18:06 -0600300 u_boot_console.run_command('tpm2 pcr_extend 0 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 0 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
Miquel Raynal2dffe1c2018-05-15 11:57:24 +0200314@pytest.mark.buildconfigspec('cmd_tpm_v2')
315def 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)