summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2015-11-18 10:56:51 -0800
committerchrome-bot <chrome-bot@chromium.org>2015-11-21 13:12:18 -0800
commitafaaba44f13e7982e68b503123d48f912f3fd5eb (patch)
tree8c01fdeee0915cbf50dd75b4b95c912ea2a34a98 /test
parent32267c1094a1961ad8acd180f3220faed51794d0 (diff)
downloadchrome-ec-afaaba44f13e7982e68b503123d48f912f3fd5eb.tar.gz
cr50: Extended command test utility
The utility builds on the extended command protocol recently introduced in the EC and allows to test implementation of various cryptographic primitives available on CR50. This patch brings in the ftdi_spi_tpm.c and mpsse.c from the AOSP trunksd package. mpsse.c has been modified to limit its feature set (no i2c or bigbang support, only SPI0 mode), and ftdit_spi_tpm.c has been modified to properly present binary strings to the Python swig wrapper. The crypro_test.xml file includes descriptions of the tests to perform on the target. Most of its contents other than the first crypto_test element are borrowed from NIST AES test vectors set. See file header for description of the contents format. The actual test command is the tpmtest.py. When started it establishes connection with the device, and then reads test vectors from crypto_test.xml and executes them one at a time. Starting the test program with the -d command line argument enables debug output sent to the console. There are some other programs in ./extras which use the mpsse.c from AOSP, they will have to be modified to use the local copy. BRANCH=none BUG=chrome-os-partner:43025 TEST=ran the following in the directory: $ make # output suppressed $ ./tpmtest.py Starting MPSSE at 800 kHz Connected to device vid:did:rid of 1ae0:0028:00 \New max timeout: 1 s SUCCESS: AES:ECB common SUCCESS: AES:ECB128 1 SUCCESS: AES:ECB192 1 SUCCESS: AES:ECB256 1 SUCCESS: AES:ECB256 2 SUCCESS: AES:CTR128I 1 SUCCESS: AES:CTR256I 1 - temporarily corrupted the contents of the clear_text element of 'AES:ECB common': $ ./tpmtest.py Starting MPSSE at 800 kHz Connected to device vid:did:rid of 1ae0:0028:00 | Out text mismatch in node AES:ECB common, operation 1: In text: 74 68 69 73 20 20 69 73 20 74 68 65 20 74 65 78 74 20 77 68 69 63 68 20 77 69 6c 6c 20 62 65 20 65 6e 63 72 79 70 74 65 64 20 69 66 20 65 76 65 72 79 74 68 69 6e 67 20 69 73 20 67 6f 69 6e 67 20 66 69 6e 65 2e 20 Expected out text: 3d e2 0f f9 ee d9 62 ce f0 8a 17 57 c6 04 86 d0 3d ec 44 72 d8 79 18 87 3f 31 81 6d 66 4c bb 10 da 8d e0 9f 63 67 b3 cc 64 b4 e8 bd 12 b0 a9 c9 09 6d f0 9f a4 e2 ae fb 0d fe 1c 90 6c e2 fe f0 68 8f b5 34 07 76 e2 a9 72 8e dd 7b 8b 52 2b 8b Real out text: f9 50 fe 93 c9 3f cb e5 9e e0 a4 7e 51 1a bb a0 36 2f d1 d6 5f a8 1d 22 5a 1a bb f7 e6 65 89 55 ad e8 f5 8f 1a 20 ff a5 c4 de 76 3e b8 ef cc 8d 9d 94 b8 89 22 1c c9 2a 43 58 c3 8c 75 9f 9f 56 ab 2f 89 1a f6 a0 36 8b 95 23 91 d6 23 47 77 36 Change-Id: I2687ac03236b528e71b92df7cb35606e473ab2c5 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/313443 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Diffstat (limited to 'test')
-rw-r--r--test/tpm_test/Makefile56
-rw-r--r--test/tpm_test/crypto_test.xml112
-rw-r--r--test/tpm_test/ftdi_spi_tpm.c448
-rw-r--r--test/tpm_test/ftdi_spi_tpm.h25
-rw-r--r--test/tpm_test/ftdi_spi_tpm.i50
-rw-r--r--test/tpm_test/mpsse.c723
-rw-r--r--test/tpm_test/mpsse.h45
-rw-r--r--test/tpm_test/support.c218
-rw-r--r--test/tpm_test/support.h87
-rwxr-xr-xtest/tpm_test/tpmtest.py364
10 files changed, 2128 insertions, 0 deletions
diff --git a/test/tpm_test/Makefile b/test/tpm_test/Makefile
new file mode 100644
index 0000000000..e68c0f1c76
--- /dev/null
+++ b/test/tpm_test/Makefile
@@ -0,0 +1,56 @@
+# 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.
+
+ifeq ($(V),)
+Q := @
+else
+Q :=
+endif
+
+obj = ../../build/tpm_test
+src = .
+SWIG = /usr/bin/swig
+
+CFLAGS = -fPIC
+CFLAGS += -I /usr/include/python2.7
+CFLAGS += -DLIBFTDI1=1
+CFLAGS += -c
+
+TARGET = ftdi_spi_tpm
+.PRECIOUS: $(obj)/ftdi_spi_tpm_wrap.c
+
+$(obj)/_$(TARGET).so:
+
+OBJS = $(obj)/$(TARGET).o $(obj)/$(TARGET)_wrap.o $(obj)/mpsse.o \
+ $(obj)/support.o
+
+DEPS := $(OBJS:.o=.o.d)
+
+$(OBJS): | $(obj)
+
+$(obj)/%.o: $(obj)/%.c
+ @echo " CC $(notdir $@)"
+ $(Q)gcc $(CFLAGS) -o $@ $<
+
+$(obj)/%.o: $(src)/%.c
+ @echo " CC $(notdir $@)"
+ $(Q)gcc $(CFLAGS) -Wall -Werror -MMD -MF $@.d -o $@ $<
+
+$(obj)/_$(TARGET).so: $(OBJS) $(obj)/$(TARGET).py
+ @echo " LD $(notdir $@)"
+ $(Q)rm -f $@
+ $(Q)ld -shared $(OBJS) -lftdi1 -o $@
+
+$(obj)/%_wrap.c: $(src)/%.i
+ @echo " SWIG $(notdir $@)"
+ $(Q)swig -python -outdir $(obj) -o $@ $<
+
+clean:
+ @rm -rf $(obj)/
+
+$(obj):
+ @echo " MKDIR $(obj)"
+ $(Q)mkdir -p $(obj)
+
+-include $(DEPS)
diff --git a/test/tpm_test/crypto_test.xml b/test/tpm_test/crypto_test.xml
new file mode 100644
index 0000000000..6de62d8bbb
--- /dev/null
+++ b/test/tpm_test/crypto_test.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0"?>
+<!--
+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.
+
+This file describes test vectors for various encryption schemes.
+
+Each description is encapsulated in a 'crypto_test' element. This element must
+have the name property set. The name starts witht the encryption scheme's name
+(say AES or DES), delimited by a colon, and followed by a three character
+encryption submode, if necessary (say ECB for AES).
+
+The rest of the attributes are self explanatory. The default format for the
+clear_text element is ASCII, for the rest - hex. This default could be
+overridded using the 'format' property.
+
+The ascii strings are stripped of leading and trailing whitespace and then
+joined using space as a separator. Whitespace in hes strings is ignored.
+
+Hex values are interpreted as a set of 4 byte entities in network byte order.
+Many of the crypto_test elements were borrowed from NIST test vectors.
+-->
+<crypto_tests>
+ <crypto_test name="AES:ECB common">
+ <clear_text>
+ this is the text which will be encrypted if everything is going fine.
+ </clear_text>
+ <key>0123456789abcdef0123456789abcdef0123456789abcdef</key>
+ <cipher_text>
+ <!--
+ Cipher text matches the case of the clear text padded with zeros to
+ the nearest block size.
+ -->
+ f90fe23d ce62d9ee 57178af0 d08604c6
+ 7244ec3d 871879d8 6d81313f 10bb4c66
+ 9fe08dda ccb36763 bde8b464 c9a9b012
+ 9ff06d09 fbaee2a4 901cfe0d f0fee26c
+ 34b58f68 a9e27607 7bdd8e72 8b2b528b
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB128 1">
+ <clear_text format="hex">
+ 33221100 77665544 bbaa9988 ffeeddcc
+ </clear_text>
+ <key>03020100 07060504 0b0a0908 0f0e0d0c</key>
+ <cipher_text>
+ d8e0c469 30047b6a 80b7cdd8 5ac5b470
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB192 1">
+ <clear_text format="hex">
+ 00000000 00000000 00000000 00000000
+ </clear_text>
+ <key>
+ 6e0fd215 9f647ebc b1765bd9 badae607
+ 948a7c96 297f7984
+ </key>
+ <cipher_text>
+ 42184e8e 3d1a594e 76086f5b 94856ff1
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB256 1">
+ <clear_text format="hex">
+ 00000000 00000000 00000000 00000000
+ </clear_text>
+ <key>
+ 00000080 00000000 00000000 00000000
+ 00000000 00000000 00000000 00000000
+ </key>
+ <cipher_text>
+ cb6d5ae3 a001b219 8afabc1e 59572ba2
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:ECB256 2">
+ <clear_text format="hex">
+ 45249ff6 179b4fdf 7b412bad 10376ce6
+ </clear_text>
+ <key>
+ 10eb3d60 be71ca15 f0ae732b 81777d85
+ 072c351f d708613b a310982d f4df1409
+ </key>
+ <cipher_text>
+ 7a4b3023 fff3f939 8f8d7d06 c7ec249e
+ </cipher_text>
+ </crypto_test>
+ <crypto_test name="AES:CTR128I 1">
+ <clear_text format="hex">
+ e2bec16b 969f402e 117e3de9 2a179373
+ </clear_text>
+ <key>
+ 16157e2b a6d2ae28 8815f7ab 3c4fcf09
+ </key>
+ <cipher_text>
+ 91614d87 26e320b6 6468ef1b ceb60d99
+ </cipher_text>
+ <iv>f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc</iv>
+ </crypto_test>
+ <crypto_test name="AES:CTR256I 1">
+ <clear_text format="hex">
+ 13c31e60 a5895777 04f5a7b7 28d2f3bb
+ </clear_text>
+ <key>
+ 10eb3d60 be71ca15 f0ae732b 81777d85
+ 072c351f d708613b a310982d f4df1409
+ </key>
+ <cipher_text>
+ e2bec16b 969f402e 117e3de9 2a179373
+ </cipher_text>
+ <iv>f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc</iv>
+ </crypto_test>
+</crypto_tests>
diff --git a/test/tpm_test/ftdi_spi_tpm.c b/test/tpm_test/ftdi_spi_tpm.c
new file mode 100644
index 0000000000..ac028ef325
--- /dev/null
+++ b/test/tpm_test/ftdi_spi_tpm.c
@@ -0,0 +1,448 @@
+/* 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.
+ */
+
+#include <endian.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ftdi_spi_tpm.h"
+
+static struct mpsse_context *mpsse_;
+static unsigned locality_; /* Set at initialization. */
+static int ftdi_trace_enabled;
+
+/* Assorted TPM2 registers for interface type FIFO. */
+#define TPM_ACCESS_REG 0
+#define TPM_STS_REG 0x18
+#define TPM_DATA_FIFO_REG 0x24
+#define TPM_DID_VID_REG 0xf00
+#define TPM_RID_REG 0xf04
+
+static struct swig_string_data empty_string_data = (struct swig_string_data){
+ .size = 0, .data = NULL
+};
+
+/* Locality management bits (in TPM_ACCESS_REG). */
+enum TpmAccessBits {
+ tpmRegValidSts = (1 << 7),
+ activeLocality = (1 << 5),
+ requestUse = (1 << 1),
+ tpmEstablishment = (1 << 0),
+};
+
+enum TpmStsBits {
+ tpmFamilyShift = 26,
+ tpmFamilyMask = ((1 << 2) - 1), /* 2 bits wide. */
+ tpmFamilyTPM2 = 1,
+ resetEstablishmentBit = (1 << 25),
+ commandCancel = (1 << 24),
+ burstCountShift = 8,
+ burstCountMask = ((1 << 16) - 1), /* 16 bits wide. */
+ stsValid = (1 << 7),
+ commandReady = (1 << 6),
+ tpmGo = (1 << 5),
+ dataAvail = (1 << 4),
+ Expect = (1 << 3),
+ selfTestDone = (1 << 2),
+ responseRetry = (1 << 1),
+};
+
+enum {
+ false = 0,
+ true = 1
+};
+
+/*
+ * SPI frame header for TPM transactions is 4 bytes in size, it is described
+ * in section "6.4.6 Spi Bit Protocol" of the TCG issued "TPM Profile (PTP)
+ * Specification Revision 00.43.
+ */
+struct SpiFrameHeader {
+ unsigned char body[4];
+};
+
+void FtdiStop(void)
+{
+ if (mpsse_)
+ Close(mpsse_);
+
+ mpsse_ = NULL;
+}
+
+static void StartTransaction(int read_write, size_t bytes, unsigned addr)
+{
+ struct SpiFrameHeader header;
+ int i;
+ uint8_t flow_c;
+ char *transfer_data;
+
+ /*
+ * give it 10 ms. TODO(vbendeb): remove this once cr50 SPS TPM driver
+ * performance is fixed.
+ */
+ usleep(10000);
+
+ /*
+ * The first byte of the frame header encodes the transaction type
+ * (read or write) and size (set to length - 1).
+ */
+ header.body[0] = (read_write ? 0x80 : 0) | 0x40 | (bytes - 1);
+
+ /* The rest of the frame header is the internal address in the TPM. */
+ for (i = 0; i < 3; i++)
+ header.body[i + 1] = (addr >> (8 * (2 - i))) & 0xff;
+
+ Start(mpsse_);
+
+ transfer_data =
+ Transfer(mpsse_, (char *)header.body, sizeof(header.body));
+
+ /*
+ * The TCG TPM over SPI specification itroduces the notion of SPI flow
+ * control (Section "6.4.5 Flow Control" of the TCG issued "TPM
+ * Profile (PTP) Specification Revision 00.43).
+ *
+ * The slave (TPM device) expects each transaction to start with a 4
+ * byte header trasmitted by master. If the slave needs to stall the
+ * transaction, it sets the MOSI bit to 0 during the last clock of the
+ * 4 byte header. In this case the master is supposed to start polling
+ * the line, byte at time, until the last bit in the received byte
+ * (transferred during the last clock of the byte) is set to 1.
+ */
+ flow_c = transfer_data[3];
+ free(transfer_data);
+ while (!(flow_c & 1)) {
+ transfer_data = Read(mpsse_, 1);
+ flow_c = transfer_data[0];
+ free(transfer_data);
+ }
+}
+
+static void trace_dump(const char *prefix, unsigned reg, size_t bytes,
+ const uint8_t *buffer)
+{
+ if (!ftdi_trace_enabled)
+ return;
+ printf("%s %2.2x:", prefix, reg);
+ if (bytes == 4) {
+ printf(" %8.8x\n", *(const uint32_t *)buffer);
+ } else {
+ int i;
+
+ for (i = 0; i < bytes; i++)
+ printf(" %2.2x", buffer[i]);
+ printf("\n");
+ }
+}
+
+static int FtdiWriteReg(unsigned reg_number, size_t bytes, void *buffer)
+{
+ if (!mpsse_)
+ return false;
+
+ trace_dump("W", reg_number, bytes, buffer);
+ StartTransaction(false, bytes, reg_number + locality_ * 0x10000);
+ Write(mpsse_, buffer, bytes);
+ Stop(mpsse_);
+ return true;
+}
+
+static int FtdiReadReg(unsigned reg_number, size_t bytes, void *buffer)
+{
+ void *data;
+
+ if (!mpsse_)
+ return false;
+
+ StartTransaction(true, bytes, reg_number + locality_ * 0x10000);
+ data = Read(mpsse_, bytes);
+ if (data)
+ memcpy(buffer, data, bytes);
+ free(data);
+ Stop(mpsse_);
+ trace_dump("R", reg_number, bytes, buffer);
+ return true;
+}
+
+static int ReadTpmSts(uint32_t *status)
+{
+ return FtdiReadReg(TPM_STS_REG, sizeof(*status), status);
+}
+
+static int WriteTpmSts(uint32_t status)
+{
+ return FtdiWriteReg(TPM_STS_REG, sizeof(status), &status);
+}
+
+static uint32_t GetBurstCount(void)
+{
+ uint32_t status;
+
+ ReadTpmSts(&status);
+ return (status >> burstCountShift) & burstCountMask;
+}
+
+int FtdiSpiInit(uint32_t freq, int enable_debug)
+{
+ uint32_t did_vid, status;
+ uint8_t cmd;
+ uint16_t vid;
+
+ if (mpsse_)
+ return true;
+
+ ftdi_trace_enabled = enable_debug;
+
+ /* round frequency down to the closest 100KHz */
+ freq = (freq / (100 * 1000)) * 100 * 1000;
+
+ printf("Starting MPSSE at %d kHz\n", freq / 1000);
+ mpsse_ = MPSSE(freq, MSB, NULL);
+ if (!mpsse_)
+ return false;
+
+ /* Reset the TPM using GPIOL0, issue a 100 ms long pulse. */
+ PinLow(mpsse_, GPIOL0);
+ usleep(100000);
+ PinHigh(mpsse_, GPIOL0);
+
+ FtdiReadReg(TPM_DID_VID_REG, sizeof(did_vid), &did_vid);
+
+ vid = did_vid & 0xffff;
+ if ((vid != 0x15d1) && (vid != 0x1ae0)) {
+ fprintf(stderr, "unknown did_vid: %#x\n", did_vid);
+ return false;
+ }
+
+ /* Try claiming locality zero. */
+ FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ if ((cmd & (activeLocality & tpmRegValidSts)) ==
+ (activeLocality & tpmRegValidSts)) {
+ /*
+ * Locality active - maybe reset line is not connected?
+ * Release the locality and try again
+ */
+ cmd = activeLocality;
+ FtdiWriteReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ }
+
+ /* tpmEstablishment can be either set or not. */
+ if ((cmd & ~tpmEstablishment) != tpmRegValidSts) {
+ fprintf(stderr, "invalid reset status: %#x\n", cmd);
+ return false;
+ }
+ cmd = requestUse;
+ FtdiWriteReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ FtdiReadReg(TPM_ACCESS_REG, sizeof(cmd), &cmd);
+ if ((cmd & ~tpmEstablishment) != (tpmRegValidSts | activeLocality)) {
+ fprintf(stderr, "failed to claim locality, status: %#x\n", cmd);
+ return false;
+ }
+
+ ReadTpmSts(&status);
+ if (((status >> tpmFamilyShift) & tpmFamilyMask) != tpmFamilyTPM2) {
+ fprintf(stderr, "unexpected TPM family value, status: %#x\n",
+ status);
+ return false;
+ }
+ FtdiReadReg(TPM_RID_REG, sizeof(cmd), &cmd);
+ printf("Connected to device vid:did:rid of %4.4x:%4.4x:%2.2x\n",
+ did_vid & 0xffff, did_vid >> 16, cmd);
+
+ return true;
+}
+
+/* This is in seconds. */
+#define MAX_STATUS_TIMEOUT 120
+static int WaitForStatus(uint32_t statusMask, uint32_t statusExpected)
+{
+ uint32_t status;
+ time_t target_time;
+ static unsigned max_timeout;
+
+ target_time = time(NULL) + MAX_STATUS_TIMEOUT;
+ do {
+ usleep(10000);
+ if (time(NULL) >= target_time) {
+ fprintf(stderr, "failed to get expected status %x\n",
+ statusExpected);
+ return false;
+ }
+ ReadTpmSts(&status);
+ } while ((status & statusMask) != statusExpected);
+
+ /* Calculate time spent waiting */
+ target_time = MAX_STATUS_TIMEOUT - target_time + time(NULL);
+ if (max_timeout < (unsigned)target_time) {
+ max_timeout = target_time;
+ printf("New max timeout: %d s\n", max_timeout);
+ }
+
+ return true;
+}
+
+static void SpinSpinner(void)
+{
+ static const char *spinner = "\\|/-";
+ static int index;
+
+ if (index > strlen(spinner))
+ index = 0;
+
+ fprintf(stdout, "%c[1D%c", 0x1b, spinner[index++]);
+ fflush(stdout);
+}
+
+#define MAX_RESPONSE_SIZE 4096
+#define HEADER_SIZE 6
+
+/* tpm_command points at a buffer 4096 bytes in size */
+struct swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size)
+{
+ uint32_t status;
+ uint32_t expected_status_bits;
+ size_t handled_so_far;
+ uint32_t payload_size;
+ char message[100];
+ int offset = 0;
+ uint8_t *response;
+
+ if (!mpsse_) {
+ fprintf(stderr, "attempt to use an uninitialized FTDI TPM!\n");
+ return empty_string_data;
+ }
+
+ response = malloc(MAX_RESPONSE_SIZE);
+ if (!response) {
+ fprintf(stderr, "attempt to use an uninitialized FTDI TPM!\n");
+ return empty_string_data;
+ }
+
+ handled_so_far = 0;
+
+ WriteTpmSts(commandReady);
+
+ memcpy(&payload_size, tpm_command + 2, sizeof(payload_size));
+ payload_size = be32toh(payload_size);
+ offset +=
+ snprintf(message, sizeof(message), "Message size %d", payload_size);
+
+ /*
+ * No need to wait for the sts.Expect bit to be set, at least with the
+ * 15d1:001b and 1ae0:0028 devices. Let's just write the command into
+ * FIFO, make sure not to exceed the burst count.
+ */
+ do {
+ uint32_t transaction_size;
+ uint32_t burst_count = GetBurstCount();
+
+ if (burst_count > 64)
+ burst_count = 64;
+
+ transaction_size = command_size - handled_so_far;
+ if (transaction_size > burst_count)
+ transaction_size = burst_count;
+
+ if (transaction_size) {
+ FtdiWriteReg(TPM_DATA_FIFO_REG, transaction_size,
+ tpm_command + handled_so_far);
+ handled_so_far += transaction_size;
+ }
+ } while (handled_so_far != command_size);
+
+ /* And tell the device it can start processing it. */
+ WriteTpmSts(tpmGo);
+
+ expected_status_bits = stsValid | dataAvail;
+ if (!WaitForStatus(expected_status_bits, expected_status_bits)) {
+ size_t i;
+
+ printf("Failed processing. %s:", message);
+ for (i = 0; i < command_size; i++) {
+ if (!(i % 16))
+ printf("\n");
+ printf(" %2.2x", tpm_command[i]);
+ }
+ printf("\n");
+ return empty_string_data;
+ }
+
+ /*
+ * The tpm_command is ready, let's read it.
+ *
+ * First we read the FIFO payload header, to see how much data to
+ * expect. The header size is fixed to six bytes, the total payload
+ * size is stored in network order in the last four bytes of the
+ * header.
+ */
+ FtdiReadReg(TPM_DATA_FIFO_REG, HEADER_SIZE, response);
+ handled_so_far = HEADER_SIZE;
+
+ /* Figure out the total payload size. */
+ memcpy(&payload_size, response + 2, sizeof(payload_size));
+ payload_size = be32toh(payload_size);
+
+ if (ftdi_trace_enabled)
+ printf("%s response size %d\n\n", message, payload_size);
+ else
+ SpinSpinner();
+
+ if (payload_size > MAX_RESPONSE_SIZE)
+ return empty_string_data;
+ /*
+ * Let's read all but the last byte in the FIFO to make sure the
+ * status register is showing correct flow control bits: 'more data'
+ * until the last byte and then 'no more data' once the last byte is
+ * read.
+ */
+ payload_size = payload_size - 1;
+ do {
+ uint32_t transaction_size;
+ uint32_t burst_count = GetBurstCount();
+
+ if (burst_count > 64)
+ burst_count = 64;
+
+ transaction_size = payload_size - handled_so_far;
+ if (transaction_size > burst_count)
+ transaction_size = burst_count;
+
+ if (transaction_size) {
+ FtdiReadReg(TPM_DATA_FIFO_REG, transaction_size,
+ response + handled_so_far);
+ handled_so_far += transaction_size;
+ }
+ } while (handled_so_far != payload_size);
+
+ /* Verify that there is still data to come. */
+ ReadTpmSts(&status);
+ if ((status & expected_status_bits) != expected_status_bits) {
+ fprintf(stderr, "unexpected status %#x\n", status);
+ return empty_string_data;
+ }
+
+ FtdiReadReg(TPM_DATA_FIFO_REG, 1, response + handled_so_far);
+
+ /* Verify that 'data available' is not asseretd any more. */
+ ReadTpmSts(&status);
+ if ((status & expected_status_bits) != stsValid) {
+ fprintf(stderr, "unexpected status %#x\n", status);
+ return empty_string_data;
+ }
+
+ /* Move the TPM back to idle state. */
+ WriteTpmSts(commandReady);
+
+ handled_so_far++;
+
+ return (struct swig_string_data) {
+ .size = handled_so_far, .data = response};
+}
diff --git a/test/tpm_test/ftdi_spi_tpm.h b/test/tpm_test/ftdi_spi_tpm.h
new file mode 100644
index 0000000000..5393d4cd86
--- /dev/null
+++ b/test/tpm_test/ftdi_spi_tpm.h
@@ -0,0 +1,25 @@
+/* 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.
+ */
+
+#ifndef __EC_TEST_TPM_TEST_FTDI_SPI_TPM_H
+#define __EC_TEST_TPM_TEST_FTDI_SPI_TPM_H
+
+#include "mpsse.h"
+
+/*
+ * This structure allows to convert string representation between C and
+ * Python.
+ */
+struct swig_string_data {
+ int size;
+ uint8_t *data;
+};
+
+int FtdiSpiInit(uint32_t freq, int enable_debug);
+void FtdiStop(void);
+struct swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size);
+
+#endif /* ! __EC_TEST_TPM_TEST_FTDI_SPI_TPM_H */
diff --git a/test/tpm_test/ftdi_spi_tpm.i b/test/tpm_test/ftdi_spi_tpm.i
new file mode 100644
index 0000000000..c9cc2fc535
--- /dev/null
+++ b/test/tpm_test/ftdi_spi_tpm.i
@@ -0,0 +1,50 @@
+/* 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 ftdi_spi_tpm
+typedef unsigned uint32_t;
+typedef unsigned char uint8_t;
+
+%{
+typedef struct swig_string_data
+{
+ int size;
+ char *data;
+} swig_string_data;
+
+extern int FtdiSpiInit(uint32_t freq, int enable_debug);
+extern void FtdiStop(void);
+extern swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size);
+%}
+
+%typemap(in) (char *tpm_command, int command_size)
+{
+ if(!PyString_Check($input))
+ {
+ PyErr_SetString(PyExc_ValueError, "String value required");
+ return NULL;
+ }
+
+ $1 = PyString_AsString($input);
+ $2 = PyString_Size($input);
+}
+
+%typemap(out) swig_string_data
+{
+ $result = PyString_FromStringAndSize($1.data, $1.size);
+ free($1.data);
+}
+
+typedef struct swig_string_data
+{
+ int size;
+ char *data;
+} swig_string_data;
+
+extern int FtdiSpiInit(uint32_t freq, int enable_debug);
+extern void FtdiStop(void);
+extern swig_string_data FtdiSendCommandAndWait(char *tpm_command,
+ int command_size);
diff --git a/test/tpm_test/mpsse.c b/test/tpm_test/mpsse.c
new file mode 100644
index 0000000000..9b8d97e8e3
--- /dev/null
+++ b/test/tpm_test/mpsse.c
@@ -0,0 +1,723 @@
+/* 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.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if LIBFTDI1 == 1
+#include <libftdi1/ftdi.h>
+#else
+#include <ftdi.h>
+#endif
+
+#include "mpsse.h"
+#include "support.h"
+
+/* FTDI interfaces */
+enum interface {
+ IFACE_ANY = INTERFACE_ANY,
+ IFACE_A = INTERFACE_A,
+ IFACE_B = INTERFACE_B,
+ IFACE_C = INTERFACE_C,
+ IFACE_D = INTERFACE_D
+};
+
+enum mpsse_commands {
+ INVALID_COMMAND = 0xAB,
+ ENABLE_ADAPTIVE_CLOCK = 0x96,
+ DISABLE_ADAPTIVE_CLOCK = 0x97,
+ ENABLE_3_PHASE_CLOCK = 0x8C,
+ DISABLE_3_PHASE_CLOCK = 0x8D,
+ TCK_X5 = 0x8A,
+ TCK_D5 = 0x8B,
+ CLOCK_N_CYCLES = 0x8E,
+ CLOCK_N8_CYCLES = 0x8F,
+ PULSE_CLOCK_IO_HIGH = 0x94,
+ PULSE_CLOCK_IO_LOW = 0x95,
+ CLOCK_N8_CYCLES_IO_HIGH = 0x9C,
+ CLOCK_N8_CYCLES_IO_LOW = 0x9D,
+ TRISTATE_IO = 0x9E,
+};
+
+/* Common clock rates */
+enum clock_rates {
+ ONE_HUNDRED_KHZ = 100000,
+ FOUR_HUNDRED_KHZ = 400000,
+ ONE_MHZ = 1000000,
+ TWO_MHZ = 2000000,
+ FIVE_MHZ = 5000000,
+ SIX_MHZ = 6000000,
+ TEN_MHZ = 10000000,
+ TWELVE_MHZ = 12000000,
+ FIFTEEN_MHZ = 15000000,
+ THIRTY_MHZ = 30000000,
+ SIXTY_MHZ = 60000000
+};
+
+#define NULL_CONTEXT_ERROR_MSG "NULL MPSSE context pointer!"
+#define SPI_TRANSFER_SIZE 512
+#define SPI_RW_SIZE (63 * 1024)
+#define SETUP_DELAY 25000
+#define LATENCY_MS 2
+#define USB_TIMEOUT 120000
+#define CHUNK_SIZE 65535
+#define MAX_SETUP_COMMANDS 10
+
+/* SK and CS are high, GPIO1 is reset on the FPGA hookup, all others low */
+#define DEFAULT_PORT (SK | CS | GPIO1)
+/* SK/DO/CS and GPIOs are outputs, DI is an input */
+#define DEFAULT_TRIS (SK | DO | CS | GPIO0 | GPIO1 | GPIO2 | GPIO3)
+
+static struct vid_pid {
+ int vid;
+ int pid;
+ char *description;
+} supported_devices[] = {
+ {
+ 0x0403, 0x6010, "FT2232 Future Technology Devices International, Ltd"},
+ {
+ 0x0403, 0x6011, "FT4232 Future Technology Devices International, Ltd"},
+ {
+ 0x0403, 0x6014,
+ "FT232H Future Technology Devices International, Ltd"},
+ /* These devices are based on FT2232 chips, but have not been tested. */
+ {
+ 0x0403, 0x8878, "Bus Blaster v2 (channel A)"}, {
+ 0x0403, 0x8879, "Bus Blaster v2 (channel B)"}, {
+ 0x0403, 0xBDC8, "Turtelizer JTAG/RS232 Adapter A"}, {
+ 0x0403, 0xCFF8, "Amontec JTAGkey"}, {
+ 0x0403, 0x8A98, "TIAO Multi Protocol Adapter"}, {
+ 0x15BA, 0x0003, "Olimex Ltd. OpenOCD JTAG"}, {
+ 0x15BA, 0x0004, "Olimex Ltd. OpenOCD JTAG TINY"}, {
+ 0, 0, NULL}
+};
+
+/*
+ * Enables or disables flushing of the FTDI chip's RX buffers after each read
+ * operation. Flushing is disable by default.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @tf - Set to 1 to enable flushing, or 0 to disable flushing.
+ *
+ * Returns void.
+ */
+static void FlushAfterRead(struct mpsse_context *mpsse, int tf)
+{
+ mpsse->flush_after_read = tf;
+}
+
+/*
+ * Enable / disable internal loopback.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @enable - Zero to disable loopback, 1 to enable loopback.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+static int SetLoopback(struct mpsse_context *mpsse, int enable)
+{
+ unsigned char buf[1] = { 0 };
+ int retval = MPSSE_FAIL;
+
+ if (is_valid_context(mpsse)) {
+ if (enable)
+ buf[0] = LOOPBACK_START;
+ else
+ buf[0] = LOOPBACK_END;
+
+ retval = raw_write(mpsse, buf, 1);
+ }
+
+ return retval;
+}
+
+/*
+ * Sets the appropriate divisor for the desired clock frequency.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @freq - Desired clock frequency in hertz.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+static int SetClock(struct mpsse_context *mpsse, uint32_t freq)
+{
+ int retval = MPSSE_FAIL;
+ uint32_t system_clock = 0;
+ uint16_t divisor = 0;
+ unsigned char buf[CMD_SIZE] = { 0 };
+
+ /*
+ * Do not call is_valid_context() here, as the FTDI chip may not be
+ * completely configured when SetClock is called
+ */
+ if (!mpsse)
+ return retval;
+
+ if (freq > SIX_MHZ) {
+ buf[0] = TCK_X5;
+ system_clock = SIXTY_MHZ;
+ } else {
+ buf[0] = TCK_D5;
+ system_clock = TWELVE_MHZ;
+ }
+
+ if (raw_write(mpsse, buf, 1) == MPSSE_OK) {
+ if (freq <= 0)
+ divisor = 0xFFFF;
+ else
+ divisor = freq2div(system_clock, freq);
+
+ buf[0] = TCK_DIVISOR;
+ buf[1] = (divisor & 0xFF);
+ buf[2] = ((divisor >> 8) & 0xFF);
+
+ if (raw_write(mpsse, buf, 3) == MPSSE_OK) {
+ mpsse->clock = div2freq(system_clock, divisor);
+ retval = MPSSE_OK;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Sets the appropriate transmit and receive commands based on the requested
+ * mode and byte order.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @endianness - MPSSE_MSB or MPSSE_LSB.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+static int SetMode(struct mpsse_context *mpsse, int endianness)
+{
+ int retval = MPSSE_OK, i = 0, setup_commands_size = 0;
+ unsigned char buf[CMD_SIZE] = { 0 };
+ unsigned char setup_commands[CMD_SIZE * MAX_SETUP_COMMANDS] = { 0 };
+
+ /*
+ * Do not call is_valid_context() here, as the FTDI chip may not be
+ * completely configured when SetMode is called
+ */
+ if (!mpsse)
+ return MPSSE_FAIL;
+
+ /* Read and write commands need to include endianness */
+ mpsse->tx = MPSSE_DO_WRITE | endianness;
+ mpsse->rx = MPSSE_DO_READ | endianness;
+ mpsse->txrx = MPSSE_DO_WRITE | MPSSE_DO_READ | endianness;
+
+ /*
+ * Clock, data out, chip select pins are outputs; all others are
+ * inputs.
+ */
+ mpsse->tris = DEFAULT_TRIS;
+
+ /* Clock and chip select pins idle high; all others are low */
+ mpsse->pidle = mpsse->pstart = mpsse->pstop = DEFAULT_PORT;
+
+ /* During reads and writes the chip select pin is brought low */
+ mpsse->pstart &= ~CS;
+
+ /* Disable FTDI internal loopback */
+ SetLoopback(mpsse, 0);
+
+ /* Ensure adaptive clock is disabled */
+ setup_commands[setup_commands_size++] = DISABLE_ADAPTIVE_CLOCK;
+
+ switch (mpsse->mode) {
+ case SPI0:
+ /* SPI mode 0 clock idles low */
+ mpsse->pidle &= ~SK;
+ mpsse->pstart &= ~SK;
+ mpsse->pstop &= ~SK;
+
+ /*
+ * SPI mode 0 propogates data on the falling edge and read
+ * data on the rising edge of the clock
+ */
+ mpsse->tx |= MPSSE_WRITE_NEG;
+ mpsse->rx &= ~MPSSE_READ_NEG;
+ mpsse->txrx |= MPSSE_WRITE_NEG;
+ mpsse->txrx &= ~MPSSE_READ_NEG;
+ break;
+ default:
+ fprintf(stderr, "%s:%d attempt to set an unsupported mode %d\n",
+ __func__, __LINE__, mpsse->mode);
+ retval = MPSSE_FAIL;
+ }
+
+ /* Send any setup commands to the chip */
+ if ((retval == MPSSE_OK) && (setup_commands_size > 0))
+ retval = raw_write(mpsse, setup_commands, setup_commands_size);
+
+ if (retval == MPSSE_OK) {
+ /* Set the idle pin states */
+ set_bits_low(mpsse, mpsse->pidle);
+
+ /* All GPIO pins are outputs, set low */
+ mpsse->trish = 0xFF;
+ mpsse->gpioh = 0x00;
+
+ buf[i++] = SET_BITS_HIGH;
+ buf[i++] = mpsse->gpioh;
+ buf[i++] = mpsse->trish;
+
+ retval = raw_write(mpsse, buf, i);
+ }
+
+ return retval;
+}
+
+/*
+ * Open device by VID/PID/index
+ *
+ * @vid - Device vendor ID.
+ * @pid - Device product ID.
+ * @freq - Clock frequency to use for the specified mode.
+ * @endianness - Specifies how data is clocked in/out (MSB, LSB).
+ * @interface - FTDI interface to use (IFACE_A - IFACE_D).
+ * @description - Device product description (set to NULL if not needed).
+ * @serial - Device serial number (set to NULL if not needed).
+ * @index - Device index (set to 0 if not needed).
+ *
+ * Returns a pointer to an MPSSE context structure.
+ * On success, mpsse->open will be set to 1.
+ * On failure, mpsse->open will be set to 0.
+ */
+static struct mpsse_context *OpenIndex(int vid,
+ int pid,
+ int freq,
+ int endianness,
+ int interface,
+ const char *description,
+ const char *serial, int index)
+{
+ int status = 0;
+ struct mpsse_context *mpsse = NULL;
+ enum modes mode = SPI0; /* Let's use this mode at all times. */
+
+ mpsse = malloc(sizeof(struct mpsse_context));
+ if (!mpsse)
+ return NULL;
+
+ memset(mpsse, 0, sizeof(struct mpsse_context));
+
+ /* Legacy; flushing is no longer needed, so disable it by default. */
+ FlushAfterRead(mpsse, 0);
+
+ /* ftdilib initialization */
+ if (ftdi_init(&mpsse->ftdi)) {
+ fprintf(stderr, "%s:%d failed to initialize FTDI\n",
+ __func__, __LINE__);
+ free(mpsse);
+ return NULL;
+ }
+
+ mpsse->ftdi_initialized = 1;
+
+ /* Set the FTDI interface */
+ ftdi_set_interface(&mpsse->ftdi, interface);
+
+ /* Try opening the specified device */
+ if (ftdi_usb_open_desc_index
+ (&mpsse->ftdi, vid, pid, description, serial, index)) {
+ Close(mpsse);
+ return NULL;
+ }
+
+ mpsse->mode = mode;
+ mpsse->vid = vid;
+ mpsse->pid = pid;
+ mpsse->status = STOPPED;
+ mpsse->endianness = endianness;
+ mpsse->xsize = SPI_RW_SIZE;
+
+ status |= ftdi_usb_reset(&mpsse->ftdi);
+ status |= ftdi_set_latency_timer(&mpsse->ftdi, LATENCY_MS);
+ status |= ftdi_write_data_set_chunksize(&mpsse->ftdi, CHUNK_SIZE);
+ status |= ftdi_read_data_set_chunksize(&mpsse->ftdi, CHUNK_SIZE);
+ status |= ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_RESET);
+
+ if (status) {
+ fprintf(stderr,
+ "%s:%d failed setting basic config for %4.4x:%4.4x\n",
+ __func__, __LINE__, vid, pid);
+ Close(mpsse);
+ return NULL;
+ }
+ /* Set the read and write timeout periods */
+ set_timeouts(mpsse, USB_TIMEOUT);
+
+ ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_MPSSE);
+
+ if ((SetClock(mpsse, freq) != MPSSE_OK)
+ || (SetMode(mpsse, endianness) != MPSSE_OK)) {
+ fprintf(stderr,
+ "%s:%d failed setting clock/mode for %4.4x:%4.4x\n",
+ __func__, __LINE__, vid, pid);
+ Close(mpsse);
+ return NULL;
+ }
+
+ mpsse->open = 1;
+
+ /* Give the chip a few mS to initialize */
+ usleep(SETUP_DELAY);
+
+ /*
+ * Not all FTDI chips support all the commands that SetMode may have
+ * sent. This clears out any errors from unsupported commands that
+ * might have been sent during set up.
+ */
+ ftdi_usb_purge_buffers(&mpsse->ftdi);
+
+ return mpsse;
+}
+
+/*
+ * Opens and initializes the first FTDI device found.
+ *
+ * @freq - Clock frequency to use for the specified mode.
+ * @endianness - Specifies how data is clocked in/out (MSB, LSB).
+ * @serial - Serial number of the USB device (NULL if not needed).
+ *
+ * Returns a pointer to an MPSSE context structure.
+ * On success, mpsse->open will be set to 1.
+ * On failure, mpsse->open will be set to 0.
+ */
+struct mpsse_context *MPSSE(int freq, int endianness, const char *serial)
+{
+ int i = 0;
+ struct mpsse_context *mpsse = NULL;
+
+ for (i = 0; supported_devices[i].vid != 0; i++) {
+ mpsse = OpenIndex(supported_devices[i].vid,
+ supported_devices[i].pid, freq, endianness,
+ IFACE_A, NULL, serial, 0);
+ if (!mpsse)
+ continue;
+
+ if (mpsse->open) {
+ mpsse->description = supported_devices[i].description;
+ break;
+ }
+ /*
+ * If there is another device still left to try, free
+ * the context pointer and try again
+ */
+ if (supported_devices[i + 1].vid != 0) {
+ Close(mpsse);
+ mpsse = NULL;
+ }
+ }
+
+ return mpsse;
+}
+
+/*
+ * Closes the device, deinitializes libftdi, and frees the MPSSE context
+ * pointer.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns void.
+ */
+
+void Close(struct mpsse_context *mpsse)
+{
+ if (!mpsse)
+ return;
+
+ if (mpsse->open) {
+ ftdi_usb_close(&mpsse->ftdi);
+ ftdi_set_bitmode(&mpsse->ftdi, 0, BITMODE_RESET);
+ }
+
+ if (mpsse->ftdi_initialized)
+ ftdi_deinit(&mpsse->ftdi);
+
+ free(mpsse);
+}
+
+/*
+ * Retrieves the last error string from libftdi.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns a pointer to the last error string.
+ */
+const char *ErrorString(struct mpsse_context *mpsse)
+{
+ if (mpsse)
+ return ftdi_get_error_string(&mpsse->ftdi);
+
+ return NULL_CONTEXT_ERROR_MSG;
+}
+
+/*
+ * Send data start condition.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int Start(struct mpsse_context *mpsse)
+{
+ int status;
+
+ if (!is_valid_context(mpsse)) {
+ mpsse->status = STOPPED;
+ return MPSSE_FAIL;
+ }
+
+ /* Set the start condition */
+ status = set_bits_low(mpsse, mpsse->pstart);
+
+ if (status == MPSSE_OK)
+ mpsse->status = STARTED;
+
+ return status;
+}
+
+/*
+ * Send data out via the selected serial protocol.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @data - Buffer of data to send.
+ * @size - Size of data.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int Write(struct mpsse_context *mpsse, char *data, int size)
+{
+ int n = 0;
+
+ if (!is_valid_context(mpsse))
+ return MPSSE_FAIL;
+
+ if (!mpsse->mode)
+ return MPSSE_FAIL;
+
+ while (n < size) {
+ unsigned char *buf;
+ int retval, buf_size, txsize;
+
+ txsize = size - n;
+ if (txsize > mpsse->xsize)
+ txsize = mpsse->xsize;
+
+ buf = build_block_buffer(mpsse, mpsse->tx,
+ (unsigned char *)(data + n),
+ txsize, &buf_size);
+ if (!buf)
+ return MPSSE_FAIL;
+
+ retval = raw_write(mpsse, buf, buf_size);
+ n += txsize;
+ free(buf);
+
+ if (retval != MPSSE_OK)
+ return retval;
+
+ }
+
+ return MPSSE_OK;
+}
+
+/* Performs a read. For internal use only; see Read() and ReadBits(). */
+static char *InternalRead(struct mpsse_context *mpsse, int size)
+{
+ unsigned char *buf;
+ int n = 0;
+
+ if (!is_valid_context(mpsse))
+ return NULL;
+
+ if (!mpsse->mode)
+ return NULL;
+ buf = malloc(size);
+
+ if (!buf)
+ return NULL;
+
+ while (n < size) {
+ int rxsize, data_size, retval;
+ unsigned char *data;
+ unsigned char sbuf[SPI_RW_SIZE] = { 0 };
+
+ rxsize = size - n;
+ if (rxsize > mpsse->xsize)
+ rxsize = mpsse->xsize;
+
+ data = build_block_buffer(mpsse, mpsse->rx,
+ sbuf, rxsize, &data_size);
+ if (!data) {
+ free(buf);
+ return NULL;
+ }
+
+ retval = raw_write(mpsse, data, data_size);
+ free(data);
+
+ if (retval != MPSSE_OK) {
+ free(buf);
+ return NULL;
+ }
+ n += raw_read(mpsse, buf + n, rxsize);
+ }
+
+ return (char *)buf;
+}
+
+/*
+ * Reads data over the selected serial protocol.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @size - Number of bytes to read.
+ *
+ * Returns a pointer to the read data on success.
+ * Returns NULL on failure.
+ */
+char *Read(struct mpsse_context *mpsse, int size)
+{
+ char *buf = NULL;
+
+ buf = InternalRead(mpsse, size);
+ return buf;
+}
+
+/*
+ * Reads and writes data over the selected serial protocol (SPI only).
+ *
+ * @mpsse - MPSSE context pointer.
+ * @data - Buffer containing bytes to write.
+ * @size - Number of bytes to transfer.
+ *
+ * Returns a pointer to the read data on success.
+ * Returns NULL on failure.
+ */
+char *Transfer(struct mpsse_context *mpsse, char *data, int size)
+{
+ unsigned char *txdata = NULL, *buf = NULL;
+ int n = 0, data_size = 0, rxsize = 0, retval = MPSSE_OK;
+
+ if (!is_valid_context(mpsse))
+ return NULL;
+
+ buf = malloc(size);
+ if (!buf)
+ return NULL;
+
+ while (n < size) {
+ /*
+ * When sending and recieving, FTDI chips don't seem to like
+ * large data blocks. Limit the size of each block to
+ * SPI_TRANSFER_SIZE
+ */
+ rxsize = size - n;
+ if (rxsize > SPI_TRANSFER_SIZE)
+ rxsize = SPI_TRANSFER_SIZE;
+
+ txdata = build_block_buffer(mpsse, mpsse->txrx,
+ (unsigned char *)(data + n),
+ rxsize, &data_size);
+ if (!txdata) {
+ retval = MPSSE_FAIL;
+ break;
+ }
+ retval = raw_write(mpsse, txdata, data_size);
+ free(txdata);
+
+ if (retval != MPSSE_OK)
+ break;
+
+ n += raw_read(mpsse, (buf + n), rxsize);
+ }
+
+ if (retval != MPSSE_OK)
+ return NULL;
+
+ return (char *)buf;
+}
+
+/*
+ * Send data stop condition.
+ *
+ * @mpsse - MPSSE context pointer.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int Stop(struct mpsse_context *mpsse)
+{
+ int retval = MPSSE_OK;
+
+ if (is_valid_context(mpsse)) {
+ /* Send the stop condition */
+ retval |= set_bits_low(mpsse, mpsse->pstop);
+
+ if (retval == MPSSE_OK) {
+ /* Restore the pins to their idle states */
+ retval |= set_bits_low(mpsse, mpsse->pidle);
+ }
+
+ mpsse->status = STOPPED;
+ } else {
+ retval = MPSSE_FAIL;
+ mpsse->status = STOPPED;
+ }
+
+ return retval;
+}
+
+/*
+ * Sets the specified pin high.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @pin - Pin number to set high.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int PinHigh(struct mpsse_context *mpsse, int pin)
+{
+ int retval = MPSSE_FAIL;
+
+ if (is_valid_context(mpsse))
+ retval = gpio_write(mpsse, pin, HIGH);
+
+ return retval;
+}
+
+/*
+ * Sets the specified pin low.
+ *
+ * @mpsse - MPSSE context pointer.
+ * @pin - Pin number to set low.
+ *
+ * Returns MPSSE_OK on success.
+ * Returns MPSSE_FAIL on failure.
+ */
+int PinLow(struct mpsse_context *mpsse, int pin)
+{
+ int retval = MPSSE_FAIL;
+
+ if (is_valid_context(mpsse))
+ retval = gpio_write(mpsse, pin, LOW);
+
+ return retval;
+}
diff --git a/test/tpm_test/mpsse.h b/test/tpm_test/mpsse.h
new file mode 100644
index 0000000000..2925dfe27a
--- /dev/null
+++ b/test/tpm_test/mpsse.h
@@ -0,0 +1,45 @@
+/* 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.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ */
+
+#ifndef __EC_TEST_TPM_TEST_MPSSE_H
+#define __EC_TEST_TPM_TEST_MPSSE_H
+
+#define MPSSE_OK 0
+#define MPSSE_FAIL -1
+
+#define MSB 0x00
+#define LSB 0x08
+
+enum gpio_pins {
+ GPIOL0 = 0,
+ GPIOL1 = 1,
+ GPIOL2 = 2,
+ GPIOL3 = 3,
+ GPIOH0 = 4,
+ GPIOH1 = 5,
+ GPIOH2 = 6,
+ GPIOH3 = 7,
+ GPIOH4 = 8,
+ GPIOH5 = 9,
+ GPIOH6 = 10,
+ GPIOH7 = 11
+};
+
+struct mpsse_context;
+
+int Write(struct mpsse_context *mpsse, char *data, int size);
+int Stop(struct mpsse_context *mpsse);
+char *Transfer(struct mpsse_context *mpsse, char *data, int size);
+char *Read(struct mpsse_context *mpsse, int size);
+struct mpsse_context *MPSSE(int freq, int endianness, const char *serial);
+void Close(struct mpsse_context *mpsse);
+int PinHigh(struct mpsse_context *mpsse, int pin);
+int PinLow(struct mpsse_context *mpsse, int pin);
+int Start(struct mpsse_context *mpsse);
+
+#endif /* ! __EC_TEST_TPM_TEST_MPSSE_H */
diff --git a/test/tpm_test/support.c b/test/tpm_test/support.c
new file mode 100644
index 0000000000..fbb4d0b6ab
--- /dev/null
+++ b/test/tpm_test/support.c
@@ -0,0 +1,218 @@
+/* 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.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ *
+ * Internal functions used by libmpsse.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#if LIBFTDI1 == 1
+#include <libftdi1/ftdi.h>
+#else
+#include <ftdi.h>
+#endif
+
+#include "support.h"
+
+/* Write data to the FTDI chip */
+int raw_write(struct mpsse_context *mpsse, unsigned char *buf, int size)
+{
+ int retval = MPSSE_FAIL;
+
+ if (mpsse->mode && (ftdi_write_data(&mpsse->ftdi, buf, size) == size))
+ retval = MPSSE_OK;
+
+ return retval;
+}
+
+/* Read data from the FTDI chip */
+int raw_read(struct mpsse_context *mpsse, unsigned char *buf, int size)
+{
+ int n = 0, r = 0;
+
+ if (!mpsse->mode)
+ return 0;
+
+ while (n < size) {
+ r = ftdi_read_data(&mpsse->ftdi, buf, size);
+ if (r < 0)
+ break;
+ n += r;
+ }
+
+ if (mpsse->flush_after_read) {
+ /*
+ * Make sure the buffers are cleared after a read or
+ * subsequent reads may fail. Is this needed anymore?
+ * It slows down repetitive read operations by ~8%.
+ */
+ ftdi_usb_purge_rx_buffer(&mpsse->ftdi);
+ }
+
+ return n;
+}
+
+/* Sets the read and write timeout periods for bulk usb data transfers. */
+void set_timeouts(struct mpsse_context *mpsse, int timeout)
+{
+ if (mpsse->mode) {
+ mpsse->ftdi.usb_read_timeout = timeout;
+ mpsse->ftdi.usb_write_timeout = timeout;
+ }
+}
+
+/* Convert a frequency to a clock divisor */
+uint16_t freq2div(uint32_t system_clock, uint32_t freq)
+{
+ return (((system_clock / freq) / 2) - 1);
+}
+
+/* Convert a clock divisor to a frequency */
+uint32_t div2freq(uint32_t system_clock, uint16_t div)
+{
+ return (system_clock / ((1 + div) * 2));
+}
+
+/* Builds a buffer of commands + data blocks */
+unsigned char *build_block_buffer(struct mpsse_context *mpsse,
+ uint8_t cmd,
+ unsigned char *data, int size, int *buf_size)
+{
+ unsigned char *buf = NULL;
+ int i = 0, j = 0, k = 0, dsize = 0, num_blocks = 0, total_size =
+ 0, xfer_size = 0;
+ uint16_t rsize = 0;
+
+ *buf_size = 0;
+
+ /* Data block size is 1 in I2C, or when in bitmode */
+ if (mpsse->mode == I2C || (cmd & MPSSE_BITMODE))
+ xfer_size = 1;
+ else
+ xfer_size = mpsse->xsize;
+
+ num_blocks = (size / xfer_size);
+ if (size % xfer_size)
+ num_blocks++;
+
+ /*
+ * The total size of the data will be the data size + the write
+ * command
+ */
+ total_size = size + (CMD_SIZE * num_blocks);
+
+ buf = malloc(total_size);
+ if (!buf)
+ return NULL;
+
+ memset(buf, 0, total_size);
+
+ for (j = 0; j < num_blocks; j++) {
+ dsize = size - k;
+ if (dsize > xfer_size)
+ dsize = xfer_size;
+
+ /* The reported size of this block is block size - 1 */
+ rsize = dsize - 1;
+
+ /* Copy in the command for this block */
+ buf[i++] = cmd;
+ buf[i++] = (rsize & 0xFF);
+ if (!(cmd & MPSSE_BITMODE))
+ buf[i++] = ((rsize >> 8) & 0xFF);
+
+ /* On a write, copy the data to transmit after the command */
+ if (cmd == mpsse->tx || cmd == mpsse->txrx) {
+
+ memcpy(buf + i, data + k, dsize);
+
+ /* i == offset into buf */
+ i += dsize;
+ /* k == offset into data */
+ k += dsize;
+ }
+ }
+
+ *buf_size = i;
+
+ return buf;
+}
+
+/* Set the low bit pins high/low */
+int set_bits_low(struct mpsse_context *mpsse, int port)
+{
+ char buf[CMD_SIZE] = { 0 };
+
+ buf[0] = SET_BITS_LOW;
+ buf[1] = port;
+ buf[2] = mpsse->tris;
+
+ return raw_write(mpsse, (unsigned char *)&buf, sizeof(buf));
+}
+
+/* Set the high bit pins high/low */
+int set_bits_high(struct mpsse_context *mpsse, int port)
+{
+ char buf[CMD_SIZE] = { 0 };
+
+ buf[0] = SET_BITS_HIGH;
+ buf[1] = port;
+ buf[2] = mpsse->trish;
+
+ return raw_write(mpsse, (unsigned char *)&buf, sizeof(buf));
+}
+
+/* Set the GPIO pins high/low */
+int gpio_write(struct mpsse_context *mpsse, int pin, int direction)
+{
+ int retval = MPSSE_FAIL;
+
+ /*
+ * The first four pins can't be changed unless we are in a stopped
+ * status
+ */
+ if (pin < NUM_GPIOL_PINS && mpsse->status == STOPPED) {
+ /* Convert pin number (0-3) to the corresponding pin bit */
+ pin = (GPIO0 << pin);
+
+ if (direction == HIGH) {
+ mpsse->pstart |= pin;
+ mpsse->pidle |= pin;
+ mpsse->pstop |= pin;
+ } else {
+ mpsse->pstart &= ~pin;
+ mpsse->pidle &= ~pin;
+ mpsse->pstop &= ~pin;
+ }
+
+ retval = set_bits_low(mpsse, mpsse->pstop);
+ } else if (pin >= NUM_GPIOL_PINS && pin < NUM_GPIO_PINS) {
+ /* Convert pin number (4 - 11) to the corresponding pin bit */
+ pin -= NUM_GPIOL_PINS;
+
+ if (direction == HIGH)
+ mpsse->gpioh |= (1 << pin);
+ else
+ mpsse->gpioh &= ~(1 << pin);
+
+ retval = set_bits_high(mpsse, mpsse->gpioh);
+ }
+
+ return retval;
+}
+
+/* Checks if a given MPSSE context is valid. */
+int is_valid_context(struct mpsse_context *mpsse)
+{
+ int retval = 0;
+
+ if (mpsse != NULL && mpsse->open)
+ retval = 1;
+
+ return retval;
+}
diff --git a/test/tpm_test/support.h b/test/tpm_test/support.h
new file mode 100644
index 0000000000..77316582cc
--- /dev/null
+++ b/test/tpm_test/support.h
@@ -0,0 +1,87 @@
+/* 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.
+ *
+ * Based on Craig Heffner's version of Dec 27 2011, published on
+ * https://github.com/devttys0/libmpsse
+ */
+
+#ifndef __EC_TEST_TPM_TEST_SUPPORT_H
+#define __EC_TEST_TPM_TEST_SUPPORT_H
+
+#include "mpsse.h"
+
+#define CMD_SIZE 3
+#define NUM_GPIOL_PINS 4
+#define NUM_GPIO_PINS 12
+#define LOW 0
+#define HIGH 1
+
+/* Supported MPSSE modes */
+enum modes {
+ SPI0 = 1,
+ SPI1 = 2,
+ SPI2 = 3,
+ SPI3 = 4,
+ I2C = 5,
+ GPIO = 6,
+ BITBANG = 7,
+};
+
+enum low_bits_status {
+ STARTED,
+ STOPPED
+};
+
+enum pins {
+ SK = 1,
+ DO = 2,
+ DI = 4,
+ CS = 8,
+ GPIO0 = 16,
+ GPIO1 = 32,
+ GPIO2 = 64,
+ GPIO3 = 128
+};
+
+struct mpsse_context {
+ char *description;
+ struct ftdi_context ftdi;
+ enum modes mode;
+ enum low_bits_status status;
+ int flush_after_read;
+ int vid;
+ int pid;
+ int clock;
+ int xsize;
+ int open;
+ int ftdi_initialized;
+ int endianness;
+ uint8_t tris;
+ uint8_t pstart;
+ uint8_t pstop;
+ uint8_t pidle;
+ uint8_t gpioh;
+ uint8_t trish;
+ uint8_t bitbang;
+ uint8_t tx;
+ uint8_t rx;
+ uint8_t txrx;
+ uint8_t tack;
+ uint8_t rack;
+};
+
+int raw_write(struct mpsse_context *mpsse, unsigned char *buf, int size);
+int raw_read(struct mpsse_context *mpsse, unsigned char *buf, int size);
+void set_timeouts(struct mpsse_context *mpsse, int timeout);
+uint16_t freq2div(uint32_t system_clock, uint32_t freq);
+uint32_t div2freq(uint32_t system_clock, uint16_t div);
+unsigned char *build_block_buffer(struct mpsse_context *mpsse,
+ uint8_t cmd,
+ unsigned char *data, int size, int *buf_size);
+int set_bits_high(struct mpsse_context *mpsse, int port);
+int set_bits_low(struct mpsse_context *mpsse, int port);
+int gpio_write(struct mpsse_context *mpsse, int pin, int direction);
+int is_valid_context(struct mpsse_context *mpsse);
+
+#endif /* ! __EC_TEST_TPM_TEST_SUPPORT_H */
diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py
new file mode 100755
index 0000000000..c2056c01c7
--- /dev/null
+++ b/test/tpm_test/tpmtest.py
@@ -0,0 +1,364 @@
+#!/usr/bin/python
+# 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 testing TPM, using both conventional and extended commands."""
+
+from __future__ import print_function
+
+import os
+import struct
+import sys
+import traceback
+import xml.etree.ElementTree as ET
+
+# 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'))
+import ftdi_spi_tpm
+
+# Basic crypto operations
+DECRYPT = 0
+ENCRYPT = 1
+
+# Extension command for dcypto testing
+EXT_CMD = 0xbaccd00a
+
+# Extension subcommands for encryption types
+AES = 0
+
+if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
+ cursor_back = '\x1b[1D' # Move one space to the left.
+else:
+ cursor_back = ''
+
+
+class TpmError(Exception):
+ pass
+
+
+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._handle = ftdi_spi_tpm
+ if not self._handle.FtdiSpiInit(freq, debug_mode):
+ raise TpmError()
+ response = self.command(''.join('%c' % int('0x%s' % x, 16)
+ for x in self.STARTUP_CMD.split()))
+ if ' '.join('%2.2x' % ord(x) for x in response) not in self.STARTUP_RSP:
+ raise TpmError('init failed')
+
+ 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 TpmError(prefix + 'bad tag value 0x%4.4x' % tag)
+ if size != len(data_blob):
+ raise TpmError(prefix + 'size mismatch: header %d, actual %d' %
+ (size, len(data_blob)))
+ if size > 4096:
+ raise TpmError(prefix + 'invalid size %d' % size)
+ if response_mode:
+ return
+ 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
+
+ raise TpmError(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, cmd_code, subcmd_code, cmd_body):
+ return struct.pack(self.HEADER_FMT, 0x8001,
+ len(cmd_body) + struct.calcsize(self.HEADER_FMT),
+ cmd_code, subcmd_code) + cmd_body
+
+ def unwrap_ext_response(self, expected_cmd, 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_cmd: an int, up to 32 bits, expected value in the
+ command/response field of the header. expected_subcmd, response
+ 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:
+ TpmError: in case there are any validation problems, the error message
+ describes the problem.
+ """
+ header_size = struct.calcsize(self.HEADER_FMT)
+ tag, size, cmd, subcmd = struct.unpack(self.HEADER_FMT,
+ response[:header_size])
+ if tag != 0x8001:
+ raise TpmError('Wrong response tag: %4.4x' % tag)
+ if cmd != expected_cmd:
+ raise TpmError('Unexpected response command field: %8.8x' % cmd)
+ if subcmd != expected_subcmd:
+ raise TpmError('Unexpected response subcommand field: %2.2x' %
+ subcmd)
+ if size != len(response):
+ raise TpmError('Size mismatch: header %d, actual %d' % (
+ size, len(response)))
+ return response[header_size:]
+
+
+def hex_dump(binstr):
+ """Convert string into its hex representation."""
+ dump_lines = ['',]
+ i = 0
+ while i < len(binstr):
+ strsize = min(16, len(binstr) - i)
+ hexstr = ' '.join('%2.2x' % ord(x) for x in binstr[i:i+strsize])
+ dump_lines.append(hexstr)
+ i += strsize
+ dump_lines.append('')
+ return '\n'.join(dump_lines)
+
+
+def get_attribute(tdesc, attr_name, required=True):
+ """Retrieve an attribute value from an XML node.
+
+ Args:
+
+ tdesc: an Element of the ElementTree, a test descriptor containing
+ necessary information to run a single encryption/description
+ session.
+ attr_name: a string, the name of the attribute to retrieve.
+ required: a Boolean, if True - the attribute must be present in the
+ descriptor, otherwise it is considered optional
+ Returns:
+ The attribute value as a string (ascii or binary)
+ Raises:
+ TpmError: on various format errors, or in case a required attribute is not
+ found, the error message describes the problem.
+
+ """
+ # Fields stored in hex format by default.
+ default_hex = ('cipher_text', 'iv', 'key')
+
+ data = tdesc.find(attr_name)
+ if data is None:
+ if required:
+ raise TpmError('node "%s" does not have attribute "%s"' %
+ (tdesc.get('name'), attr_name))
+ return ''
+
+ # Attribute is present, does it have to be decoded from hex?
+ cell_format = data.get('format')
+ if not cell_format:
+ if attr_name in default_hex:
+ cell_format = 'hex'
+ else:
+ cell_format = 'ascii'
+ elif cell_format not in ('hex', 'ascii'):
+ raise TpmError('%s:%s, unrecognizable format "%s"' %
+ (tdesc.get('name'), attr_name, cell_format))
+
+ text = ' '.join(x.strip() for x in data.text.splitlines() if x)
+ if cell_format == 'ascii':
+ return text
+
+ # Drop spaces from hex representation.
+ text = text.replace(' ', '')
+ if len(text) & 3:
+ raise TpmError('%s:%s %swrong hex number size' %
+ (tdesc.get('name'), attr_name, hex_dump(text)))
+ # Convert text to binary
+ value = ''
+ for x in range(len(text)/8):
+ try:
+ value += struct.pack('<I', int('0x%s' % text[8*x:8*(x+1)], 16))
+ except ValueError:
+ raise TpmError('%s:%s %swrong hex value' %
+ (tdesc.get('name'), attr_name, hex_dump(text)))
+ return value
+
+
+class CryptoD(object):
+ """A helper object to contain an encryption scheme description.
+
+ Attributes:
+ subcmd: a 16 bit max integer, the extension subcommand to be used with
+ this encryption scheme.
+ sumbodes: an optional dictionary, the keys are strings, names of the
+ encryption scheme submodes, the values are integers to be included in
+ the appropriate subcommand fields to communicat the submode to the
+ device.
+ """
+
+ def __init__(self, subcommand, submodes=None):
+ self.subcmd = subcommand
+ if not submodes:
+ submodes = {}
+ self.submodes = submodes
+
+SUPPORTED_MODES = {
+ 'AES': CryptoD(AES, {
+ 'ECB': 0,
+ 'CTR': 1,
+ 'CBC': 2,
+ 'GCM': 3
+ }),
+}
+
+
+def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm, debug_mode):
+ """Perform a basic operation(encrypt or decrypt).
+
+ This function creates an extended command with the requested parameters,
+ sends it to the device, and then compares the response to the expected
+ value.
+
+ Args:
+ node_name: a string, the name of the XML node this data comes from. The
+ format of the name is "<enc type>:<submode> ....", where <enc type> is
+ the major encryption mode (say AED or DES) and submode - a variant of
+ the major scheme, if exists.
+
+ op_type: an int, encodes the operation to perform (encrypt/decrypt), passed
+ directly to the device as a field in the extended command
+ key: a binary string
+ iv: a binary string, might be empty
+ in_text: a binary string, the input of the encrypt/decrypt operation
+ out_text: a binary string, might be empty, the expected output of the
+ operation. Note that it could be shorter than actual output (padded to
+ integer number of blocks), in which case only its length of bytes is
+ compared debug_mode: a Boolean, if True - enables tracing on the console
+ tpm: a TPM object to send extended commands to an initialized TPM
+ debug_mode: a Boolean, if True - this function and the FTDI driver
+ generate debug messated on the console.
+
+ Returns:
+ The actual binary string, result of the operation, if the
+ comparison with the expected value was successful.
+
+ Raises:
+ TpmError: in case there were problems parsing the node name, or verifying
+ the operation results.
+ """
+ mode_name, submode_name = node_name.split(':')
+ submode_name = submode_name[:3].upper()
+
+ mode = SUPPORTED_MODES.get(mode_name.upper())
+ if not mode:
+ raise TpmError('unrecognizable mode in node "%s"' % node_name)
+
+ submode = mode.submodes.get(submode_name, 0)
+ cmd = '%c' % op_type # Encrypt or decrypt
+ cmd += '%c' % submode # A particular type of a generic algorithm.
+ cmd += '%c' % len(key)
+ cmd += key
+ cmd += '%c' % len(iv)
+ if iv:
+ cmd += iv
+ cmd += struct.pack('>H', len(in_text))
+ cmd += in_text
+ if debug_mode:
+ print('%d:%d cmd size' % (op_type, mode.subcmd), len(cmd), hex_dump(cmd))
+ wrapped_response = tpm.command(tpm.wrap_ext_command(
+ EXT_CMD, mode.subcmd, cmd))
+ real_out_text = tpm.unwrap_ext_response(
+ EXT_CMD, mode.subcmd, wrapped_response)
+ if out_text:
+ if len(real_out_text) > len(out_text):
+ real_out_text = real_out_text[:len(out_text)] # Ignore padding
+ if real_out_text != out_text:
+ if debug_mode:
+ print('Out text mismatch in node %s:\n' % node_name)
+ else:
+ raise TpmError('Out text mismatch in node %s, operation %d:\n'
+ 'In text:%sExpected out text:%sReal out text:%s' % (
+ node_name, op_type,
+ hex_dump(in_text),
+ hex_dump(out_text),
+ hex_dump(real_out_text)))
+ return real_out_text
+
+
+def crypto_test(tdesc, tpm, debug_mode):
+ """Perform a single test described in the xml file.
+
+ The xml node contains all pertinent information about the test inputs and
+ outputs.
+
+ Args:
+ tdesc: an Element of the ElementTree, a test descriptor containing
+ necessary information to run a single encryption/description
+ session.
+ tpm: a TPM object to send extended commands to an initialized TPM
+ debug_mode: a Boolean, if True - this function and the FTDI driver
+ generate debug messated on the console.
+ Raises:
+ TpmError: on various execution errors, the details are included in the
+ error message.
+ """
+ node_name = tdesc.get('name')
+ key = get_attribute(tdesc, 'key')
+ if len(key) not in (16, 24, 32):
+ raise TpmError('wrong key size "%s:%s"' % (
+ node_name,
+ ''.join('%2.2x' % ord(x) for x in key)))
+ iv = get_attribute(tdesc, 'iv', required=False)
+ if iv and len(iv) != 16:
+ raise TpmError('wrong iv size "%s:%s"' % (
+ node_name,
+ ''.join('%2.2x' % ord(x) for x in iv)))
+ clear_text = get_attribute(tdesc, 'clear_text')
+ if debug_mode:
+ print('clear text size', len(clear_text))
+ cipher_text = get_attribute(tdesc, 'cipher_text', required=False)
+ real_cipher_text = crypto_run(node_name, ENCRYPT, key, iv,
+ clear_text, cipher_text, tpm, debug_mode)
+ crypto_run(node_name, DECRYPT, key, iv, real_cipher_text,
+ clear_text, tpm, debug_mode)
+ print(cursor_back + 'SUCCESS: %s' % node_name)
+
+
+if __name__ == '__main__':
+ tree = ET.parse(os.path.join(root_dir, 'crypto_test.xml'))
+ root = tree.getroot()
+ try:
+ debug_needed = len(sys.argv) == 2 and sys.argv[1] == '-d'
+ t = TPM(debug_mode=debug_needed)
+
+ for child in root:
+ crypto_test(child, t, debug_needed)
+ except TpmError as e:
+ print()
+ print(e)
+ if debug_needed:
+ traceback.print_exc()
+ sys.exit(1)