xref: /aosp_15_r20/external/autotest/server/site_tests/firmware_TPMExtend/firmware_TPMExtend.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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