| # Copyright 2018 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from hashlib import sha256 |
| import logging |
| from pprint import pformat |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib.cros import pinweaver_client |
| from autotest_lib.server import test |
| |
| |
| def compute_empty_tree_auxilary_hashes(bits_per_level=2, height=6): |
| """Returns a binary string representation of the auxilary digests of an |
| empty path in a Merkle tree with the specified parameters. |
| """ |
| num_siblings = 2 ^ bits_per_level - 1 |
| child = '\0' * 32 |
| result = '' |
| for _ in range(height): |
| part = child * num_siblings |
| child = sha256(part + child).digest() |
| result += part |
| return result |
| |
| class firmware_Cr50PinWeaverServer(test.test): |
| """Tests the PinWeaver functionality on Cr50 using pinweaver_client through |
| trunksd. |
| """ |
| |
| version = 1 |
| |
| RESULT_CODE_SUCCESS = 'EC_SUCCESS' |
| RESULT_CODE_AUTH_FAILED = 'PW_ERR_LOWENT_AUTH_FAILED' |
| RESULT_CODE_RATE_LIMITED = 'PW_ERR_RATE_LIMIT_REACHED' |
| |
| def run_once(self, host): |
| """Runs the firmware_Cr50PinWeaverServer test. |
| This test is made up of the pinweaver_client self test, and a test that |
| checks that PinWeaver works as expected across device reboots. |
| """ |
| |
| # Run "pinweaver_client selftest". |
| try: |
| if not pinweaver_client.SelfTest(host): |
| raise error.TestFail('Failed SelfTest: %s' % |
| self.__class__.__name__) |
| except pinweaver_client.PinWeaverNotAvailableError: |
| logging.info('PinWeaver not supported!') |
| raise error.TestNAError('PinWeaver is not available') |
| |
| # Check PinWeaver logic across reboots including the reboot counter. |
| # Insert an entry. |
| # |
| # Label 0 is guaranteed to be empty because the self test above resets |
| # the tree and removes the leaf it adds. |
| label = 0 |
| h_aux = compute_empty_tree_auxilary_hashes().encode('hex') |
| le_secret = sha256('1234').hexdigest() |
| he_secret = sha256('ag3#l4Z9').hexdigest() |
| reset_secret = sha256('[email protected]').hexdigest() |
| delay_schedule = '5 %d' % 0x00ffffffff |
| result = pinweaver_client.InsertLeaf(host, label, h_aux, le_secret, |
| he_secret, reset_secret, |
| delay_schedule) |
| logging.info('Insert: %s', pformat(result)) |
| if (result['result_code']['name'] != |
| firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): |
| raise error.TestFail('Failed InsertLeaf: %s' % |
| self.__class__.__name__) |
| cred_metadata = result['cred_metadata'] |
| |
| # Exhaust the allowed number of attempts. |
| for i in range(6): |
| result = pinweaver_client.TryAuth(host, h_aux, '0' * 64, |
| cred_metadata) |
| if result['cred_metadata']: |
| cred_metadata = result['cred_metadata'] |
| logging.info('TryAuth: %s', pformat(result)) |
| if ((i <= 4 and result['result_code']['name'] != |
| firmware_Cr50PinWeaverServer.RESULT_CODE_AUTH_FAILED) or |
| (i > 4 and result['result_code']['name'] != |
| firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED)): |
| raise error.TestFail('Failed TryAuth: %s' % |
| self.__class__.__name__) |
| |
| if result['seconds_to_wait'] == 0: |
| raise error.TestFail('Failed TryAuth: %s' % |
| self.__class__.__name__) |
| |
| # Reboot the device. This calls TPM_startup() which reloads the Merkle |
| # tree from NVRAM. Note that this doesn't reset the timer on Cr50, so |
| # restart_count doesn't increment. |
| host.reboot() |
| |
| # Verify that the lockout is still enforced. |
| result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata) |
| logging.info('TryAuth: %s', pformat(result)) |
| if (result['result_code']['name'] != |
| firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED): |
| raise error.TestFail('Failed TryAuth: %s' % |
| self.__class__.__name__) |
| if result['seconds_to_wait'] == 0: |
| raise error.TestFail('Failed TryAuth: %s' % |
| self.__class__.__name__) |
| |
| # Perform a reset. |
| result = pinweaver_client.ResetAuth(host, h_aux, reset_secret, |
| cred_metadata) |
| if (result['result_code']['name'] != |
| firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): |
| raise error.TestFail('Failed ResetAuth: %s' % |
| self.__class__.__name__) |
| cred_metadata = result['cred_metadata'] |
| logging.info('ResetAuth: %s', pformat(result)) |
| |
| # Verify that using a PIN would work. |
| result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata) |
| mac = result['mac'] |
| logging.info('TryAuth: %s', pformat(result)) |
| if (result['result_code']['name'] != |
| firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): |
| raise error.TestFail('Failed TryAuth: %s' % |
| self.__class__.__name__) |
| |
| # Remove the leaf. |
| result = pinweaver_client.RemoveLeaf(host, label, h_aux, mac) |
| logging.info('RemoveLeaf: %s', pformat(result)) |
| if (result['result_code']['name'] != |
| firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): |
| raise error.TestFail('Failed RemoveLeaf: %s' % |
| self.__class__.__name__) |