1# Copyright 2018 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from hashlib import sha256 6import logging 7from pprint import pformat 8 9from autotest_lib.client.common_lib import error 10from autotest_lib.client.common_lib.cros import pinweaver_client 11from autotest_lib.server import test 12 13 14def compute_empty_tree_auxilary_hashes(bits_per_level=2, height=6): 15 """Returns a binary string representation of the auxilary digests of an 16 empty path in a Merkle tree with the specified parameters. 17 """ 18 num_siblings = 2 ^ bits_per_level - 1 19 child = b'\0' * 32 20 result = b'' 21 for _ in range(height): 22 part = child * num_siblings 23 child = sha256(part + child).digest() 24 result += part 25 return result 26 27class firmware_Cr50PinWeaverServer(test.test): 28 """Tests the PinWeaver functionality on Cr50 using pinweaver_client through 29 trunksd. 30 """ 31 32 version = 1 33 34 RESULT_CODE_SUCCESS = 'EC_SUCCESS' 35 RESULT_CODE_AUTH_FAILED = 'PW_ERR_LOWENT_AUTH_FAILED' 36 RESULT_CODE_RATE_LIMITED = 'PW_ERR_RATE_LIMIT_REACHED' 37 38 def run_once(self, host): 39 """Runs the firmware_Cr50PinWeaverServer test. 40 This test is made up of the pinweaver_client self test, and a test that 41 checks that PinWeaver works as expected across device reboots. 42 """ 43 44 # Run "pinweaver_client selftest". 45 try: 46 if not pinweaver_client.SelfTest(host): 47 raise error.TestFail('Failed SelfTest: %s' % 48 self.__class__.__name__) 49 except pinweaver_client.PinWeaverNotAvailableError: 50 logging.info('PinWeaver not supported!') 51 raise error.TestNAError('PinWeaver is not available') 52 53 # Check PinWeaver logic across reboots including the reboot counter. 54 # Insert an entry. 55 # 56 # Label 0 is guaranteed to be empty because the self test above resets 57 # the tree and removes the leaf it adds. 58 label = 0 59 hashes = compute_empty_tree_auxilary_hashes() 60 # TODO(mruthven): always use hashes.hex() after python3 migration. 61 h_aux = hashes.hex() if hasattr(hashes, 62 'hex') else hashes.encode('hex') 63 le_secret = sha256(b'1234').hexdigest() 64 he_secret = sha256(b'ag3#l4Z9').hexdigest() 65 reset_secret = sha256(b'[email protected]').hexdigest() 66 delay_schedule = '5 %d' % 0x00ffffffff 67 result = pinweaver_client.InsertLeaf(host, label, h_aux, le_secret, 68 he_secret, reset_secret, 69 delay_schedule) 70 logging.info('Insert: %s', pformat(result)) 71 if (result['result_code']['name'] != 72 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 73 raise error.TestFail('Failed InsertLeaf: %s' % 74 self.__class__.__name__) 75 cred_metadata = result['cred_metadata'] 76 77 # Exhaust the allowed number of attempts. 78 for i in range(6): 79 result = pinweaver_client.TryAuth(host, h_aux, '0' * 64, 80 cred_metadata) 81 if result['cred_metadata']: 82 cred_metadata = result['cred_metadata'] 83 logging.info('TryAuth: %s', pformat(result)) 84 if ((i <= 4 and result['result_code']['name'] != 85 firmware_Cr50PinWeaverServer.RESULT_CODE_AUTH_FAILED) or 86 (i > 4 and result['result_code']['name'] != 87 firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED)): 88 raise error.TestFail('Failed TryAuth: %s' % 89 self.__class__.__name__) 90 91 if result['seconds_to_wait'] == 0: 92 raise error.TestFail('Failed TryAuth: %s' % 93 self.__class__.__name__) 94 95 # Reboot the device. This calls TPM_startup() which reloads the Merkle 96 # tree from NVRAM. Note that this doesn't reset the timer on Cr50, so 97 # restart_count doesn't increment. 98 host.reboot() 99 100 # Verify that the lockout is still enforced. 101 result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata) 102 logging.info('TryAuth: %s', pformat(result)) 103 if (result['result_code']['name'] != 104 firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED): 105 raise error.TestFail('Failed TryAuth: %s' % 106 self.__class__.__name__) 107 if result['seconds_to_wait'] == 0: 108 raise error.TestFail('Failed TryAuth: %s' % 109 self.__class__.__name__) 110 111 # Perform a reset. 112 result = pinweaver_client.ResetAuth(host, h_aux, reset_secret, 113 cred_metadata) 114 if (result['result_code']['name'] != 115 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 116 raise error.TestFail('Failed ResetAuth: %s' % 117 self.__class__.__name__) 118 cred_metadata = result['cred_metadata'] 119 logging.info('ResetAuth: %s', pformat(result)) 120 121 # Verify that using a PIN would work. 122 result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata) 123 mac = result['mac'] 124 logging.info('TryAuth: %s', pformat(result)) 125 if (result['result_code']['name'] != 126 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 127 raise error.TestFail('Failed TryAuth: %s' % 128 self.__class__.__name__) 129 130 # Remove the leaf. 131 result = pinweaver_client.RemoveLeaf(host, label, h_aux, mac) 132 logging.info('RemoveLeaf: %s', pformat(result)) 133 if (result['result_code']['name'] != 134 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 135 raise error.TestFail('Failed RemoveLeaf: %s' % 136 self.__class__.__name__) 137