1# Copyright 2014 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 hashlib, logging 6 7from autotest_lib.client.common_lib import error 8from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 9 10def _encode_text(text): 11 return text.encode('utf-8') 12 13class firmware_TPMExtend(FirmwareTest): 14 """Test to ensure TPM PCRs are extended correctly.""" 15 version = 1 16 NEEDS_SERVO_USB = True 17 18 def initialize(self, host, cmdline_args): 19 super(firmware_TPMExtend, self).initialize(host, cmdline_args) 20 self.switcher.setup_mode('normal') 21 self.setup_usbkey(usbkey=True, host=False) 22 fwver = self.faft_client.system.run_shell_command_get_output( 23 'crossystem fwid')[0] 24 try: 25 fwver_major = int(fwver.split('.')[1]) 26 except ValueError: 27 raise error.TestFail('Could not determine firmware version') 28 self.disable_hwid_check = fwver_major < 6425 29 30 def _tpm1_check_pcr(self, num, hash_obj): 31 pcrs_file = '/sys/class/*/tpm0/device/pcrs' 32 pcrs = '\n'.join(self.faft_client.system.run_shell_command_get_output( 33 'cat %s' % pcrs_file)) 34 logging.debug('Dumping PCRs read from device: \n%s', pcrs) 35 extended = hashlib.sha1(b'\0' * 20 + hash_obj.digest()[:20]).hexdigest() 36 spaced = ' '.join(extended[i:i + 2] 37 for i in range(0, len(extended), 2)) 38 logging.debug('PCR %d should contain hash: %s', num, spaced) 39 return ('PCR-%.2d: %s' % (num, spaced.upper())) in pcrs 40 41 def _tpm2_check_pcr(self, num, hash_obj): 42 out = self.faft_client.system.run_shell_command_get_output( 43 'trunks_client --read_pcr --index=%d' % num)[0] 44 logging.debug('PCR %d read from device: %s', num, out) 45 padded = (hash_obj.digest() + b'\0' * 12)[:32] 46 extended = hashlib.sha256(b'\0' * 32 + padded).hexdigest().upper() 47 logging.debug('PCR %d should contain hash: %s', num, extended) 48 return extended in out 49 50 def _check_pcr(self, num, hash_obj): 51 """Returns true iff PCR |num| was extended with hashlib |hash_obj|.""" 52 if '1.' in self.faft_client.tpm.get_tpm_version(): 53 return self._tpm1_check_pcr(num, hash_obj) 54 else: 55 return self._tpm2_check_pcr(num, hash_obj) 56 57 def _check_pcr_bootmode(self, dev_mode, rec_mode, keyblock_flags): 58 bootmode = _encode_text(chr(dev_mode) + 59 chr(rec_mode) + 60 chr(keyblock_flags)) 61 if not self._check_pcr(0, hashlib.sha1(bootmode)): 62 msg = 'PCR0 was not extended with bootmode %d|%d|%d!' % ( 63 dev_mode, rec_mode, keyblock_flags) 64 raise error.TestFail(msg) 65 66 def run_once(self): 67 """Runs a single iteration of the test.""" 68 if self.disable_hwid_check: 69 logging.info( 70 'Skip testing HWID digest in PCR1 due to firmware version') 71 else: 72 logging.info('Verifying HWID digest in PCR1') 73 hwid = self.faft_client.system.run_shell_command_get_output( 74 'crossystem hwid')[0] 75 logging.debug('HWID reported by device is: %s', hwid) 76 if not self._check_pcr(1, hashlib.sha256(_encode_text(hwid))): 77 raise error.TestFail( 78 'PCR1 was not extended with SHA256 of HWID!') 79 80 logging.info('Verifying bootmode digest in PCR0 in normal mode') 81 self.check_state((self.checkers.crossystem_checker, { 82 'devsw_boot': '0', 83 'mainfw_type': 'normal' 84 })) 85 # dev_mode: 0, rec_mode: 0, keyblock_flags: "normal" (1) 86 self._check_pcr_bootmode(0, 0, 1) 87 88 logging.info('Verifying bootmode digest in PCR0 in recovery mode') 89 self.switcher.reboot_to_mode(to_mode='rec') 90 self.check_state((self.checkers.crossystem_checker, { 91 'devsw_boot': '0', 92 'mainfw_type': 'recovery' 93 })) 94 # dev_mode: 0, rec_mode: 1, keyblock_flags: "unknown" (0) 95 self._check_pcr_bootmode(0, 1, 0) 96 97 logging.info('Transitioning to dev mode for next test') 98 self.switcher.reboot_to_mode(to_mode='dev') 99 100 logging.info('Verifying bootmode digest in PCR0 in developer mode') 101 self.check_state((self.checkers.crossystem_checker, { 102 'devsw_boot': '1', 103 'mainfw_type': 'developer' 104 })) 105 # dev_mode: 1, rec_mode: 0, keyblock_flags: "normal" (1) 106 self._check_pcr_bootmode(1, 0, 1) 107 108 logging.info('Verifying bootmode digest in PCR0 in dev-recovery mode') 109 self.switcher.reboot_to_mode(to_mode='rec') 110 self.check_state((self.checkers.crossystem_checker, { 111 'devsw_boot': '1', 112 'mainfw_type': 'recovery' 113 })) 114 # dev_mode: 1, rec_mode: 1, keyblock_flags: "unknown" (0) 115 self._check_pcr_bootmode(1, 1, 0) 116 117 logging.info('All done, returning to normal mode') 118 self.switcher.reboot_to_mode(to_mode='normal') 119