diff options
Diffstat (limited to 'test/tpm_test/tpmtest.py')
-rwxr-xr-x | test/tpm_test/tpmtest.py | 298 |
1 files changed, 158 insertions, 140 deletions
diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py index 11218cbcc6..96f2587396 100755 --- a/test/tpm_test/tpmtest.py +++ b/test/tpm_test/tpmtest.py @@ -1,8 +1,8 @@ #!/usr/bin/env python2 +# -*- coding: utf-8 -*- # Copyright 2015 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - """Module for initializing and driving a SPI TPM.""" from __future__ import print_function @@ -15,9 +15,9 @@ import traceback # Suppressing pylint warning about an import not at the top of the file. The # path needs to be set *before* the last import. -# pylint: disable=C6204 -root_dir = os.path.dirname(os.path.abspath(sys.argv[0])) -sys.path.append(os.path.join(root_dir, '..', '..', 'build', 'tpm_test')) +# pylint: disable=wrong-import-position +ROOT_DIR = os.path.dirname(os.path.abspath(sys.argv[0])) +sys.path.append(os.path.join(ROOT_DIR, '..', '..', 'build', 'tpm_test')) import crypto_test import drbg_test @@ -36,144 +36,162 @@ EXT_CMD = 0xbaccd00a class TPM(object): - """TPM accessor class. - - Object of this class allows to send valid and extended TPM commands (using - the command() method. The wrap_command/unwrap_response methods provide a - means of encapsulating extended commands in proper TPM data packets, as well - as extracting extended command responses. - - Attributes: - _handle: a ftdi_spi_tpm object, a USB/FTDI/SPI driver which allows - communicate with a TPM connected over USB dongle. - """ - - HEADER_FMT = '>H2IH' - STARTUP_CMD = '80 01 00 00 00 0c 00 00 01 44 00 00' - STARTUP_RSP = ('80 01 00 00 00 0a 00 00 00 00', - '80 01 00 00 00 0a 00 00 01 00') - - def __init__(self, freq=800*1000, debug_mode=False): - self._debug_enabled = debug_mode - self._handle = ftdi_spi_tpm - if not self._handle.FtdiSpiInit(freq, debug_mode): - raise subcmd.TpmTestError('Failed to connect') - - def validate(self, data_blob, response_mode=False): - """Check if a data blob complies with TPM command/response header format.""" - (tag, size, cmd_code, _) = struct.unpack_from( - self.HEADER_FMT, data_blob + ' ') - prefix = 'Misformatted blob: ' - if tag not in (0x8001, 0x8002): - raise subcmd.TpmTestError(prefix + 'bad tag value 0x%4.4x' % tag) - if size != len(data_blob): - raise subcmd.TpmTestError(prefix + 'size mismatch: header %d, actual %d' - % (size, len(data_blob))) - if size > 4096: - raise subcmd.TpmTestError(prefix + 'invalid size %d' % size) - if response_mode: - # Startup response code, extension or vendor command response code - if cmd_code == 0x100 or cmd_code == 0 or cmd_code == 0x500: - return - else: - raise subcmd.TpmTestError( - prefix + 'invalid response code 0x%x' % cmd_code) - if cmd_code >= 0x11f and cmd_code <= 0x18f: - return # This is a valid command - if cmd_code == EXT_CMD: - return # This is an extension command - if cmd_code >= 0x20000000 and cmd_code <= 0x200001ff: - return # this is vendor command - raise subcmd.TpmTestError(prefix + 'invalid command code 0x%x' % cmd_code) - - def command(self, cmd_data): - # Verify command header - self.validate(cmd_data) - response = self._handle.FtdiSendCommandAndWait(cmd_data) - self.validate(response, response_mode=True) - return response - - def wrap_ext_command(self, subcmd_code, cmd_body): - return struct.pack(self.HEADER_FMT, 0x8001, - len(cmd_body) + struct.calcsize(self.HEADER_FMT), - EXT_CMD, subcmd_code) + cmd_body - - def unwrap_ext_response(self, expected_subcmd, response): - """Verify basic validity and strip off TPM extended command header. - - Get the response generated by the device, as it came off the wire, verify - that header fields match expectations, then strip off the extension - command header and return the payload to the caller. - - Args: - expected_subcmd: an int, up to 16 bits in size, the extension command - this response is supposed to be for. - response: a binary string, the actual response received over the wire. - Returns: - the binary string of the response payload, if validation succeeded. - Raises: - subcmd.TpmTestError: in case there are any validation problems, the - error message describes the problem. + """TPM accessor class. + + Object of this class allows to send valid and extended TPM commands (using + the command() method. The wrap_command/unwrap_response methods provide a + means of encapsulating extended commands in proper TPM data packets, as well + as extracting extended command responses. + + Attributes: + _handle: a ftdi_spi_tpm object, a USB/FTDI/SPI driver which allows + communicate with a TPM connected over USB dongle. """ - header_size = struct.calcsize(self.HEADER_FMT) - tag, size, cmd, sub = struct.unpack(self.HEADER_FMT, - response[:header_size]) - if tag != 0x8001: - raise subcmd.TpmTestError('Wrong response tag: %4.4x' % tag) - if cmd: - raise subcmd.TpmTestError('Unexpected response command field: %8.8x' % - cmd) - if sub != expected_subcmd: - raise subcmd.TpmTestError('Unexpected response subcommand field: %2.2x' % - sub) - if size != len(response): - raise subcmd.TpmTestError('Size mismatch: header %d, actual %d' % ( - size, len(response))) - return response[header_size:] - - def debug_enabled(self): - return self._debug_enabled + + HEADER_FMT = '>H2IH' + STARTUP_CMD = '80 01 00 00 00 0c 00 00 01 44 00 00' + STARTUP_RSP = ('80 01 00 00 00 0a 00 00 00 00', + '80 01 00 00 00 0a 00 00 01 00') + + def __init__(self, freq=800*1000, debug_mode=False): + self._debug_enabled = debug_mode + self._handle = ftdi_spi_tpm + if not self._handle.FtdiSpiInit(freq, debug_mode): + raise subcmd.TpmTestError('Failed to connect') + + def validate(self, data_blob, response_mode=False): + """Validate TPM header format + + Check if a data blob complies with TPM command/response + header format. + """ + + (tag, size, cmd_code, _) = struct.unpack_from( + self.HEADER_FMT, data_blob + ' ') + prefix = 'Misformatted blob: ' + if tag not in (0x8001, 0x8002): + raise subcmd.TpmTestError(prefix + 'bad tag value 0x%4.4x' % tag) + if size != len(data_blob): + raise subcmd.TpmTestError(prefix + + 'size mismatch: header %d, actual %d' + % (size, len(data_blob))) + if size > 4096: + raise subcmd.TpmTestError(prefix + 'invalid size %d' % size) + if response_mode: + # Startup response code, extension or vendor command response code + if cmd_code not in (0, 0x100, 0x500): + raise subcmd.TpmTestError( + prefix + 'invalid response code 0x%x' % cmd_code) + return + if 0x11f <= cmd_code <= 0x18f: + return # This is a valid command + if cmd_code == EXT_CMD: + return # This is an extension command + if 0x20000000 <= cmd_code <= 0x200001ff: + return # this is vendor command + raise subcmd.TpmTestError(prefix + 'invalid command code 0x%x' + % cmd_code) + + def command(self, cmd_data): + """Verify command header""" + self.validate(cmd_data) + response = self._handle.FtdiSendCommandAndWait(cmd_data) + self.validate(response, response_mode=True) + return response + + def wrap_ext_command(self, subcmd_code, cmd_body): + """Wrap TPM command into extension command header""" + return struct.pack(self.HEADER_FMT, 0x8001, + len(cmd_body) + struct.calcsize(self.HEADER_FMT), + EXT_CMD, subcmd_code) + cmd_body + + def unwrap_ext_response(self, expected_subcmd, response): + """Verify basic validity and strip off TPM extended command header. + + Get the response generated by the device, as it came off the wire, + verify that header fields match expectations, then strip off the + extension command header and return the payload to the caller. + + Args: + expected_subcmd: an int, up to 16 bits in size, the extension + command this response is supposed to be for. + response: a binary string, the actual response received + over the wire. + + Returns: + the binary string of the response payload, + if validation succeeded. + + Raises: + subcmd.TpmTestError: in case there are any validation problems, + the error message describes the problem. + """ + header_size = struct.calcsize(self.HEADER_FMT) + tag, size, cmd, sub = struct.unpack(self.HEADER_FMT, + response[:header_size]) + if tag != 0x8001: + raise subcmd.TpmTestError('Wrong response tag: %4.4x' % tag) + if cmd: + raise subcmd.TpmTestError('Unexpected response command' + ' field: %8.8x' % cmd) + if sub != expected_subcmd: + raise subcmd.TpmTestError('Unexpected response subcommand' + ' field: %2.2x' % sub) + if size != len(response): + raise subcmd.TpmTestError('Size mismatch: header %d, actual %d' % + (size, len(response))) + return response[header_size:] + + def debug_enabled(self): + """Return status of debugging""" + return self._debug_enabled def usage(): - print ('Syntax: tpmtest.py [-d | -t | -h ]\n' - ' -d - prints additional debug information during tests\n' - ' -t - dump raw output from TRNG to /tmp/trng_output\n' - ' -h - this help\n') - return + """Print usage information""" + print('Syntax: tpmtest.py [-d | -t | -h ]\n' + ' -d - prints additional debug information during tests\n' + ' -t - dump raw output from TRNG to /tmp/trng_output\n' + ' -h - this help\n') + +def main(): + """Run TPM tests""" + try: + opts, _ = getopt.getopt(sys.argv[1:], 'dth', 'help') + except getopt.GetoptError as err: + print(str(err)) + usage() + sys.exit(2) + debug_needed = False + trng_only = False + for option, _ in opts: + if option == '-d': + debug_needed = True + elif option == '-t': + trng_only = True + elif option in ('-h', '--help'): + usage() + sys.exit(0) + try: + tpm_object = TPM(debug_mode=debug_needed) + if trng_only: + trng_test.trng_test(tpm_object) + sys.exit(1) + crypto_test.crypto_tests(tpm_object, os.path.join(ROOT_DIR, + 'crypto_test.xml')) + drbg_test.drbg_test(tpm_object) + ecc_test.ecc_test(tpm_object) + ecies_test.ecies_test(tpm_object) + hash_test.hash_test(tpm_object) + hkdf_test.hkdf_test(tpm_object) + rsa_test.rsa_test(tpm_object) + upgrade_test.upgrade(tpm_object) + except subcmd.TpmTestError as tpm_exc: + exc_file, exc_line = traceback.extract_tb(sys.exc_traceback)[-1][:2] + print('\nError in %s:%s: ' % (os.path.basename(exc_file), exc_line), + tpm_exc) + if debug_needed: + traceback.print_exc() + sys.exit(1) if __name__ == '__main__': - try: - opts, args = getopt.getopt(sys.argv[1:], 'dth','help') - except getopt.GetoptError as err: - print(str(err)) - usage() - sys.exit(2) - debug_needed = False - trng_only = False - for o, a in opts: - if o == '-d': - debug_needed = True - elif o == '-t': - trng_only = True - elif o == '-h' or o == '--help': - usage() - sys.exit(0) - try: - t = TPM(debug_mode=debug_needed) - if trng_only: - trng_test.trng_test(t) - sys.exit(1) - crypto_test.crypto_tests(t, os.path.join(root_dir, 'crypto_test.xml')) - drbg_test.drbg_test(t) - ecc_test.ecc_test(t) - ecies_test.ecies_test(t) - hash_test.hash_test(t) - hkdf_test.hkdf_test(t) - rsa_test.rsa_test(t) - upgrade_test.upgrade(t) - except subcmd.TpmTestError as e: - exc_file, exc_line = traceback.extract_tb(sys.exc_traceback)[-1][:2] - print('\nError in %s:%s: ' % (os.path.basename(exc_file), exc_line), e) - if debug_needed: - traceback.print_exc() - sys.exit(1) + main() |