autotest: use attestation and tpm_manager client BUG=b:175510138 TEST=test_that $DUT platform_* Cq-Depend: chromium:2603198 Change-Id: I34e9bb5e8a6cfa54e710555e5966a53799670b2d Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2589435 Commit-Queue: Yi Chou <yich@google.com> Tested-by: Yi Chou <yich@google.com> Reviewed-by: Andrey Pronin <apronin@chromium.org> Reviewed-by: Leo Lai <cylai@google.com>
diff --git a/client/bin/site_sysinfo.py b/client/bin/site_sysinfo.py index 94454f0..2cbee2b 100755 --- a/client/bin/site_sysinfo.py +++ b/client/bin/site_sysinfo.py
@@ -442,8 +442,9 @@ from autotest_lib.client.cros import cryptohome # Get the dictionary attack counter. keyval["TPM_DICTIONARY_ATTACK_COUNTER"] = ( - cryptohome.get_tpm_more_status().get( - 'dictionary_attack_counter', 'Failed to query cryptohome')) + cryptohome.get_tpm_da_info().get( + 'dictionary_attack_counter', + 'Failed to query tpm_manager')) # Return the updated keyvals. return keyval
diff --git a/client/cros/cryptohome.py b/client/cros/cryptohome.py index 904046d..61f6698 100644 --- a/client/cros/cryptohome.py +++ b/client/cros/cryptohome.py
@@ -18,7 +18,9 @@ from autotest_lib.client.common_lib import error from autotest_lib.client.cros.cros_disks import DBusClient +ATTESTATION_CMD = '/usr/bin/attestation_client' CRYPTOHOME_CMD = '/usr/sbin/cryptohome' +TPM_MANAGER_CMD = '/usr/bin/tpm_manager_client' GUEST_USER_NAME = '$guest' UNAVAILABLE_ACTION = 'Unknown action or no action given.' MOUNT_RETRY_COUNT = 20 @@ -88,63 +90,65 @@ A TPM status dictionary, for example: { 'Enabled': True, 'Owned': True, - 'Being Owned': False, - 'Ready': True, - 'Password': '' + 'Ready': True } """ - out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_status') + out = __run_cmd(TPM_MANAGER_CMD + ' status --nonsensitive') status = {} - for field in ['Enabled', 'Owned', 'Being Owned', 'Ready']: - match = re.search('TPM %s: (true|false)' % field, out) + for field in ['is_enabled', 'is_owned']: + match = re.search('%s: (true|false)' % field, out) if not match: raise ChromiumOSError('Invalid TPM status: "%s".' % out) status[field] = match.group(1) == 'true' - match = re.search('TPM Password: (\w*)', out) - status['Password'] = '' - if match: - status['Password'] = match.group(1) + status['Enabled'] = status['is_enabled'] + status['Owned'] = status['is_owned'] + status['Ready'] = status['is_enabled'] and status['is_owned'] return status -def get_tpm_more_status(): - """Get more of the TPM status. +def get_tpm_password(): + """Get the TPM password. Returns: - A TPM more status dictionary, for example: - { 'dictionary_attack_lockout_in_effect': False, - 'attestation_prepared': False, - 'boot_lockbox_finalized': False, - 'enabled': True, - 'owned': True, - 'owner_password': '' + A TPM password + """ + out = __run_cmd(TPM_MANAGER_CMD + ' status') + match = re.search('owner_password: (\w*)', out) + password = '' + if match: + hex_pass = match.group(1).decode("hex") + password = ''.join( + chr(int(hex_pass[i:i + 2], 16)) + for i in range(0, len(hex_pass), 2)) + return password + + +def get_tpm_da_info(): + """Get the TPM dictionary attack information. + Returns: + A TPM dictionary attack status dictionary, for example: + { 'dictionary_attack_counter': 0, - 'dictionary_attack_lockout_seconds_remaining': 0, - 'dictionary_attack_threshold': 10, - 'attestation_enrolled': False, - 'initialized': True, - 'verified_boot_measured': False, - 'install_lockbox_finalized': True + 'dictionary_attack_threshold': 200, + 'dictionary_attack_lockout_in_effect': False, + 'dictionary_attack_lockout_seconds_remaining': 0 } - An empty dictionary is returned if the command is not supported. """ status = {} - out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_more_status | grep :') - if out.startswith(UNAVAILABLE_ACTION): - # --action=tpm_more_status only exists >= 41. - logging.info('Method not supported!') - return status - for line in out.splitlines(): + out = __run_cmd(TPM_MANAGER_CMD + ' get_da_info') + for line in out.splitlines()[1:-1]: items = line.strip().split(':') + if len(items) != 2: + continue if items[1].strip() == 'false': value = False elif items[1].strip() == 'true': value = True - elif items[1].strip().isdigit(): - value = int(items[1].strip()) + elif items[1].split('(')[0].strip().isdigit(): + value = int(items[1].split('(')[0].strip()) else: value = items[1].strip(' "') - status[items[0]] = value + status[items[0].strip()] = value return status @@ -210,7 +214,7 @@ def is_tpm_lockout_in_effect(): """Returns true if the TPM lockout is in effect; false otherwise.""" - status = get_tpm_more_status() + status = get_tpm_da_info() return status.get('dictionary_attack_lockout_in_effect', None) @@ -233,13 +237,28 @@ return status +def get_install_attribute_status(): + """Query the install attribute status + + Returns: + A status string, which could be: + "UNKNOWN" + "TPM_NOT_OWNED" + "FIRST_INSTALL" + "VALID" + "INVALID" + """ + out = __run_cmd(CRYPTOHOME_CMD + ' --action=install_attributes_get_status') + return out.strip() + + def get_tpm_attestation_status(): """Get the TPM attestation status. Works similar to get_tpm_status(). """ - out = __run_cmd(CRYPTOHOME_CMD + ' --action=tpm_attestation_status') + out = __run_cmd(ATTESTATION_CMD + ' status') status = {} - for field in ['Prepared', 'Enrolled']: - match = re.search('Attestation %s: (true|false)' % field, out) + for field in ['prepared_for_enrollment', 'enrolled']: + match = re.search('%s: (true|false)' % field, out) if not match: raise ChromiumOSError('Invalid attestation status: "%s".' % out) status[field] = match.group(1) == 'true' @@ -318,7 +337,7 @@ mounted = False while retry < MOUNT_RETRY_COUNT and not mounted: time.sleep(1) - logging.info("Retry " + str(retry + 1)) + logging.info("Retry %s", str(retry + 1)) __run_cmd(' '.join(args)) # TODO: Remove this additional call to get_user_hash(user) when # crbug.com/690994 is fixed @@ -343,6 +362,7 @@ def test_auth(user, password): + """Test key auth.""" cmd = [CRYPTOHOME_CMD, '--action=check_key_ex', '--user=%s' % user, '--password=%s' % password, '--async'] out = __run_cmd(' '.join(cmd)) @@ -351,6 +371,7 @@ def add_le_key(user, password, new_password, new_key_label): + """Add low entropy key.""" args = [CRYPTOHOME_CMD, '--action=add_key_ex', '--key_policy=le', '--user=%s' % user, '--password=%s' % password, '--new_key_label=%s' % new_key_label, @@ -359,6 +380,7 @@ def remove_key(user, password, remove_key_label): + """Remove a key.""" args = [CRYPTOHOME_CMD, '--action=remove_key_ex', '--user=%s' % user, '--password=%s' % password, '--remove_key_label=%s' % remove_key_label] @@ -366,6 +388,7 @@ def get_supported_key_policies(): + """Get supported key policies.""" args = [CRYPTOHOME_CMD, '--action=get_supported_key_policies'] out = __run_cmd(' '.join(args)) logging.info(out) @@ -415,7 +438,7 @@ return ns_mount_line.split() try: - logging.debug('Active cryptohome mounts:\n' + + logging.debug('Active cryptohome mounts:\n%s', utils.system_output('cat %s' % cryptohomed_path)) mount_line = utils.system_output( 'grep %s %s' % (mount_point, cryptohomed_path), @@ -529,7 +552,7 @@ http://mail.google.com/support/bin/answer.py?hl=en&ctx=mail&answer=10313 """ if not credential: - return None + return None parts = credential.split('@') if len(parts) != 2: @@ -543,6 +566,7 @@ def crash_cryptohomed(): + """Let cryptohome crash.""" # Try to kill cryptohomed so we get something to work with. pid = __run_cmd('pgrep cryptohomed') try: @@ -616,6 +640,7 @@ def change_password(user, password, new_password): + """Change user password.""" args = [ CRYPTOHOME_CMD, '--action=migrate_key_ex', @@ -682,19 +707,19 @@ def __wait_for_specific_signal(self, signal, data): - """Wait for the |signal| with matching |data| - Returns the resulting dict on success or {} on error. - """ - # Do not bubble up the timeout here, just return {}. - result = {} - try: - result = self.wait_for_signal(signal) - except utils.TimeoutError: - return {} - for k in data.keys(): - if k not in result or result[k] != data[k]: + """Wait for the |signal| with matching |data| + Returns the resulting dict on success or {} on error. + """ + # Do not bubble up the timeout here, just return {}. + result = {} + try: + result = self.wait_for_signal(signal) + except utils.TimeoutError: return {} - return result + for k in data.keys(): + if k not in result or result[k] != data[k]: + return {} + return result # Perform a data-less async call.
diff --git a/client/site_tests/desktopui_CheckRlzPingSent/desktopui_CheckRlzPingSent.py b/client/site_tests/desktopui_CheckRlzPingSent/desktopui_CheckRlzPingSent.py index 598ff87..18fef0a 100644 --- a/client/site_tests/desktopui_CheckRlzPingSent/desktopui_CheckRlzPingSent.py +++ b/client/site_tests/desktopui_CheckRlzPingSent/desktopui_CheckRlzPingSent.py
@@ -88,8 +88,8 @@ def _wait_for_rlz_lock(self): """Waits for the DUT to get into locked state after login.""" def get_install_lockbox_finalized_status(): - status = cryptohome.get_tpm_more_status() - return status.get('install_lockbox_finalized') + status = cryptohome.get_install_attribute_status() + return status == 'VALID' try: utils.poll_for_condition(
diff --git a/client/site_tests/firmware_Cr50VirtualNVRam/firmware_Cr50VirtualNVRam.py b/client/site_tests/firmware_Cr50VirtualNVRam/firmware_Cr50VirtualNVRam.py index 9e2775e..3565cd8 100644 --- a/client/site_tests/firmware_Cr50VirtualNVRam/firmware_Cr50VirtualNVRam.py +++ b/client/site_tests/firmware_Cr50VirtualNVRam/firmware_Cr50VirtualNVRam.py
@@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import logging, re +import re from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error from autotest_lib.client.cros import service_stopper @@ -37,6 +37,7 @@ return out def expect_tpmc_error(subcommand, expected_error): + """Expect a tpmc error.""" check_tpmc(subcommand, '.*failed.*%s.*' % expected_error) class firmware_Cr50VirtualNVRam(test.test): @@ -49,7 +50,7 @@ global tpm_pw_hex cryptohome.take_tpm_ownership(wait_for_ownership=True) - tpm_owner_password = cryptohome.get_tpm_status()['Password'] + tpm_owner_password = cryptohome.get_tpm_password() if not tpm_owner_password: raise error.TestError('TPM owner password is empty after ' 'taking ownership.') @@ -275,6 +276,7 @@ '0x12f'); # TPM_RC_AUTH_UNAVAILABLE def initialize(self): + """Initialize the test.""" self.__take_tpm_ownership() # Stop services that access to the TPM, to be able to use tpmc. # Note: for TPM2 the order of re-starting services (they are started @@ -306,4 +308,5 @@ self.__read_tests() def cleanup(self): + """Cleanup the test.""" self._services.restore_services()
diff --git a/client/site_tests/platform_CryptohomeTpmLiveTest/platform_CryptohomeTpmLiveTest.py b/client/site_tests/platform_CryptohomeTpmLiveTest/platform_CryptohomeTpmLiveTest.py index 5d9b409..232e2c5 100644 --- a/client/site_tests/platform_CryptohomeTpmLiveTest/platform_CryptohomeTpmLiveTest.py +++ b/client/site_tests/platform_CryptohomeTpmLiveTest/platform_CryptohomeTpmLiveTest.py
@@ -13,9 +13,10 @@ version = 1 def run_once(self): + """Run TPM live tests.""" cryptohome.take_tpm_ownership(wait_for_ownership=True) - tpm_owner_password = cryptohome.get_tpm_status()['Password'] + tpm_owner_password = cryptohome.get_tpm_password() if not tpm_owner_password: raise error.TestError('TPM owner password is empty after taking ' 'ownership.')
diff --git a/client/site_tests/platform_InitLoginPerf/platform_InitLoginPerf.py b/client/site_tests/platform_InitLoginPerf/platform_InitLoginPerf.py index 62065f0..26baec2 100644 --- a/client/site_tests/platform_InitLoginPerf/platform_InitLoginPerf.py +++ b/client/site_tests/platform_InitLoginPerf/platform_InitLoginPerf.py
@@ -23,7 +23,9 @@ @return: Attestation readiness status - True/False. """ - return cryptohome.get_tpm_more_status().get('attestation_prepared', False) + return cryptohome.get_tpm_attestation_status().get( + 'prepared_for_enrollment', False) + def get_bootstat_timestamp(name, occurrence): """Gets the timestamp in ms of the given timestamp name and occurrence @@ -177,7 +179,7 @@ expected_value=True, timeout_sec=timeout): logging.debug('tpm_more_status: %r', - cryptohome.get_tpm_more_status()) + cryptohome.get_tpm_attestation_status()) raise error.TestFail('Timeout waiting for attestation_prepared') def get_init_durations(self):