• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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