From e97057783ec37f5f077fded1704d0bde606d961c Mon Sep 17 00:00:00 2001 From: Bill Roberts Date: Sat, 13 Jan 2024 19:51:50 -0600 Subject: [PATCH] [RFC] tcti/py: initial commit Implmenet a TCTI module that will allow folks to write TCTIs in Python3. This will allow for more crazy and complex TCTI scenarios without having to re-write everyones stacks to use tpm2-pytss. TODO: 1. Fix failing unit tests Example Commandline: ```bash PYTHONPATH=$HOME tpm2_getcap --verbose --tcti=py:pytctitabrmd properties-fixed ``` Example TCTI: For the ultimate in mind-bending: C -> Python -> C and back for efficiency :-p. ```python3 from tpm2_pytss import TCTILdr class MyPyTCTI(object): def __init__(self, args: str): c = args.split(":", maxsplit=1) print(f"PYTHON: Initializing TCTI Ldr with mod: {c[0]} args: {c[1]}") self._tcti = TCTILdr(c[0], c[1]) @property def magic(self): # Optional Method print("PYHTON magic") return 42 def receive(self, timeout: int) -> bytes: print("PYTHON receive") return self._tcti.receive(timeout=timeout) def transmit(self, data:bytes): print("PYTHON transmit") self._tcti.transmit(data) def tcti_init(args: str) -> MyPyTCTI: print(f"PYTHON tcti_init called with: {args}") return MyPyTCTI(args) ``` Signed-off-by: Bill Roberts --- Makefile-test.am | 12 + Makefile.am | 24 ++ configure.ac | 20 +- doc/tcti-py.md | 63 +++++ include/tss2/tss2_tcti_py.h | 20 ++ lib/tss2-tcti-pc.def | 4 + lib/tss2-tcti-pc.map | 7 + lib/tss2-tcti-py.def | 4 + lib/tss2-tcti-py.map | 7 + lib/tss2-tcti-py.pc.in | 11 + man/tss2-tcti-py.7.in | 14 + src/tss2-tcti/tcti-py.c | 527 ++++++++++++++++++++++++++++++++++++ src/tss2-tcti/tcti-py.h | 36 +++ src/tss2-tcti/tctildr-dl.c | 2 +- test/helper/pytcti.py | 30 ++ test/unit/tcti-py.c | 221 +++++++++++++++ 16 files changed, 1000 insertions(+), 2 deletions(-) create mode 100644 doc/tcti-py.md create mode 100644 include/tss2/tss2_tcti_py.h create mode 100644 lib/tss2-tcti-pc.def create mode 100644 lib/tss2-tcti-pc.map create mode 100644 lib/tss2-tcti-py.def create mode 100644 lib/tss2-tcti-py.map create mode 100644 lib/tss2-tcti-py.pc.in create mode 100644 man/tss2-tcti-py.7.in create mode 100644 src/tss2-tcti/tcti-py.c create mode 100644 src/tss2-tcti/tcti-py.h create mode 100644 test/helper/pytcti.py create mode 100644 test/unit/tcti-py.c diff --git a/Makefile-test.am b/Makefile-test.am index 5f2478aad..0766177ab 100644 --- a/Makefile-test.am +++ b/Makefile-test.am @@ -139,6 +139,9 @@ endif if ENABLE_TCTI_I2C_FTDI TESTS_UNIT += test/unit/tcti-i2c-ftdi endif +if ENABLE_TCTI_PY +TESTS_UNIT += test/unit/tcti-py +endif if ESYS TESTS_UNIT += \ test/unit/esys-context-null \ @@ -599,6 +602,15 @@ test_unit_tcti_i2c_ftdi_SOURCES = test/unit/tcti-i2c-ftdi.c \ src/tss2-tcti/tcti-i2c-ftdi.c endif +if ENABLE_TCTI_PY +test_unit_tcti_py_CFLAGS = $(CMOCKA_CFLAGS) $(TESTS_CFLAGS) $(PYTHON3_CFLAGS) +test_unit_tcti_py_LDFLAGS = $(PYTHON3_LIBS) -ldl -lpthread -ltss2-mu -ltss2-esys +test_unit_tcti_py_LDADD = $(CMOCKA_LIBS) $(test_unit_log_LDADD) +test_unit_tcti_py_SOURCES = test/unit/tcti-py.c \ + src/tss2-tcti/tcti-py.c \ + src/tss2-tcti/tcti-common.c +endif + test_unit_tctildr_CFLAGS = $(CMOCKA_CFLAGS) $(TESTS_CFLAGS) test_unit_tctildr_LDADD = $(CMOCKA_LIBS) $(libutil) test_unit_tctildr_LDFLAGS = -Wl,--wrap=calloc,--wrap=free \ diff --git a/Makefile.am b/Makefile.am index af0d83347..f3a48339e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -541,6 +541,28 @@ src_tss2_tcti_libtss2_tcti_i2c_ftdi_la_SOURCES = \ src/tss2-tcti/tcti-i2c-ftdi.h endif # ENABLE_TCTI_I2C_FTDI +# tcti library for python implemented TCTIs +if ENABLE_TCTI_PY +libtss2_tcti_py = src/tss2-tcti/libtss2-tcti-py.la +tss2_HEADERS += $(srcdir)/include/tss2/tss2_tcti_py.h +lib_LTLIBRARIES += $(libtss2_tcti_py) +pkgconfig_DATA += lib/tss2-tcti-py.pc +EXTRA_DIST += lib/tss2-tcti-py.map \ + lib/tss2-tcti-py.def + +src_tss2_tcti_libtss2_tcti_py_la_CFLAGS = $(AM_CFLAGS) $(PYTHON3_CFLAGS) -Wno-deprecated-declarations +src_tss2_tcti_libtss2_tcti_py_la_LDFLAGS = -ldl -lpthread $(PYTHON3_LIBS) +if HAVE_LD_VERSION_SCRIPT +src_tss2_tcti_libtss2_tcti_py_la_LDFLAGS += -Wl,--version-script=$(srcdir)/lib/tss2-tcti-py.map +endif # HAVE_LD_VERSION_SCRIPT +src_tss2_tcti_libtss2_tcti_py_la_LIBADD = $(libutil) +src_tss2_tcti_libtss2_tcti_py_la_SOURCES = \ + src/tss2-tcti/tcti-common.c \ + src/tss2-tcti/tcti-py.c \ + src/tss2-tcti/tcti-py.h +endif # ENABLE_TCTI_PY + + ### TCG TSS SYS spec library ### libtss2_sys = src/tss2-sys/libtss2-sys.la tss2_HEADERS += $(srcdir)/include/tss2/tss2_sys.h @@ -936,6 +958,7 @@ man7_MANS = \ man/man7/tss2-tcti-spi-ftdi.7 \ man/man7/tss2-tcti-i2c-helper.7 \ man/man7/tss2-tcti-i2c-ftdi.7 \ + man/man7/tss2-tcti-py.7 \ man/man7/tss2-tctildr.7 if FAPI @@ -1033,6 +1056,7 @@ EXTRA_DIST += \ man/tss2-tcti-spi-ftdi.7.in \ man/tss2-tcti-i2c-helper.7.in \ man/tss2-tcti-i2c-ftdi.7.in \ + man/tss2-tcti-py.7.in \ man/tss2-tctildr.7.in CLEANFILES += \ diff --git a/configure.ac b/configure.ac index 4250802ed..d21a80367 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,8 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) #Backward compatible setti AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_FILES([Makefile Doxyfile lib/tss2-sys.pc lib/tss2-esys.pc lib/tss2-mu.pc lib/tss2-tcti-device.pc lib/tss2-tcti-mssim.pc lib/tss2-tcti-swtpm.pc lib/tss2-tcti-pcap.pc lib/tss2-tcti-libtpms.pc lib/tss2-rc.pc lib/tss2-tctildr.pc lib/tss2-fapi.pc lib/tss2-tcti-cmd.pc lib/tss2-policy.pc lib/tss2-tcti-spi-helper.pc lib/tss2-tcti-spi-ltt2go.pc lib/tss2-tcti-spidev.pc lib/tss2-tcti-spi-ftdi.pc lib/tss2-tcti-i2c-helper.pc lib/tss2-tcti-i2c-ftdi.pc]) +AC_CONFIG_FILES([Makefile Doxyfile lib/tss2-sys.pc lib/tss2-esys.pc lib/tss2-mu.pc lib/tss2-tcti-device.pc lib/tss2-tcti-mssim.pc lib/tss2-tcti-swtpm.pc lib/tss2-tcti-pcap.pc lib/tss2-tcti-libtpms.pc lib/tss2-rc.pc lib/tss2-tctildr.pc lib/tss2-fapi.pc lib/tss2-tcti-cmd.pc lib/tss2-policy.pc lib/tss2-tcti-spi-helper.pc lib/tss2-tcti-spi-ltt2go.pc lib/tss2-tcti-spidev.pc lib/tss2-tcti-spi-ftdi.pc lib/tss2-tcti-i2c-helper.pc lib/tss2-tcti-i2c-ftdi.pc +lib/tss2-tcti-py.pc]) # propagate configure arguments to distcheck AC_SUBST([DISTCHECK_CONFIGURE_FLAGS],[$ac_configure_args]) @@ -390,6 +391,22 @@ AM_CONDITIONAL([ENABLE_TCTI_I2C_FTDI], [test "x$enable_tcti_i2c_ftdi" != xno]) AS_IF([test "x$enable_tcti_i2c_ftdi" = "xyes"], AC_DEFINE([TCTI_I2C_FTDI],[1], [TCTI FOR USB BASED ACCESS TO I2C BASED TPM OVER THE FTDI MPSSE USB TO I2C BRIDGE])) +AC_ARG_ENABLE([tcti-py], + [AS_HELP_STRING([--enable-tcti-py], + [build the tcti-py module])],, + [enable_tcti_py=no]) + +AM_CONDITIONAL([ENABLE_TCTI_PY], [test "x$enable_tcti_py" != xno]) +AS_IF([test "x$enable_tcti_py" = "xyes"], + [AC_DEFINE([TCTI_PY],[1], [TCTI FOR PYTHON3 BASED TCTI IMPLEMENTATIONS]) + PKG_CHECK_MODULES([PYTHON3], [python3]) + PYTHON3_LIBS=$(python3-config --ldflags --embed) +]) + +# If building tcti-py with unit tests you need ESAPI +AS_IF([test "x$enable_tcti_py" = "xyes" -a "x$enable_unit" = "xyes" -a "x$enable_esys" != "xyes" ], + [AC_MSG_ERROR(["Must enable esys with tss2-py-tcti])]) + AC_ARG_ENABLE([tcti-fuzzing], [AS_HELP_STRING([--enable-tcti-fuzzing], [build the tcti-fuzzing module])],, @@ -754,4 +771,5 @@ AC_MSG_RESULT([ tcti_spidev $enable_tcti_spidev tcti_spi_ftdi $enable_tcti_spi_ftdi tcti_i2c_ftdi $enable_tcti_i2c_ftdi + tcti_py $enable_tcti_py ]) diff --git a/doc/tcti-py.md b/doc/tcti-py.md new file mode 100644 index 000000000..e74c01da7 --- /dev/null +++ b/doc/tcti-py.md @@ -0,0 +1,63 @@ +# Python TCTI (py-tcti) + +The Python TCTI can be used to write TCTI modules in Python3. This allows a user to make use of the +robust language features and modules that are available in Python3. + +## Invoking the Python TCTI + +Like implementing any TCTI, one can follow the friendly conventions or provide the full path of the shared +object to `Tss2_TCTILdr_Initialize` or one can instantiate the TCTI directly with `TSS2_TCTI_Py_Initialize`. + +One needs to specify a module name (the name of a python file) and optionally arguments to pass to an init +function in that module. The signature of this method is: `def tcti_init(args: str) -> Object` and the +args string passed in is the args option appended on the configuration string. + +One possible way to use it with the command line tools is via the `--tcti=py::`. For +example: +```bash +# Python3 file pytcti.py exists in $HOME +PYTHONPATH=$HOME tpm2_getcap --tcti=py:pytcti properties-fixed +``` + +## Example Python TCTI + +The below sample code TCTI just uses tpm2-pytss package to call TCTILdr with whatever +argument string is provided. It just showcases full path delivery of commands. To invoke +the example code below, assuming that the python file is named pytcti.py and you want +to connect to tpm2-abrmd resource manager do: +```bash +PYTHONPATH=$HOME tpm2_getcap --tcti=py:pytcti:tabrmd properties-fixed +``` + +```python3 +# SPDX-License-Identifier: BSD-2-Clause +from tpm2_pytss import TCTILdr + + +class MyPyTCTI(object): + def __init__(self, args: str): + c = args.split(":", maxsplit=1) + mod = c[0] + args = c[1] if len(c) > 1 else "None" + print(f"PYTHON: Initializing TCTI Ldr with mod: {mod} args: {args}") + self._tcti = TCTILdr(mod, args) + + @property + def magic(self): + # Optional Method + print("PYTHON magic") + return 42 + + def receive(self, timeout: int) -> bytes: + print("PYTHON receive") + return self._tcti.receive(timeout=timeout) + + def transmit(self, data: bytes): + print("PYTHON transmit") + self._tcti.transmit(data) + + +def tcti_init(args: str) -> MyPyTCTI: + print(f"PYTHON tcti_init called with: {args}") + return MyPyTCTI(args) +``` diff --git a/include/tss2/tss2_tcti_py.h b/include/tss2/tss2_tcti_py.h new file mode 100644 index 000000000..5c0b2f92c --- /dev/null +++ b/include/tss2/tss2_tcti_py.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +#ifndef TSS2_TCTI_CMD_H +#define TSS2_TCTI_CMD_H + +#include "tss2_tcti.h" + +#ifdef __cplusplus +extern "C" { +#endif + +TSS2_RC Tss2_Tcti_Py_Init ( + TSS2_TCTI_CONTEXT *tctiContext, + size_t *size, + const char *conf); + +#ifdef __cplusplus +} +#endif + +#endif /* TSS2_TCTI_CMD_H */ diff --git a/lib/tss2-tcti-pc.def b/lib/tss2-tcti-pc.def new file mode 100644 index 000000000..4e01b5d4e --- /dev/null +++ b/lib/tss2-tcti-pc.def @@ -0,0 +1,4 @@ +LIBRARY tss2-tcti-py +EXPORTS + Tss2_Tcti_Info + Tss2_Tcti_Py_Init diff --git a/lib/tss2-tcti-pc.map b/lib/tss2-tcti-pc.map new file mode 100644 index 000000000..2ed89e768 --- /dev/null +++ b/lib/tss2-tcti-pc.map @@ -0,0 +1,7 @@ +{ + global: + Tss2_Tcti_Info; + Tss2_Tcti_Py_Init; + local: + *; +}; diff --git a/lib/tss2-tcti-py.def b/lib/tss2-tcti-py.def new file mode 100644 index 000000000..4e01b5d4e --- /dev/null +++ b/lib/tss2-tcti-py.def @@ -0,0 +1,4 @@ +LIBRARY tss2-tcti-py +EXPORTS + Tss2_Tcti_Info + Tss2_Tcti_Py_Init diff --git a/lib/tss2-tcti-py.map b/lib/tss2-tcti-py.map new file mode 100644 index 000000000..2ed89e768 --- /dev/null +++ b/lib/tss2-tcti-py.map @@ -0,0 +1,7 @@ +{ + global: + Tss2_Tcti_Info; + Tss2_Tcti_Py_Init; + local: + *; +}; diff --git a/lib/tss2-tcti-py.pc.in b/lib/tss2-tcti-py.pc.in new file mode 100644 index 000000000..cc173c681 --- /dev/null +++ b/lib/tss2-tcti-py.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: tss2-tcti-py +Description: TCTI library for implementing TCTIs in Python3. +URL: https://github.com/tpm2-software/tpm2-tss +Version: @VERSION@ +Cflags: -I${includedir} -I${includedir}/tss2 +Libs: -ltss2-py -L${libdir} diff --git a/man/tss2-tcti-py.7.in b/man/tss2-tcti-py.7.in new file mode 100644 index 000000000..423f1ff04 --- /dev/null +++ b/man/tss2-tcti-py.7.in @@ -0,0 +1,14 @@ +.\" Process this file with +.\" groff -man -Tascii foo.1 +.\" +.TH TCTI-PY 7 "JANUARY 2023" "TPM2 Software Stack" +.SH NAME +tcti-py \- Launch Python3 TPM2 TCTIs +.SH SYNOPSIS +Write a TCTI in Python3. +.SH DESCRIPTION +tcti-py is a library that allows creating TCTIs in Python3 by implementing a +a TCTI interface in Python. More complete documentation can be found by +visiting this URL: https://github.com/tpm2-software/tpm2-tss/tree/master/doc/tcti-py.md + +It is related to the TPM Command Transmission Interface Specification\*(rq specification. diff --git a/src/tss2-tcti/tcti-py.c b/src/tss2-tcti/tcti-py.c new file mode 100644 index 000000000..c8edc4df3 --- /dev/null +++ b/src/tss2-tcti/tcti-py.c @@ -0,0 +1,527 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#define PY_SSIZE_T_CLEAN +#include + +#include "tss2_tcti_py.h" + +#include "tcti-py.h" +#include "tcti-common.h" +#define LOGMODULE tcti +#include "util/log.h" + +/* we don't support make_sticky */ +#define TCTI_PY_VERSION 1 + +#define SAFE_DECREF(x) \ + do { \ + Py_XDECREF(x); \ + x = NULL; \ + } while(0) + +/* + * I wanted to use constructor and destructor attributes for this, however, gcc was + * complaining that 'deconstructor' attribute directive ignored. So instead we + * just do a simple refcnt. + */ +static struct { + size_t refcnt; + pthread_mutex_t lock; + void *dlhandle; +} _global = { + .lock = PTHREAD_MUTEX_INITIALIZER +}; + +/* + * This function wraps the "up-cast" of the opaque TCTI context type to the + * type for the command TCTI context. If passed a NULL context the function + * returns a NULL ptr. The function doesn't check magic number anymore + * It should checked by the appropriate tcti_common_checks. + */ +static TSS2_TCTI_PY_CONTEXT* +tcti_py_context_cast (TSS2_TCTI_CONTEXT *tcti_ctx) +{ + if (tcti_ctx == NULL) + return NULL; + + return (TSS2_TCTI_PY_CONTEXT*) tcti_ctx; +} +/* + * This function down-casts the cmd TCTI context to the common context + * defined in the tcti-common module. + */ +static TSS2_TCTI_COMMON_CONTEXT* +tcti_py_down_cast (TSS2_TCTI_PY_CONTEXT *tcti_cmd) +{ + if (tcti_cmd == NULL) { + return NULL; + } + return &tcti_cmd->common; +} + +static TSS2_RC +tcti_py_get_poll_handles_not_implemented (TSS2_TCTI_CONTEXT *tctiContext, + TSS2_TCTI_POLL_HANDLE *handles, size_t *num_handles) +{ + UNUSED(tctiContext); + UNUSED(handles); + UNUSED(num_handles); + return TSS2_TCTI_RC_NOT_IMPLEMENTED; +} + +static TSS2_RC +tcti_py_transmit (TSS2_TCTI_CONTEXT *tcti_ctx, size_t size, + const uint8_t *buffer) +{ + TSS2_RC rc = TSS2_TCTI_RC_GENERAL_FAILURE; + + PyObject *py_buf = NULL; + TSS2_TCTI_PY_CONTEXT *tcti_py = tcti_py_context_cast (tcti_ctx); + TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_py_down_cast (tcti_py); + + TSS2_RC r = tcti_common_transmit_checks (tcti_common, buffer, + TSS2_TCTI_MAGIC (tcti_common)); + if (r != TSS2_RC_SUCCESS) { + return r; + } + + py_buf = Py_BuildValue("(y#)", buffer, size); + if (!py_buf) { + PyErr_Print(); + rc = TSS2_TCTI_RC_MEMORY; + goto py_error; + } + + PyObject_CallObject(tcti_py->methods.transmit, py_buf); + if(PyErr_Occurred()) { + goto py_error; + } + + tcti_common->state = TCTI_STATE_RECEIVE; + rc = TSS2_RC_SUCCESS; +out: + return rc; + +py_error: + PyErr_Print(); + SAFE_DECREF(py_buf); + goto out; +} + +static TSS2_RC +tcti_py_receive (TSS2_TCTI_CONTEXT *tctiContext, size_t *response_size, + unsigned char *response_buffer, int32_t timeout) +{ + PyObject *py_timeout = NULL, *py_response = NULL; + + TSS2_RC rc = TSS2_TCTI_RC_GENERAL_FAILURE; + TSS2_TCTI_PY_CONTEXT *tcti_py = tcti_py_context_cast (tctiContext); + TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_py_down_cast (tcti_py); + + TSS2_RC r = tcti_common_receive_checks (tcti_common, response_size, + TSS2_TCTI_MAGIC (tcti_common)); + if (r != TSS2_RC_SUCCESS) { + return r; + } + + /* + * we we're called before and have a response that needs to be returned + * if we don't have a response, go fetch one from the Python TCTI + */ + if (!tcti_py->cur_response) { + + /* long is at least 32, but may be 64 so just don't pass int32_t + * or python may read too far if long is 64 + */ + long timeout_long = timeout; + py_timeout = Py_BuildValue("(l)", timeout_long); + if (!py_timeout) { + rc = TSS2_TCTI_RC_MEMORY; + goto py_error; + } + + + py_response = PyObject_CallObject(tcti_py->methods.receive, py_timeout); + if (!py_response) { + *response_size = 0; + goto py_error; + } + + if (!PyBytes_Check(py_response)) { + LOG_ERROR("Expected Python TCTI receive to return a bytes like object"); + rc = TSS2_TCTI_RC_BAD_VALUE; + goto error; + } + } else { + /* use our remembered response from before */ + py_response = tcti_py->cur_response; + /* current function takes ownership */ + tcti_py->cur_response = NULL; + } + + Py_ssize_t py_response_size = PyBytes_Size(py_response); + + /* + * State: No response buffer calling for size, + * Action: Remember this buffer until the next call, return OK + */ + if (!response_buffer) { + /* take ownership */ + tcti_py->cur_response = py_response; + py_response = NULL; + *response_size = py_response_size; + + } else if (py_response_size > (ssize_t)*response_size) { + /* + * State: Response buffer provided but too small + * Action: + * if buffer too small return TSS2_TCTI_RC_INSUFFICIENT_BUFFER and + * remember buffer for next call + */ + + /* take ownership */ + tcti_py->cur_response = py_response; + py_response = NULL; + + /* let caller know needed size */ + *response_size = py_response_size; + rc = TSS2_TCTI_RC_INSUFFICIENT_BUFFER; + goto error; + + } else { + + /* + * State: Buffer of sufficient size + * Action: Copy to user and forget remembered buffer + * + * Note: The current remembered buffer may or may not be set depending + * on if this function succeeded on the first try (not set) vs multiple + * calls. + */ + char *data = PyBytes_AsString(py_response); + if (!data) { + /* forget the internal buffer on error if set and NULL it */ + SAFE_DECREF(tcti_py->cur_response); + rc = TSS2_TCTI_RC_MEMORY; + goto py_error; + } + + /* copy to user */ + memcpy(response_buffer, data, py_response_size); + SAFE_DECREF(tcti_py->cur_response); + + + /* + * Executing code beyond this point transitions the state machine to + * TRANSMIT. Another call to this function will not be possible until + * another command is sent to the TPM. + */ + tcti_common->header.size = 0; + tcti_common->state = TCTI_STATE_TRANSMIT; + } + + rc = TSS2_RC_SUCCESS; +out: + return rc; + +py_error: + PyErr_Print(); +error: + SAFE_DECREF(py_response); + SAFE_DECREF(py_timeout); + goto out; +} + +static void +tcti_py_finalize (TSS2_TCTI_CONTEXT *tctiContext) +{ + TSS2_TCTI_PY_CONTEXT *tcti_py = tcti_py_context_cast (tctiContext); + + if (tcti_py == NULL) { + return; + } + + SAFE_DECREF(tcti_py->methods.make_sticky); + SAFE_DECREF(tcti_py->methods.receive); + SAFE_DECREF(tcti_py->methods.transmit); + SAFE_DECREF(tcti_py->py_tcti); + SAFE_DECREF(tcti_py->cur_response); + + /* Decrement the reference count on PYTHON init */ + pthread_mutex_lock(&_global.lock); + assert(_global.refcnt != 0); + _global.refcnt--; + if (_global.refcnt == 0) { + Py_Finalize(); + if (_global.dlhandle) { + dlclose(_global.dlhandle); + _global.dlhandle = NULL; + } + } + pthread_mutex_unlock(&_global.lock); +} + +#define GET_TRANSMIT_FROM_PY(tcti_py) _get_method(tcti_py, &tcti_py->methods.transmit, "transmit", "transmit(self, data: bytes) -> None", false) +#define GET_RECIEVE_FROM_PY(tcti_py) _get_method(tcti_py, &tcti_py->methods.receive, "receive", "receive(self, timeout: int) -> bytes", false) + +static TSS2_RC _get_method(TSS2_TCTI_PY_CONTEXT *tcti_py, PyObject **save, const char *method, const char *signature, bool none_ok) { + + PyObject *py_method = NULL; + PyObject *py_method_name = NULL; + + TSS2_RC rc = TSS2_TCTI_RC_NOT_IMPLEMENTED; + + py_method_name = PyUnicode_FromString(method); + if (!py_method_name) { + rc = TSS2_TCTI_RC_MEMORY; + goto py_error; + } + py_method = PyObject_GetAttr(tcti_py->py_tcti, py_method_name); + if (!py_method) { + if (none_ok) { + LOG_DEBUG("Python TCTI does not implement method %s", method); + goto success; + } + LOG_ERROR("Expected Python TCTI to have method %s", signature); + goto py_error; + } + + if (!PyCallable_Check(py_method)) { + LOG_ERROR("Expected %s to be a callable method", signature); + goto py_error; + } + + /* Take Ownership of the PyObject method */ + *save = py_method; + py_method = NULL; +success: + rc = TSS2_RC_SUCCESS; +out: + SAFE_DECREF(py_method_name); + return rc; + +py_error: + PyErr_Print(); + SAFE_DECREF(py_method); + goto out; +} + +static TSS2_RC get_magic(TSS2_TCTI_PY_CONTEXT *tcti_py) { + + PyObject *tmp_result = NULL; + PyObject *tmp_arg = NULL; + TSS2_RC rc = TSS2_TCTI_RC_NOT_IMPLEMENTED; + TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_py_down_cast (tcti_py); + + /* same concept as version for magic, but magic is a uint64_t */ + tmp_arg = PyUnicode_FromString("magic"); + if (!tmp_arg) { + rc = TSS2_TCTI_RC_MEMORY; + goto py_error; + } + + tmp_result = PyObject_GetAttr(tcti_py->py_tcti, tmp_arg); + if (!tmp_result) { + LOG_ERROR("Expected module to implement attribute magic returning an 8 byte max integer"); + goto py_error; + } + + unsigned long long magic = tmp_result ? PyLong_AsUnsignedLongLong(tmp_result) : TCTI_PY_MAGIC; + TSS2_TCTI_MAGIC (tcti_common) = magic; + + rc = TSS2_RC_SUCCESS; + +py_error: + PyErr_Print(); + SAFE_DECREF(tmp_result); + SAFE_DECREF(tmp_arg); + + return rc; +} + +TSS2_RC tcti_py_init_py_module (TSS2_TCTI_PY_CONTEXT *tcti_py) { + + TSS2_RC rc = TSS2_TCTI_RC_NOT_IMPLEMENTED; + TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_py_down_cast (tcti_py); + + rc = get_magic(tcti_py); + if (rc) { + goto error; + } + + rc = GET_TRANSMIT_FROM_PY(tcti_py); + if (rc) { + goto error; + } + + rc = GET_RECIEVE_FROM_PY(tcti_py); + if (rc) { + goto error; + } + + TSS2_TCTI_VERSION (tcti_common) = TCTI_PY_VERSION; + TSS2_TCTI_TRANSMIT (tcti_common) = tcti_py_transmit; + TSS2_TCTI_RECEIVE (tcti_common) = tcti_py_receive; + TSS2_TCTI_FINALIZE (tcti_common) = tcti_py_finalize; + + /* no make sticky or get_poll_handles */ + TSS2_TCTI_MAKE_STICKY (tcti_common) = tcti_make_sticky_not_implemented; + TSS2_TCTI_GET_POLL_HANDLES (tcti_common) = tcti_py_get_poll_handles_not_implemented; + + tcti_common->state = TCTI_STATE_TRANSMIT; + tcti_common->locality = 0; + memset (&tcti_common->header, 0, sizeof (tcti_common->header)); + + tcti_py->cur_response = NULL; + + rc = TSS2_RC_SUCCESS; + +error: + return rc; + +} + +TSS2_RC +Tss2_Tcti_Py_Init (TSS2_TCTI_CONTEXT *tcti_context, size_t *size, + const char *conf) +{ + TSS2_RC rc = TSS2_TCTI_RC_GENERAL_FAILURE; + + TSS2_TCTI_PY_CONTEXT *tcti_py = tcti_py_context_cast(tcti_context); + + char *conf_copy = NULL; + PyObject *py_module = NULL, *py_dict = NULL, *py_initfn = NULL, + *py_modname = NULL, *py_arg = NULL; + + if (size == NULL) { + LOG_ERROR("size pointer must be valid"); + return TSS2_TCTI_RC_BAD_VALUE; + } + + if (!conf) { + LOG_ERROR("must have a module name to run in conf"); + return TSS2_TCTI_RC_BAD_VALUE; + } + + if (tcti_context == NULL) { + *size = sizeof (TSS2_TCTI_PY_CONTEXT); + return TSS2_RC_SUCCESS; + } + + conf_copy = strdup(conf); + if (!conf_copy) { + return TSS2_TCTI_RC_MEMORY; + } + + char *arg = NULL; + char *module = conf_copy; + char *sep = strchr(conf_copy, ':'); + if (sep) { + *sep = '\0'; + arg = &sep[1]; + } + + LOG_DEBUG ("Initializing Python TCTI with module: \"%s\" arg \"%s\"", + module, arg ? arg : "(null)"); + + /* Increment the reference count on PYTHON init */ + pthread_mutex_lock(&_global.lock); + size_t new_cnt = 0; + bool overflow = __builtin_add_overflow(_global.refcnt, 1, &new_cnt); + if (!overflow) { + _global.refcnt = new_cnt; + if (new_cnt == 1) { + /* See: https://stackoverflow.com/questions/60719987/embedding-python-which-uses-numpy-in-c-doesnt-work-in-library-dynamically-loa */ + _global.dlhandle = dlopen ("libpython3.so", RTLD_LAZY|RTLD_GLOBAL); + if (!_global.dlhandle) { + LOG_WARNING("Could not dlopen libpython3.so, some things may not work: %s", dlerror()); + } + Py_Initialize(); + } + } + pthread_mutex_unlock(&_global.lock); + if (overflow) { + LOG_ERROR("Max instance count limit reached"); + goto error; + } + + py_modname = PyUnicode_FromString(module); + if (!py_modname) { + goto py_error; + } + + py_arg = Py_BuildValue("(z)", arg); + if (!py_arg) { + goto py_error; + } + + py_module = PyImport_Import(py_modname); + if (!py_module) { + goto py_error; + } + + py_dict = PyModule_GetDict(py_module); + if (!py_dict) { + goto py_error; + } + + /* + * modules implement a method called tcti_init(args) -> Object + * Where the returned object implements the required attributes. + */ + py_initfn = PyDict_GetItemString(py_dict, (char*)"tcti_init"); + if (!py_initfn) { + goto py_error; + } + + if (!PyCallable_Check(py_initfn)) { + goto py_error; + } + + /* This is the instance object that implements the TCTI */ + tcti_py->py_tcti = PyObject_CallObject(py_initfn, py_arg); + if (!tcti_py->py_tcti) { + goto py_error; + } + + rc = tcti_py_init_py_module (tcti_py); + if (rc != TSS2_RC_SUCCESS) { + goto py_error; + } + + rc = TSS2_RC_SUCCESS; +out: + SAFE_DECREF(py_module); + SAFE_DECREF(py_dict); + SAFE_DECREF(py_initfn); + SAFE_DECREF(py_modname); + SAFE_DECREF(py_arg); + return rc; +py_error: + PyErr_Print(); +error: + tcti_py_finalize(tcti_context); + goto out; +} + +/* public info structure */ +const TSS2_TCTI_INFO tss2_tcti_info = { + .version = TCTI_PY_VERSION, + .name = TCTI_PY_NAME, + .description = TCTI_PY_DESCRIPTION, + .config_help = TCTI_PY_HELP, + .init = Tss2_Tcti_Py_Init, +}; + +const TSS2_TCTI_INFO* +Tss2_Tcti_Info (void) +{ + return &tss2_tcti_info; +} diff --git a/src/tss2-tcti/tcti-py.h b/src/tss2-tcti/tcti-py.h new file mode 100644 index 000000000..8ef5cdb7b --- /dev/null +++ b/src/tss2-tcti/tcti-py.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#ifndef TCTI_CMD_H +#define TCTI_CMD_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "tcti-common.h" +#include "util-io/io.h" + +#define TCTI_PY_NAME "tcti-py" +#define TCTI_PY_DESCRIPTION "TCTI module for writing a TCTI in Python3." +#define TCTI_PY_HELP "String used as module:arg passed to tcti_init(arg: str) -> TCTI Object." + +#define TCTI_PY_MAGIC 0xcf5e2c7f4b24deb0ULL + +typedef struct TSS2_TCTI_PY_CONTEXT TSS2_TCTI_PY_CONTEXT; +struct TSS2_TCTI_PY_CONTEXT { + TSS2_TCTI_COMMON_CONTEXT common; + PyObject *py_tcti; + PyObject *cur_response; + struct { + /* no finalize as __del__ will be called for the TCTI instance */ + PyObject *transmit; + PyObject *receive; + PyObject *make_sticky; + } methods; +}; + +#endif /* TCTI_CMD_H */ diff --git a/src/tss2-tcti/tctildr-dl.c b/src/tss2-tcti/tctildr-dl.c index d26219d2f..2855ccbff 100644 --- a/src/tss2-tcti/tctildr-dl.c +++ b/src/tss2-tcti/tctildr-dl.c @@ -112,7 +112,7 @@ handle_from_name(const char *file, if (*handle != NULL) { return TSS2_RC_SUCCESS; } else { - LOG_DEBUG("Could not load TCTI file \"%s\": %s", file, dlerror()); + LOG_ERROR("Could not load TCTI file \"%s\": %s", file, dlerror()); } } diff --git a/test/helper/pytcti.py b/test/helper/pytcti.py new file mode 100644 index 000000000..24317bbf2 --- /dev/null +++ b/test/helper/pytcti.py @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: BSD-2-Clause +import os + +class MyPyTCTI(object): + def __init__(self, args: str): + pass + + @property + def magic(self): + if os.environ.get("TPM2_TSS_TEST_PYTCTI_FAIL", None) == "magic": + raise RuntimeException("Failing in magic") + # Optional attribute + return 42 + + def receive(self, timeout: int) -> bytes: + if os.environ.get("TPM2_TSS_TEST_PYTCTI_FAIL", None) == "receive": + raise RuntimeException("Failing in receive") + + data = b"\x80\x01\x00\x00\x01\x83\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00.\x00\x00\x01\x002.0\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\xa4\x00\x00\x01\x03\x00\x00\x00K\x00\x00\x01\x04\x00\x00\x07\xe5\x00\x00\x01\x05IBM\x00\x00\x00\x01\x06SW \x00\x00\x01\x07 TPM\x00\x00\x01\x08\x00\x00\x00\x00\x00\x00\x01\t\x00\x00\x00\x00\x00\x00\x01\n\x00\x00\x00\x01\x00\x00\x01\x0b \x19\x10#\x00\x00\x01\x0c\x00\x1666\x00\x00\x01\r\x00\x00\x04\x00\x00\x00\x01\x0e\x00\x00\x00\x03\x00\x00\x01\x0f\x00\x00\x00\x07\x00\x00\x01\x10\x00\x00\x00\x03\x00\x00\x01\x11\x00\x00\x00@\x00\x00\x01\x12\x00\x00\x00\x18\x00\x00\x01\x13\x00\x00\x00\x03\x00\x00\x01\x14\xff\xff\xff\xff\x00\x00\x01\x16\x00\x00\x00\x00\x00\x00\x01\x17\x00\x00\x08\x00\x00\x00\x01\x18\x00\x00\x00\x06\x00\x00\x01\x19\x00\x00\x10\x00\x00\x00\x01\x1a\x00\x00\x00\r\x00\x00\x01\x1b\x00\x00\x00\x06\x00\x00\x01\x1c\x00\x00\x01\x00\x00\x00\x01\x1d\x00\x00\x00\xff\x00\x00\x01\x1e\x00\x00\x10\x00\x00\x00\x01\x1f\x00\x00\x10\x00\x00\x00\x01 \x00\x00\x00@\x00\x00\x01!\x00\x00\n\x84\x00\x00\x01\"\x00\x00\x01\x94\x00\x00\x01#2.0\x00\x00\x00\x01$\x00\x00\x00\x00\x00\x00\x01%\x00\x00\x00\xa4\x00\x00\x01&\x00\x00\x00K\x00\x00\x01'\x00\x00\x07\xe5\x00\x00\x01(\x00\x00\x00\x80\x00\x00\x01)\x00\x00\x00n\x00\x00\x01*\x00\x00\x00n\x00\x00\x01+\x00\x00\x00\x00\x00\x00\x01,\x00\x00\x04\x00\x00\x00\x01-\x00\x00\x00\x00\x00\x00\x01.\x00\x00\x04\x00" + return data + + def transmit(self, data: bytes): + if os.environ.get("TPM2_TSS_TEST_PYTCTI_FAIL", None) == "transmit": + raise RuntimeException("Failing in transmit") + + +def tcti_init(args: str) -> MyPyTCTI: + if os.environ.get("TPM2_TSS_TEST_PYTCTI_FAIL", None) == "tcti_init": + raise RuntimeException("Failing in tcti_init") + return MyPyTCTI(args) diff --git a/test/unit/tcti-py.c b/test/unit/tcti-py.c new file mode 100644 index 000000000..b02c9c7c3 --- /dev/null +++ b/test/unit/tcti-py.c @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tss2/tss2_esys.h" +#include "tss2/tss2_mu.h" +#include "tss2/tss2_tcti_py.h" + +#include "tss2-tcti/tcti-py.h" + +/* test/helper/pytcti.py */ +#define PY_TCTI "pytcti" + +static char expected_response[] = { + 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x01, 0x00, 0x32, + 0x2e, 0x30, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x01, 0x03, 0x00, + 0x00, 0x00, 0x4b, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x07, 0xe5, 0x00, + 0x00, 0x01, 0x05, 0x49, 0x42, 0x4d, 0x00, 0x00, 0x00, 0x01, 0x06, 0x53, + 0x57, 0x20, 0x20, 0x00, 0x00, 0x01, 0x07, 0x20, 0x54, 0x50, 0x4d, 0x00, + 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x0b, 0x20, 0x19, 0x10, 0x23, 0x00, 0x00, 0x01, 0x0c, 0x00, + 0x16, 0x36, 0x36, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x0f, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x01, 0x11, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x12, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x01, 0x14, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x17, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x19, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x00, 0x01, 0x1b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x01, 0x1c, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x1d, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x01, 0x1e, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x1f, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x01, 0x21, 0x00, 0x00, 0x0a, 0x84, 0x00, 0x00, 0x01, 0x22, 0x00, + 0x00, 0x01, 0x94, 0x00, 0x00, 0x01, 0x23, 0x32, 0x2e, 0x30, 0x00, 0x00, + 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x25, 0x00, + 0x00, 0x00, 0xa4, 0x00, 0x00, 0x01, 0x26, 0x00, 0x00, 0x00, 0x4b, 0x00, + 0x00, 0x01, 0x27, 0x00, 0x00, 0x07, 0xe5, 0x00, 0x00, 0x01, 0x28, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x00, 0x6e, 0x00, + 0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x01, 0x2b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x01, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2e, 0x00, + 0x00, 0x04, 0x00 +}; + +static void +tcti_py_init_context_and_size_null_test (void **state) +{ + TSS2_RC rc; + + rc = Tss2_Tcti_Py_Init (NULL, NULL, NULL); + assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE); +} + +static void +tcti_py_init_size_test (void **state) +{ + size_t tcti_size = 0; + TSS2_RC rc; + + rc = Tss2_Tcti_Py_Init (NULL, &tcti_size, "foomod"); + assert_int_equal (rc, TSS2_RC_SUCCESS); + assert_int_equal (tcti_size, sizeof (TSS2_TCTI_PY_CONTEXT)); +} + +static void +tcti_py_test_cmd (void **state) +{ + TSS2_RC rc; + + char _tcti[sizeof(TSS2_TCTI_PY_CONTEXT)]; + size_t tcti_size = sizeof(_tcti); + TSS2_TCTI_CONTEXT *tcti = (TSS2_TCTI_CONTEXT *)&_tcti; + rc = Tss2_Tcti_Py_Init (tcti, &tcti_size, PY_TCTI); + assert_int_equal (rc, TSS2_RC_SUCCESS); + + ESYS_CONTEXT *ectx = NULL; + rc = Esys_Initialize(&ectx, tcti, NULL); + assert_int_equal (rc, TSS2_RC_SUCCESS); + + TPMI_YES_NO yes_no = TPM2_NO; + TPMS_CAPABILITY_DATA *cap_data = NULL; + rc = Esys_GetCapability(ectx, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_TPM_PROPERTIES, TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES, + &yes_no, &cap_data); + assert_int_equal (rc, TSS2_RC_SUCCESS); + assert_non_null(cap_data); + + size_t offset = 0; + uint8_t buf[sizeof(*cap_data)]; + rc = Tss2_MU_TPMS_CAPABILITY_DATA_Marshal(cap_data, buf, sizeof(buf), &offset); + Esys_Free(cap_data); + assert_int_equal (rc, TSS2_RC_SUCCESS); + + assert_int_equal(offset, sizeof(expected_response)); + assert_memory_equal(buf, expected_response, offset); +} + +static int +teardown_exc(void **state) +{ + return setenv("TPM2_TSS_TEST_PYTCTI_FAIL", "", 1); +} + +static int +setup_tcti_init_exc(void **state) +{ + return setenv("TPM2_TSS_TEST_PYTCTI_FAIL", "tcti_init", 1); +} + +static void +tcti_py_test_tcti_init_exception (void **state) +{ + TSS2_RC rc; + + char _tcti[sizeof(TSS2_TCTI_PY_CONTEXT)]; + size_t tcti_size = sizeof(_tcti); + TSS2_TCTI_CONTEXT *tcti = (TSS2_TCTI_CONTEXT *)&_tcti; + + rc = Tss2_Tcti_Py_Init (tcti, &tcti_size, PY_TCTI); + assert_int_not_equal (rc, TSS2_RC_SUCCESS); +} + +static int +setup_tcti_recv_exc(void **state) +{ + return setenv("TPM2_TSS_TEST_PYTCTI_FAIL", "tcti_receive", 1); +} + +static void +tcti_py_test_tcti_recv_exception (void **state) +{ + TSS2_RC rc; + + char _tcti[sizeof(TSS2_TCTI_PY_CONTEXT)]; + size_t tcti_size = sizeof(_tcti); + TSS2_TCTI_CONTEXT *tcti = (TSS2_TCTI_CONTEXT *)&_tcti; + + rc = Tss2_Tcti_Py_Init (tcti, &tcti_size, PY_TCTI); + assert_int_not_equal (rc, TSS2_RC_SUCCESS); + + ESYS_CONTEXT *ectx = NULL; + rc = Esys_Initialize(&ectx, tcti, NULL); + assert_int_equal (rc, TSS2_RC_SUCCESS); + + TPMI_YES_NO yes_no = TPM2_NO; + TPMS_CAPABILITY_DATA *cap_data = NULL; + rc = Esys_GetCapability(ectx, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_TPM_PROPERTIES, TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES, + &yes_no, &cap_data); + assert_int_not_equal (rc, TSS2_RC_SUCCESS); +} + +static int +setup_tcti_transmit_exc(void **state) +{ + return setenv("TPM2_TSS_TEST_PYTCTI_FAIL", "tcti_transmit", 1); +} + +static void +tcti_py_test_tcti_transmit_exception (void **state) +{ + TSS2_RC rc; + + char _tcti[sizeof(TSS2_TCTI_PY_CONTEXT)]; + size_t tcti_size = sizeof(_tcti); + TSS2_TCTI_CONTEXT *tcti = (TSS2_TCTI_CONTEXT *)&_tcti; + + rc = Tss2_Tcti_Py_Init (tcti, &tcti_size, PY_TCTI); + assert_int_not_equal (rc, TSS2_RC_SUCCESS); + + ESYS_CONTEXT *ectx = NULL; + rc = Esys_Initialize(&ectx, tcti, NULL); + assert_int_equal (rc, TSS2_RC_SUCCESS); + + TPMI_YES_NO yes_no = TPM2_NO; + TPMS_CAPABILITY_DATA *cap_data = NULL; + rc = Esys_GetCapability(ectx, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_TPM_PROPERTIES, TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES, + &yes_no, &cap_data); + assert_int_not_equal (rc, TSS2_RC_SUCCESS); +} + +int +main (int argc, + char *argv[]) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test (tcti_py_init_context_and_size_null_test), + cmocka_unit_test (tcti_py_init_size_test), + cmocka_unit_test (tcti_py_test_cmd), + cmocka_unit_test_setup_teardown (tcti_py_test_tcti_init_exception, setup_tcti_init_exc, teardown_exc), + + cmocka_unit_test_setup_teardown (tcti_py_test_tcti_recv_exception, setup_tcti_recv_exc, teardown_exc), + cmocka_unit_test_setup_teardown (tcti_py_test_tcti_transmit_exception, setup_tcti_transmit_exc, teardown_exc), + }; + + setenv("PYTHONPATH", "/home/billdev/workspace/tpm2-tss/test/helper", 1); + + return cmocka_run_group_tests (tests, NULL, NULL); +}