12dffe1c6SMiquel Raynal# SPDX-License-Identifier: GPL-2.0+ 22dffe1c6SMiquel Raynal# Copyright (c) 2018, Bootlin 32dffe1c6SMiquel Raynal# Author: Miquel Raynal <miquel.raynal@bootlin.com> 42dffe1c6SMiquel Raynal 52dffe1c6SMiquel Raynalimport os.path 62dffe1c6SMiquel Raynalimport pytest 72dffe1c6SMiquel Raynalimport u_boot_utils 82dffe1c6SMiquel Raynalimport re 92dffe1c6SMiquel Raynalimport time 102dffe1c6SMiquel Raynal 112dffe1c6SMiquel Raynal""" 122dffe1c6SMiquel RaynalTest the TPMv2.x related commands. You must have a working hardware setup in 132dffe1c6SMiquel Raynalorder to do these tests. 142dffe1c6SMiquel Raynal 152dffe1c6SMiquel RaynalNotes: 162dffe1c6SMiquel Raynal* These tests will prove the password mechanism. The TPM chip must be cleared of 172dffe1c6SMiquel Raynalany password. 182dffe1c6SMiquel Raynal* Commands like pcr_setauthpolicy and pcr_resetauthpolicy are not implemented 192dffe1c6SMiquel Raynalhere because they would fail the tests in most cases (TPMs do not implement them 202dffe1c6SMiquel Raynaland return an error). 212dffe1c6SMiquel Raynal""" 222dffe1c6SMiquel Raynal 232dffe1c6SMiquel Raynalupdates = 0 242dffe1c6SMiquel Raynal 252dffe1c6SMiquel Raynaldef force_init(u_boot_console, force=False): 262dffe1c6SMiquel Raynal """When a test fails, U-Boot is reset. Because TPM stack must be initialized 272dffe1c6SMiquel Raynal after each reboot, we must ensure these lines are always executed before 282dffe1c6SMiquel Raynal trying any command or they will fail with no reason. Executing 'tpm init' 292dffe1c6SMiquel Raynal twice will spawn an error used to detect that the TPM was not reset and no 302dffe1c6SMiquel Raynal initialization code should be run. 312dffe1c6SMiquel Raynal """ 32*d87434a2SMiquel Raynal output = u_boot_console.run_command('tpm2 init') 332dffe1c6SMiquel Raynal if force or not 'Error' in output: 342dffe1c6SMiquel Raynal u_boot_console.run_command('echo --- start of init ---') 35*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR') 36*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 self_test full') 37*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT') 382dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 392dffe1c6SMiquel Raynal if not output.endswith('0'): 40*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM') 412dffe1c6SMiquel Raynal u_boot_console.run_command('echo --- end of init ---') 422dffe1c6SMiquel Raynal 432dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 442dffe1c6SMiquel Raynaldef test_tpm2_init(u_boot_console): 452dffe1c6SMiquel Raynal """Init the software stack to use TPMv2 commands.""" 462dffe1c6SMiquel Raynal 47*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 init') 482dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 492dffe1c6SMiquel Raynal assert output.endswith('0') 502dffe1c6SMiquel Raynal 512dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 522dffe1c6SMiquel Raynaldef test_tpm2_startup(u_boot_console): 532dffe1c6SMiquel Raynal """Execute a TPM2_Startup command. 542dffe1c6SMiquel Raynal 552dffe1c6SMiquel Raynal Initiate the TPM internal state machine. 562dffe1c6SMiquel Raynal """ 572dffe1c6SMiquel Raynal 58*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR') 592dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 602dffe1c6SMiquel Raynal assert output.endswith('0') 612dffe1c6SMiquel Raynal 622dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 632dffe1c6SMiquel Raynaldef test_tpm2_self_test_full(u_boot_console): 642dffe1c6SMiquel Raynal """Execute a TPM2_SelfTest (full) command. 652dffe1c6SMiquel Raynal 662dffe1c6SMiquel Raynal Ask the TPM to perform all self tests to also enable full capabilities. 672dffe1c6SMiquel Raynal """ 682dffe1c6SMiquel Raynal 69*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 self_test full') 702dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 712dffe1c6SMiquel Raynal assert output.endswith('0') 722dffe1c6SMiquel Raynal 732dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 742dffe1c6SMiquel Raynaldef test_tpm2_continue_self_test(u_boot_console): 752dffe1c6SMiquel Raynal """Execute a TPM2_SelfTest (continued) command. 762dffe1c6SMiquel Raynal 772dffe1c6SMiquel Raynal Ask the TPM to finish its self tests (alternative to the full test) in order 782dffe1c6SMiquel Raynal to enter a fully operational state. 792dffe1c6SMiquel Raynal """ 802dffe1c6SMiquel Raynal 81*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 self_test continue') 822dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 832dffe1c6SMiquel Raynal assert output.endswith('0') 842dffe1c6SMiquel Raynal 852dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 862dffe1c6SMiquel Raynaldef test_tpm2_clear(u_boot_console): 872dffe1c6SMiquel Raynal """Execute a TPM2_Clear command. 882dffe1c6SMiquel Raynal 892dffe1c6SMiquel Raynal Ask the TPM to reset entirely its internal state (including internal 902dffe1c6SMiquel Raynal configuration, passwords, counters and DAM parameters). This is half of the 912dffe1c6SMiquel Raynal TAKE_OWNERSHIP command from TPMv1. 922dffe1c6SMiquel Raynal 932dffe1c6SMiquel Raynal Use the LOCKOUT hierarchy for this. The LOCKOUT/PLATFORM hierarchies must 942dffe1c6SMiquel Raynal not have a password set, otherwise this test will fail. ENDORSEMENT and 952dffe1c6SMiquel Raynal PLATFORM hierarchies are also available. 962dffe1c6SMiquel Raynal """ 972dffe1c6SMiquel Raynal 98*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT') 992dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 1002dffe1c6SMiquel Raynal assert output.endswith('0') 1012dffe1c6SMiquel Raynal 102*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM') 1032dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 1042dffe1c6SMiquel Raynal assert output.endswith('0') 1052dffe1c6SMiquel Raynal 1062dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 1072dffe1c6SMiquel Raynaldef test_tpm2_change_auth(u_boot_console): 1082dffe1c6SMiquel Raynal """Execute a TPM2_HierarchyChangeAuth command. 1092dffe1c6SMiquel Raynal 1102dffe1c6SMiquel Raynal Ask the TPM to change the owner, ie. set a new password: 'unicorn' 1112dffe1c6SMiquel Raynal 1122dffe1c6SMiquel Raynal Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are 1132dffe1c6SMiquel Raynal also available. 1142dffe1c6SMiquel Raynal """ 1152dffe1c6SMiquel Raynal 1162dffe1c6SMiquel Raynal force_init(u_boot_console) 1172dffe1c6SMiquel Raynal 118*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn') 1192dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 1202dffe1c6SMiquel Raynal assert output.endswith('0') 1212dffe1c6SMiquel Raynal 122*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn') 1232dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 124*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM') 1252dffe1c6SMiquel Raynal assert output.endswith('0') 1262dffe1c6SMiquel Raynal 1272dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 1282dffe1c6SMiquel Raynaldef test_tpm2_get_capability(u_boot_console): 1292dffe1c6SMiquel Raynal """Execute a TPM_GetCapability command. 1302dffe1c6SMiquel Raynal 1312dffe1c6SMiquel Raynal Display one capability. In our test case, let's display the default DAM 1322dffe1c6SMiquel Raynal lockout counter that should be 0 since the CLEAR: 1332dffe1c6SMiquel Raynal - TPM_CAP_TPM_PROPERTIES = 0x6 1342dffe1c6SMiquel Raynal - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14 1352dffe1c6SMiquel Raynal 1362dffe1c6SMiquel Raynal There is no expected default values because it would depend on the chip 1372dffe1c6SMiquel Raynal used. We can still save them in order to check they have changed later. 1382dffe1c6SMiquel Raynal """ 1392dffe1c6SMiquel Raynal 1402dffe1c6SMiquel Raynal force_init(u_boot_console) 1412dffe1c6SMiquel Raynal ram = u_boot_utils.find_ram_base(u_boot_console) 1422dffe1c6SMiquel Raynal 143*d87434a2SMiquel Raynal read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram) 1442dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 1452dffe1c6SMiquel Raynal assert output.endswith('0') 1462dffe1c6SMiquel Raynal assert 'Property 0x0000020e: 0x00000000' in read_cap 1472dffe1c6SMiquel Raynal 1482dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 1492dffe1c6SMiquel Raynaldef test_tpm2_dam_parameters(u_boot_console): 1502dffe1c6SMiquel Raynal """Execute a TPM2_DictionaryAttackParameters command. 1512dffe1c6SMiquel Raynal 1522dffe1c6SMiquel Raynal Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change: 1532dffe1c6SMiquel Raynal - Max number of failed authentication before lockout: 3 1542dffe1c6SMiquel Raynal - Time before the failure counter is automatically decremented: 10 sec 1552dffe1c6SMiquel Raynal - Time after a lockout failure before it can be attempted again: 0 sec 1562dffe1c6SMiquel Raynal 1572dffe1c6SMiquel Raynal For an unknown reason, the DAM parameters must be changed before changing 1582dffe1c6SMiquel Raynal the authentication, otherwise the lockout will be engaged after the first 1592dffe1c6SMiquel Raynal failed authentication attempt. 1602dffe1c6SMiquel Raynal """ 1612dffe1c6SMiquel Raynal 1622dffe1c6SMiquel Raynal force_init(u_boot_console) 1632dffe1c6SMiquel Raynal ram = u_boot_utils.find_ram_base(u_boot_console) 1642dffe1c6SMiquel Raynal 1652dffe1c6SMiquel Raynal # Set the DAM parameters to known values 166*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 dam_parameters 3 10 0') 1672dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 1682dffe1c6SMiquel Raynal assert output.endswith('0') 1692dffe1c6SMiquel Raynal 1702dffe1c6SMiquel Raynal # Check the values have been saved 171*d87434a2SMiquel Raynal read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram) 1722dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 1732dffe1c6SMiquel Raynal assert output.endswith('0') 1742dffe1c6SMiquel Raynal assert 'Property 0x0000020f: 0x00000003' in read_cap 1752dffe1c6SMiquel Raynal assert 'Property 0x00000210: 0x0000000a' in read_cap 1762dffe1c6SMiquel Raynal assert 'Property 0x00000211: 0x00000000' in read_cap 1772dffe1c6SMiquel Raynal 1782dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 1792dffe1c6SMiquel Raynaldef test_tpm2_pcr_read(u_boot_console): 1802dffe1c6SMiquel Raynal """Execute a TPM2_PCR_Read command. 1812dffe1c6SMiquel Raynal 1822dffe1c6SMiquel Raynal Perform a PCR read of the 0th PCR. Must be zero. 1832dffe1c6SMiquel Raynal """ 1842dffe1c6SMiquel Raynal 1852dffe1c6SMiquel Raynal force_init(u_boot_console) 186f4eef40bSQuentin Schulz ram = u_boot_utils.find_ram_base(u_boot_console) 1872dffe1c6SMiquel Raynal 188*d87434a2SMiquel Raynal read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram) 1892dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 1902dffe1c6SMiquel Raynal assert output.endswith('0') 1912dffe1c6SMiquel Raynal 1922dffe1c6SMiquel Raynal # Save the number of PCR updates 1932dffe1c6SMiquel Raynal str = re.findall(r'\d+ known updates', read_pcr)[0] 1942dffe1c6SMiquel Raynal global updates 1952dffe1c6SMiquel Raynal updates = int(re.findall(r'\d+', str)[0]) 1962dffe1c6SMiquel Raynal 1972dffe1c6SMiquel Raynal # Check the output value 1982dffe1c6SMiquel Raynal assert 'PCR #0 content' in read_pcr 1992dffe1c6SMiquel Raynal assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr 2002dffe1c6SMiquel Raynal 2012dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 2022dffe1c6SMiquel Raynaldef test_tpm2_pcr_extend(u_boot_console): 2032dffe1c6SMiquel Raynal """Execute a TPM2_PCR_Extend command. 2042dffe1c6SMiquel Raynal 2052dffe1c6SMiquel Raynal Perform a PCR extension with a known hash in memory (zeroed since the board 2062dffe1c6SMiquel Raynal must have been rebooted). 2072dffe1c6SMiquel Raynal 2082dffe1c6SMiquel Raynal No authentication mechanism is used here, not protecting against packet 2092dffe1c6SMiquel Raynal replay, yet. 2102dffe1c6SMiquel Raynal """ 2112dffe1c6SMiquel Raynal 2122dffe1c6SMiquel Raynal force_init(u_boot_console) 213f4eef40bSQuentin Schulz ram = u_boot_utils.find_ram_base(u_boot_console) 2142dffe1c6SMiquel Raynal 215*d87434a2SMiquel Raynal u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram) 2162dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 2172dffe1c6SMiquel Raynal assert output.endswith('0') 2182dffe1c6SMiquel Raynal 219*d87434a2SMiquel Raynal read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram) 2202dffe1c6SMiquel Raynal output = u_boot_console.run_command('echo $?') 2212dffe1c6SMiquel Raynal assert output.endswith('0') 2222dffe1c6SMiquel Raynal assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr 2232dffe1c6SMiquel Raynal assert '43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b' in read_pcr 2242dffe1c6SMiquel Raynal 2252dffe1c6SMiquel Raynal str = re.findall(r'\d+ known updates', read_pcr)[0] 2262dffe1c6SMiquel Raynal new_updates = int(re.findall(r'\d+', str)[0]) 2272dffe1c6SMiquel Raynal assert (updates + 1) == new_updates 2282dffe1c6SMiquel Raynal 2292dffe1c6SMiquel Raynal@pytest.mark.buildconfigspec('cmd_tpm_v2') 2302dffe1c6SMiquel Raynaldef test_tpm2_cleanup(u_boot_console): 2312dffe1c6SMiquel Raynal """Ensure the TPM is cleared from password or test related configuration.""" 2322dffe1c6SMiquel Raynal 2332dffe1c6SMiquel Raynal force_init(u_boot_console, True) 234