1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2018, Bootlin
3 # Author: Miquel Raynal <miquel.raynal@bootlin.com>
12 Test the TPMv2.x related commands. You must have a working hardware setup in
13 order to do these tests.
16 * These tests will prove the password mechanism. The TPM chip must be cleared of
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
24 This test doesn't rely on boardenv_* configuration value but can change test
27 * Setup env__tpm_device_test_skip to True if tests with TPM devices should be
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.
41 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
43 pytest.skip('skip TPM device test')
44 output = u_boot_console.run_command('tpm2 init')
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 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')
50 output = u_boot_console.run_command('echo $?')
51 if not output.endswith('0'):
52 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
53 u_boot_console.run_command('echo --- end of init ---')
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'
60 @pytest.mark.buildconfigspec('cmd_tpm_v2')
61 def test_tpm2_init(u_boot_console):
62 """Init the software stack to use TPMv2 commands."""
63 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
65 pytest.skip('skip TPM device test')
66 u_boot_console.run_command('tpm2 init')
67 output = u_boot_console.run_command('echo $?')
68 assert output.endswith('0')
70 @pytest.mark.buildconfigspec('cmd_tpm_v2')
71 def test_tpm2_startup(u_boot_console):
72 """Execute a TPM2_Startup command.
74 Initiate the TPM internal state machine.
76 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
77 output = u_boot_console.run_command('echo $?')
78 assert output.endswith('0')
80 def tpm2_sandbox_init(u_boot_console):
81 """Put sandbox back into a known state so we can run a test
83 This allows all tests to run in parallel, since no test depends on another.
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')
90 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
92 pytest.skip('skip TPM device test')
93 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
94 output = u_boot_console.run_command('echo $?')
95 assert output.endswith('0')
97 u_boot_console.run_command('tpm2 self_test full')
98 output = u_boot_console.run_command('echo $?')
99 assert output.endswith('0')
101 @pytest.mark.buildconfigspec('cmd_tpm_v2')
102 def test_tpm2_sandbox_self_test_full(u_boot_console):
103 """Execute a TPM2_SelfTest (full) command.
105 Ask the TPM to perform all self tests to also enable full capabilities.
107 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')
113 u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
114 output = u_boot_console.run_command('echo $?')
115 assert output.endswith('0')
117 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
119 pytest.skip('skip TPM device test')
120 u_boot_console.run_command('tpm2 self_test full')
121 output = u_boot_console.run_command('echo $?')
122 assert output.endswith('0')
124 @pytest.mark.buildconfigspec('cmd_tpm_v2')
125 def test_tpm2_continue_self_test(u_boot_console):
126 """Execute a TPM2_SelfTest (continued) command.
128 Ask the TPM to finish its self tests (alternative to the full test) in order
129 to enter a fully operational state.
132 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
134 pytest.skip('skip TPM device test')
135 if is_sandbox(u_boot_console):
136 tpm2_sandbox_init(u_boot_console)
137 u_boot_console.run_command('tpm2 self_test continue')
138 output = u_boot_console.run_command('echo $?')
139 assert output.endswith('0')
141 @pytest.mark.buildconfigspec('cmd_tpm_v2')
142 def test_tpm2_clear(u_boot_console):
143 """Execute a TPM2_Clear command.
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.
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.
153 if is_sandbox(u_boot_console):
154 tpm2_sandbox_init(u_boot_console)
156 skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
158 pytest.skip('skip TPM device test')
159 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
160 output = u_boot_console.run_command('echo $?')
161 assert output.endswith('0')
163 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
164 output = u_boot_console.run_command('echo $?')
165 assert output.endswith('0')
167 @pytest.mark.buildconfigspec('cmd_tpm_v2')
168 def test_tpm2_change_auth(u_boot_console):
169 """Execute a TPM2_HierarchyChangeAuth command.
171 Ask the TPM to change the owner, ie. set a new password: 'unicorn'
173 Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are
176 if is_sandbox(u_boot_console):
177 tpm2_sandbox_init(u_boot_console)
178 force_init(u_boot_console)
180 u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
181 output = u_boot_console.run_command('echo $?')
182 assert output.endswith('0')
184 u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')
185 output = u_boot_console.run_command('echo $?')
186 u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
187 assert output.endswith('0')
189 @pytest.mark.buildconfigspec('cmd_tpm_v2')
190 def test_tpm2_get_capability(u_boot_console):
191 """Execute a TPM_GetCapability command.
193 Display one capability. In our test case, let's display the default DAM
194 lockout counter that should be 0 since the CLEAR:
195 - TPM_CAP_TPM_PROPERTIES = 0x6
196 - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14
198 There is no expected default values because it would depend on the chip
199 used. We can still save them in order to check they have changed later.
201 if is_sandbox(u_boot_console):
202 tpm2_sandbox_init(u_boot_console)
204 force_init(u_boot_console)
205 ram = u_boot_utils.find_ram_base(u_boot_console)
207 read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram)
208 output = u_boot_console.run_command('echo $?')
209 assert output.endswith('0')
210 assert 'Property 0x0000020e: 0x00000000' in read_cap
212 @pytest.mark.buildconfigspec('cmd_tpm_v2')
213 def test_tpm2_dam_parameters(u_boot_console):
214 """Execute a TPM2_DictionaryAttackParameters command.
216 Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change:
217 - Max number of failed authentication before lockout: 3
218 - Time before the failure counter is automatically decremented: 10 sec
219 - Time after a lockout failure before it can be attempted again: 0 sec
221 For an unknown reason, the DAM parameters must be changed before changing
222 the authentication, otherwise the lockout will be engaged after the first
223 failed authentication attempt.
225 if is_sandbox(u_boot_console):
226 tpm2_sandbox_init(u_boot_console)
227 force_init(u_boot_console)
228 ram = u_boot_utils.find_ram_base(u_boot_console)
230 # Set the DAM parameters to known values
231 u_boot_console.run_command('tpm2 dam_parameters 3 10 0')
232 output = u_boot_console.run_command('echo $?')
233 assert output.endswith('0')
235 # Check the values have been saved
236 read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram)
237 output = u_boot_console.run_command('echo $?')
238 assert output.endswith('0')
239 assert 'Property 0x0000020f: 0x00000003' in read_cap
240 assert 'Property 0x00000210: 0x0000000a' in read_cap
241 assert 'Property 0x00000211: 0x00000000' in read_cap
243 @pytest.mark.buildconfigspec('cmd_tpm_v2')
244 def test_tpm2_pcr_read(u_boot_console):
245 """Execute a TPM2_PCR_Read command.
247 Perform a PCR read of the 0th PCR. Must be zero.
249 if is_sandbox(u_boot_console):
250 tpm2_sandbox_init(u_boot_console)
252 force_init(u_boot_console)
253 ram = u_boot_utils.find_ram_base(u_boot_console)
255 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram)
256 output = u_boot_console.run_command('echo $?')
257 assert output.endswith('0')
259 # Save the number of PCR updates
260 str = re.findall(r'\d+ known updates', read_pcr)[0]
262 updates = int(re.findall(r'\d+', str)[0])
264 # Check the output value
265 assert 'PCR #0 content' in read_pcr
266 assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
268 @pytest.mark.buildconfigspec('cmd_tpm_v2')
269 def test_tpm2_pcr_extend(u_boot_console):
270 """Execute a TPM2_PCR_Extend command.
272 Perform a PCR extension with a known hash in memory (zeroed since the board
273 must have been rebooted).
275 No authentication mechanism is used here, not protecting against packet
278 if is_sandbox(u_boot_console):
279 tpm2_sandbox_init(u_boot_console)
280 force_init(u_boot_console)
281 ram = u_boot_utils.find_ram_base(u_boot_console)
283 u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
284 output = u_boot_console.run_command('echo $?')
285 assert output.endswith('0')
287 # Read the value back into a different place so we can still use 'ram' as
289 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
290 output = u_boot_console.run_command('echo $?')
291 assert output.endswith('0')
292 assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr
293 assert '43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b' in read_pcr
295 str = re.findall(r'\d+ known updates', read_pcr)[0]
296 new_updates = int(re.findall(r'\d+', str)[0])
297 assert (updates + 1) == new_updates
299 u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
300 output = u_boot_console.run_command('echo $?')
301 assert output.endswith('0')
303 read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % (ram + 0x20))
304 output = u_boot_console.run_command('echo $?')
305 assert output.endswith('0')
306 assert '7a 05 01 f5 95 7b df 9c b3 a8 ff 49 66 f0 22 65' in read_pcr
307 assert 'f9 68 65 8b 7a 9c 62 64 2c ba 11 65 e8 66 42 f5' in read_pcr
309 str = re.findall(r'\d+ known updates', read_pcr)[0]
310 new_updates = int(re.findall(r'\d+', str)[0])
311 assert (updates + 2) == new_updates
313 @pytest.mark.buildconfigspec('cmd_tpm_v2')
314 def test_tpm2_cleanup(u_boot_console):
315 """Ensure the TPM is cleared from password or test related configuration."""
317 force_init(u_boot_console, True)