• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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