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 = '\0' * 32 20 result = '' 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 h_aux = compute_empty_tree_auxilary_hashes().encode('hex') 60 le_secret = sha256('1234').hexdigest() 61 he_secret = sha256('ag3#l4Z9').hexdigest() 62 reset_secret = sha256('W8oE@Ja2mq.R1').hexdigest() 63 delay_schedule = '5 %d' % 0x00ffffffff 64 result = pinweaver_client.InsertLeaf(host, label, h_aux, le_secret, 65 he_secret, reset_secret, 66 delay_schedule) 67 logging.info('Insert: %s', pformat(result)) 68 if (result['result_code']['name'] != 69 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 70 raise error.TestFail('Failed InsertLeaf: %s' % 71 self.__class__.__name__) 72 cred_metadata = result['cred_metadata'] 73 74 # Exhaust the allowed number of attempts. 75 for i in range(6): 76 result = pinweaver_client.TryAuth(host, h_aux, '0' * 64, 77 cred_metadata) 78 if result['cred_metadata']: 79 cred_metadata = result['cred_metadata'] 80 logging.info('TryAuth: %s', pformat(result)) 81 if ((i <= 4 and result['result_code']['name'] != 82 firmware_Cr50PinWeaverServer.RESULT_CODE_AUTH_FAILED) or 83 (i > 4 and result['result_code']['name'] != 84 firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED)): 85 raise error.TestFail('Failed TryAuth: %s' % 86 self.__class__.__name__) 87 88 if result['seconds_to_wait'] == 0: 89 raise error.TestFail('Failed TryAuth: %s' % 90 self.__class__.__name__) 91 92 # Reboot the device. This calls TPM_startup() which reloads the Merkle 93 # tree from NVRAM. Note that this doesn't reset the timer on Cr50, so 94 # restart_count doesn't increment. 95 host.reboot() 96 97 # Verify that the lockout is still enforced. 98 result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata) 99 logging.info('TryAuth: %s', pformat(result)) 100 if (result['result_code']['name'] != 101 firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED): 102 raise error.TestFail('Failed TryAuth: %s' % 103 self.__class__.__name__) 104 if result['seconds_to_wait'] == 0: 105 raise error.TestFail('Failed TryAuth: %s' % 106 self.__class__.__name__) 107 108 # Perform a reset. 109 result = pinweaver_client.ResetAuth(host, h_aux, reset_secret, 110 cred_metadata) 111 if (result['result_code']['name'] != 112 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 113 raise error.TestFail('Failed ResetAuth: %s' % 114 self.__class__.__name__) 115 cred_metadata = result['cred_metadata'] 116 logging.info('ResetAuth: %s', pformat(result)) 117 118 # Verify that using a PIN would work. 119 result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata) 120 mac = result['mac'] 121 logging.info('TryAuth: %s', pformat(result)) 122 if (result['result_code']['name'] != 123 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 124 raise error.TestFail('Failed TryAuth: %s' % 125 self.__class__.__name__) 126 127 # Remove the leaf. 128 result = pinweaver_client.RemoveLeaf(host, label, h_aux, mac) 129 logging.info('RemoveLeaf: %s', pformat(result)) 130 if (result['result_code']['name'] != 131 firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS): 132 raise error.TestFail('Failed RemoveLeaf: %s' % 133 self.__class__.__name__) 134