Home | History | Annotate | Download | only in firmware_Cr50VirtualNVRam
      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 import logging, re
      6 from autotest_lib.client.bin import test, utils
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.cros import service_stopper
      9 from autotest_lib.client.cros import cryptohome
     10 
     11 tpm_owner_password = ''
     12 tpm_pw_hex = ''
     13 
     14 def run_tpmc_cmd(subcommand):
     15     """Make this test more readable by simplifying commonly used tpmc command.
     16 
     17     @param subcommand: String of the tpmc subcommand (read, raw, ...)
     18     @return String output (which may be empty).
     19     """
     20 
     21     # Run tpmc command, redirect stderr to stdout, and merge to one line.
     22     cmd = 'tpmc %s 2>&1 | awk 1 ORS=""' % subcommand
     23     return utils.system_output(cmd, ignore_status=True).strip()
     24 
     25 def check_tpmc(subcommand, expected):
     26     """Runs tpmc command and checks the output against an expected regex.
     27 
     28     @param subcommand: String of the tpmc subcommand (read, raw, ...)
     29     @param expected: String re.
     30     @raises error.TestError() for not matching expected.
     31     """
     32     error_msg = 'invalid response to tpmc %s' % subcommand
     33     out = run_tpmc_cmd(subcommand)
     34     if (not re.match(expected, out)):
     35         raise error.TestError('%s: %s' % (error_msg, out))
     36 
     37     return out
     38 
     39 def expect_tpmc_error(subcommand, expected_error):
     40     check_tpmc(subcommand, '.*failed.*%s.*' % expected_error)
     41 
     42 class firmware_Cr50VirtualNVRam(test.test):
     43     'Tests the virtual NVRAM functionality in Cr50 through TPM commands.'
     44 
     45     version = 1
     46 
     47     def __take_tpm_ownership(self):
     48         global tpm_owner_password
     49         global tpm_pw_hex
     50         cryptohome.take_tpm_ownership(wait_for_ownership=True)
     51 
     52         tpm_owner_password = cryptohome.get_tpm_status()['Password']
     53         if not tpm_owner_password:
     54             raise error.TestError('TPM owner password is empty after '
     55                                   'taking ownership.')
     56         for ch in tpm_owner_password:
     57             tpm_pw_hex = tpm_pw_hex + format(ord(ch), 'x') + ' '
     58 
     59     def __read_tests(self):
     60         # Check reading board ID returns a valid value.
     61         bid = check_tpmc('read 0x3fff00 0xc',
     62                          '([0-9a-f]{1,2} ?){12}')
     63 
     64         # Check that a subset of board ID can be read.
     65         check_tpmc('read 0x3fff00 0x6',
     66                    ' '.join(bid.split()[:6]))  # Matches previous read
     67 
     68         # Check that size constraints are respected.
     69         expect_tpmc_error('read 0x3fffff 0xd',
     70                           '0x146')  # TPM_RC_NV_RANGE
     71 
     72         # Check zero-length can be read.
     73         check_tpmc('read 0x3fff00 0x0', '')
     74 
     75         # Check arbitrary index can be read.
     76         check_tpmc('read 0x3fff73 0x0', '')
     77 
     78         # Check highest index can be read.
     79         check_tpmc('read 0x3fffff 0x0', '')
     80 
     81     def __get_write_cmd(self, index, offset, size):
     82         assert (size + 35) < 256
     83         assert offset < 256
     84         cmd_size = format(35 + size, 'x') + ' '
     85         size_hex = '00 ' + format(size, 'x') + ' '
     86         offset_hex = '00 ' + format(offset, 'x') + ' '
     87         data = ''
     88         for _ in range(size):
     89             data = data + 'ff '
     90 
     91         return ('raw '
     92                 '80 02 '                    # TPM_ST_SESSIONS
     93                 '00 00 00 ' + cmd_size +    # commandSize
     94                 '00 00 01 37 ' +            # TPM_CC_NV_Write
     95                  index + ' ' +              # authHandle
     96                  index + ' ' +              # nvIndex
     97                 '00 00 00 09 '              # sessionSize
     98                 '40 00 00 09 '              # passwordAuth
     99                 '00 00 '                    # nonceSize
    100                 '00 '                       # sessionAttributes
    101                 '00 00 ' +                  # password length
    102                  size_hex +                 # dataSize
    103                  data +                     # data
    104                  offset_hex)                # offset
    105 
    106     def __write_tests(self):
    107         # Check an implemented index cannot be written to:
    108 
    109         # Zero-length write.
    110         expect_tpmc_error(self.__get_write_cmd('01 3f ff 00', 0, 0),
    111                           '0x148')  # TPM_RC_NV_LOCKED
    112 
    113         # Single byte.
    114         expect_tpmc_error(self.__get_write_cmd('01 3f ff 00', 0, 1),
    115                           '0x148')  # TPM_RC_NV_LOCKED
    116 
    117         # Single byte, offset.
    118         expect_tpmc_error(self.__get_write_cmd('01 3f ff 00', 4, 1),
    119                           '0x148')  # TPM_RC_NV_LOCKED
    120 
    121         # Write full length of index.
    122         expect_tpmc_error(self.__get_write_cmd('01 3f ff 00', 0, 12),
    123                           '0x148')  # TPM_RC_NV_LOCKED
    124 
    125         # Check an unimplemented index cannot be written to.
    126         expect_tpmc_error(self.__get_write_cmd('01 3f ff ff', 0, 1),
    127                           '0x148')  # TPM_RC_NV_LOCKED
    128 
    129     def __get_define_cmd(self, index, size):
    130         assert (len(tpm_owner_password) + 45) < 256
    131         pw_sz = format(len(tpm_owner_password), 'x') + ' '
    132         session_sz = format(len(tpm_owner_password) + 9, 'x') + ' '
    133         cmd_sz = format(len(tpm_owner_password) + 45, 'x') + ' '
    134 
    135         return ('raw '
    136                 '80 02 '
    137                 '00 00 00 ' + cmd_sz  +  # commandSize
    138                 '00 00 01 2a '              # commandCode
    139                 '40 00 00 01 '              # TPM_RH_OWNER
    140                 '00 00 00 ' + session_sz +  # sessionSize
    141                 '40 00 00 09 '              # passwordAuth
    142                 '00 00 '                    # nonceSize
    143                 '00 '                       # sessionAttributes
    144                 '00 ' + pw_sz +             # password length
    145                 tpm_pw_hex +                # password
    146                 '00 00 '                    # auth value
    147 
    148                 # TPM2B_NV_PUBLIC: publicInfo
    149                   '00 0e ' +                 # size
    150                   index + ' '                # nvIndex
    151                   '00 0b '                   # TPM_ALG_SHA256
    152                   '00 02 00 02 '             # attributes
    153                   '00 00 '                   # authPolicy
    154                   '00 ' + format(size, 'x')) # size
    155 
    156     def __get_undefine_cmd(self, index):
    157         assert (len(tpm_owner_password) + 31) < 256
    158         pw_sz = format(len(tpm_owner_password), 'x') + ' '
    159         session_sz = format(len(tpm_owner_password) + 9, 'x') + ' '
    160         cmd_sz = format(len(tpm_owner_password) + 31, 'x') + ' '
    161 
    162         return ('raw '
    163                 '80 02 '
    164                 '00 00 00 ' + cmd_sz + ' ' +  # commandSize
    165                 '00 00 01 22 '                # commandCode
    166                 '40 00 00 01 ' +              # TPM_RH_OWNER
    167                 index + ' ' +                 # nvIndex
    168                 '00 00 00 ' + session_sz +    # sessionSize
    169                 '40 00 00 09 '                # passwordAuth
    170                 '00 00 '                      # nonceSize
    171                 '00 '                         # sessionAttributes
    172                 '00 ' + pw_sz +               # password length
    173                 tpm_pw_hex)                   # password
    174 
    175     def __definespace_sanity_check(self):
    176         # A space outside the virtual range can be defined
    177         check_tpmc(self.__get_define_cmd('01 4f aa df', 12),
    178                    '(0x[0-9]{2} ){6}'
    179                    '(0x00 ){4}'        # TPM_RC_SUCCESS
    180                    '(0x[0-9]{2} ?){9}')
    181 
    182         # A space outside the virtual range can be undefined.
    183         check_tpmc(self.__get_undefine_cmd('01 4f aa df'),
    184                    '(0x[0-9]{2} ){6}'
    185                    '(0x00 ){4}'        # TPM_RC_SUCCESS
    186                    '(0x[0-9]{2} ?){9}')
    187 
    188     def __definespace_tests(self):
    189         # Check an implemented space in the virtual range cannot be defined.
    190         expect_tpmc_error(self.__get_define_cmd('01 3f ff 00', 12),
    191                           '0x149')  # TPM_RC_NV_AUTHORIZATION
    192 
    193         # Check an unimplemented space in the virtual range cannot be defined.
    194         expect_tpmc_error(self.__get_define_cmd('01 3f ff df', 12),
    195                           '0x149')  # TPM_RC_NV_AUTHORIZATION
    196 
    197     def __undefinespace_tests(self):
    198         # Check an implemented space in the virtual range cannot be defined.
    199         expect_tpmc_error(self.__get_undefine_cmd('01 3f ff 00'),
    200                           '0x149')  # TPM_RC_NV_AUTHORIZATION
    201 
    202         # Check an unimplemented space in the virtual range cannot be defined.
    203         expect_tpmc_error(self.__get_undefine_cmd('01 3f ff df'),
    204                           '0x149')  # TPM_RC_NV_AUTHORIZATION
    205 
    206     def __readpublic_test(self):
    207         public = check_tpmc(('raw '
    208                     '80 01 '         # TPM_ST_NO_SESSIONS
    209                     '00 00 00 0e '   # commandSize
    210                     '00 00 01 69 '   # TPM_CC_ReadPublic
    211                     '01 3f ff 00'),  # nvIndex
    212                    '(0x([0-9a-f]){2} *){62}')
    213 
    214         # For attribute details see Table 204, Part 2 of TPM2.0 spec
    215 
    216         attributes = hex(1 << 10 | # TPMA_NV_POLICY_DELETE
    217                          1 << 11 | # TPMA_NV_WRITELOCKED
    218                          1 << 13 | # TPMA_NV_WRITEDEFINE
    219                          1 << 18 | # TPMA_NV_AUTHREAD
    220                          1 << 29)  # TPMA_NV_WRITTEN
    221 
    222         attributes = '%s %s %s %s ' % (attributes[2:4],
    223                                        attributes[4:6],
    224                                        attributes[6:8],
    225                                        attributes[8:10])
    226 
    227         expected = ('80 01 '        # TPM_ST_NO_SESSIONS
    228                     '00 00 00 3e '  # responseSize
    229                     '00 00 00 00 '  # responseCode
    230 
    231                     # TPM2B_PUBLIC: nvPublic
    232                       '00 0e '        # size
    233                       '01 3f ff 00 '  # nvIndex
    234                       '00 0b ' +      # TPM_ALG_SHA256
    235                       attributes +    # attributes
    236                       '00 00 '        # authPolicy
    237                       '00 0c '        # dataSize
    238 
    239                    # TPM2B_NAME: name
    240                      '00 22 '  # size
    241                      '([0-9a-f] ?){34} ')
    242 
    243         if (not re.match(expected, re.sub('0x', '', public))):
    244             raise error.TestError('%s does not match expected (%s)'
    245                                   % (public, expected))
    246 
    247     def __readlock_test(self):
    248         # Virtual NV indices explicitly cannot be read locked, and attempts
    249         # to do so will return an authorization error.
    250 
    251         expect_tpmc_error(('raw '
    252                            '80 02 '             # TPM_ST_SESSIONS
    253                            '00 00 00 1f '       # commandSize
    254                            '00 00 01 4f '       # TPM_CC_NV_ReadLock
    255                            '01 3f ff 00 '       # authHandle
    256                            '01 3f ff 00 '       # nvIndex
    257                            '00 00 00 09 '       # sessionSize
    258                            '40 00 00 09 '       # password auth
    259                            '00 00 00 00 00'),   # empty password
    260                           '0x149')  # TPM_RC_NV_AUTHORIZATION
    261 
    262     def __writelock_test(self):
    263         # Virtual NV indices have no write policy defined, so cannot be write
    264         # locked.
    265 
    266         expect_tpmc_error(('raw '
    267                            '80 02 '              # TPM_ST_SESSIONS
    268                            '00 00 00 1f '        # commandSize
    269                            '00 00 01 38 '        # TPM_CC_NV_WriteLock
    270                            '01 3f ff 00 '        # authHandle
    271                            '01 3f ff 00 '        # nvIndex
    272                            '00 00 00 09 '        # sessionSize
    273                            '40 00 00 09 '        # password auth
    274                            '00 00 00 00 00'),    # empty password
    275                           '0x12f');  # TPM_RC_AUTH_UNAVAILABLE
    276 
    277     def initialize(self):
    278         self.__take_tpm_ownership()
    279         # Stop services that access to the TPM, to be able to use tpmc.
    280         # Note: for TPM2 the order of re-starting services (they are started
    281         # in the reversed listed order) is important: e.g. tpm_managerd must
    282         # start after trunksd, and cryptohomed after attestationd.
    283         self._services = service_stopper.ServiceStopper(['cryptohomed',
    284                                                          'chapsd',
    285                                                          'attestationd',
    286                                                          'tpm_managerd',
    287                                                          'tcsd',
    288                                                          'trunksd'])
    289         self._services.stop_services()
    290 
    291     def run_once(self):
    292         """Run a few TPM state checks."""
    293         # If first virtual index is not defined, assumed we are not running
    294         # on cr50, or running on an old build of cr50. Skip the test.
    295         if re.match('.*failed.*0x18b.*',  # TPM_RC_HANDLE
    296                     run_tpmc_cmd('read 0x3fff00 0x0')):
    297             raise error.TestNAError("TPM does not support vNVRAM")
    298 
    299         self.__readpublic_test()
    300         self.__definespace_sanity_check()
    301         self.__definespace_tests()
    302         self.__undefinespace_tests()
    303         self.__readlock_test()
    304         self.__writelock_test()
    305         self.__write_tests()
    306         self.__read_tests()
    307 
    308     def cleanup(self):
    309         self._services.restore_services()
    310