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 5import logging, re 6from autotest_lib.client.bin import test, utils 7from autotest_lib.client.common_lib import error 8from autotest_lib.client.cros import service_stopper 9from autotest_lib.client.cros import cryptohome 10 11tpm_owner_password = '' 12tpm_pw_hex = '' 13 14def 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 25def 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 39def expect_tpmc_error(subcommand, expected_error): 40 check_tpmc(subcommand, '.*failed.*%s.*' % expected_error) 41 42class 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