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