Home | History | Annotate | Download | only in firmware_Cr50PinWeaverServer
      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 
      5 from hashlib import sha256
      6 import logging
      7 from pprint import pformat
      8 
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.client.common_lib.cros import pinweaver_client
     11 from autotest_lib.server import test
     12 
     13 
     14 def 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 
     27 class 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 (at] 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