| /*********************************************************************** |
| * Copyright (c) 2015 - 2017 Intel Corporation |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| **********************************************************************/ |
| |
| #include <inttypes.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/time.h> |
| #include <inttypes.h> |
| |
| #include <uriparser/Uri.h> |
| |
| #include "sapi/tpm20.h" |
| #include "sapi/tss2_mu.h" |
| #include "tcti/tcti_socket.h" |
| #include "sysapi_util.h" |
| #include "tcti.h" |
| #include "sockets.h" |
| #include "tss2_endian.h" |
| #define LOGMODULE tcti |
| #include "log/log.h" |
| |
| #define TCTI_SOCKET_DEFAULT "tcp://127.0.0.1:2321" |
| |
| static TSS2_RC tctiRecvBytes ( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| SOCKET sock, |
| unsigned char *data, |
| int len |
| ) |
| { |
| TSS2_RC result = 0; |
| result = recvBytes (sock, data, len); |
| if ((INT32)result == SOCKET_ERROR) { |
| LOG_ERROR("In recvBytes, recv failed (socket: 0x%x) with error: %d", |
| sock, WSAGetLastError ()); |
| return TSS2_TCTI_RC_IO_ERROR; |
| } |
| LOGBLOB_DEBUG(data, len, "Receive Bytes from socket #0x%x:", sock); |
| |
| return TSS2_RC_SUCCESS; |
| } |
| |
| static TSS2_RC tctiSendBytes ( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| SOCKET sock, |
| const unsigned char *data, |
| int len |
| ) |
| { |
| TSS2_RC ret = TSS2_RC_SUCCESS; |
| |
| LOGBLOB_DEBUG(data, len, "Send Bytes to socket #0x%x:", sock); |
| |
| ret = sendBytes (sock, data, len); |
| if (ret != TSS2_RC_SUCCESS) { |
| LOG_ERROR("In recvBytes, recv failed (socket: 0x%x) with error: %d", |
| sock, WSAGetLastError ()); |
| } |
| return ret; |
| } |
| |
| TSS2_RC SendSessionEndSocketTcti ( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| UINT8 tpmCmdServer |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| /* Value for "send command" to MS simulator */ |
| uint8_t buffer [4] = { 0, }; |
| SOCKET sock; |
| TSS2_RC rval = TSS2_RC_SUCCESS; |
| |
| if (tpmCmdServer) { |
| sock = tcti_intel->tpmSock; |
| } else { |
| sock = tcti_intel->otherSock; |
| } |
| |
| rval = Tss2_MU_UINT32_Marshal (TPM_SESSION_END, |
| buffer, |
| sizeof (buffer), |
| NULL); |
| if (rval == TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| rval = tctiSendBytes (tctiContext, sock, (char unsigned *)buffer, 4); |
| |
| return( rval ); |
| } |
| |
| TSS2_RC SocketSendTpmCommand( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| size_t command_size, |
| uint8_t *command_buffer |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| UINT32 tpmSendCommand; |
| UINT32 cnt, cnt1; |
| UINT8 locality; |
| TSS2_RC rval = TSS2_RC_SUCCESS; |
| size_t offset; |
| |
| rval = tcti_send_checks (tctiContext, command_buffer); |
| if (rval != TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| |
| #if LOGLEVEL == LOGLEVEL_DEBUG || \ |
| LOGLEVEL == LOGLEVEL_TRACE |
| UINT32 commandCode; |
| offset = sizeof (TPM2_ST) + sizeof (UINT32); |
| rval = Tss2_MU_TPM2_CC_Unmarshal (command_buffer, |
| command_size, |
| &offset, |
| &commandCode); |
| if (rval) return rval; |
| LOG_DEBUG("Command sent on socket #0x%x: %" PRIx16, tcti_intel->tpmSock, |
| commandCode); |
| #endif |
| /* |
| * Size TPM 1.2 and TPM 2.0 headers overlap exactly, we can use |
| * either 1.2 or 2.0 header to get the size. |
| */ |
| offset = sizeof (TPM2_ST); |
| rval = Tss2_MU_UINT32_Unmarshal (command_buffer, |
| command_size, |
| &offset, |
| &cnt); |
| if (rval != TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| |
| /* Send TPM2_SEND_COMMAND */ |
| rval = Tss2_MU_UINT32_Marshal (MS_SIM_TPM_SEND_COMMAND, |
| (uint8_t*)&tpmSendCommand, |
| sizeof (tpmSendCommand), |
| NULL); /* Value for "send command" to MS simulator. */ |
| if (rval != TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| |
| rval = tctiSendBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)&tpmSendCommand, |
| 4); |
| if (rval != TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| |
| /* Send the locality */ |
| locality = (UINT8)tcti_intel->status.locality; |
| rval = tctiSendBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)&locality, |
| 1); |
| if (rval != TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| |
| LOG_DEBUG("Locality = %d", tcti_intel->status.locality); |
| |
| /* Send number of bytes. */ |
| cnt1 = cnt; |
| cnt = HOST_TO_BE_32(cnt); |
| rval = tctiSendBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)&cnt, |
| 4); |
| if (rval != TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| |
| /* Send the TPM command buffer */ |
| rval = tctiSendBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)command_buffer, |
| cnt1); |
| if (rval != TSS2_RC_SUCCESS) { |
| return rval; |
| } |
| LOGBLOB_DEBUG(command_buffer, cnt1, "Sent command buffer="); |
| |
| tcti_intel->status.commandSent = 1; |
| |
| tcti_intel->previousStage = TCTI_STAGE_SEND_COMMAND; |
| tcti_intel->status.tagReceived = 0; |
| tcti_intel->status.responseSizeReceived = 0; |
| tcti_intel->status.protocolResponseSizeReceived = 0; |
| |
| return rval; |
| } |
| |
| TSS2_RC SocketCancel( |
| TSS2_TCTI_CONTEXT *tctiContext |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| TSS2_RC rc; |
| |
| rc = tcti_common_checks (tctiContext); |
| if (rc != TSS2_RC_SUCCESS) { |
| return rc; |
| } else if (tcti_intel->status.commandSent != 1) { |
| return TSS2_TCTI_RC_BAD_SEQUENCE; |
| } else { |
| return PlatformCommand (tctiContext, MS_SIM_CANCEL_ON); |
| } |
| } |
| |
| TSS2_RC SocketSetLocality( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| uint8_t locality |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| TSS2_RC rc; |
| |
| rc = tcti_common_checks (tctiContext); |
| if (rc != TSS2_RC_SUCCESS) { |
| return rc; |
| } |
| if (tcti_intel->status.commandSent == 1) { |
| return TSS2_TCTI_RC_BAD_SEQUENCE; |
| } |
| |
| tcti_intel->status.locality = locality; |
| |
| return TSS2_RC_SUCCESS; |
| } |
| |
| TSS2_RC SocketGetPollHandles( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| TSS2_TCTI_POLL_HANDLE *handles, |
| size_t *num_handles) |
| { |
| return TSS2_TCTI_RC_NOT_IMPLEMENTED; |
| } |
| |
| void SocketFinalize( |
| TSS2_TCTI_CONTEXT *tctiContext |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| TSS2_RC rc; |
| |
| rc = tcti_common_checks (tctiContext); |
| if (rc != TSS2_RC_SUCCESS) { |
| return; |
| } |
| |
| SendSessionEndSocketTcti (tctiContext, 1); |
| SendSessionEndSocketTcti (tctiContext, 0); |
| |
| CloseSockets (tcti_intel->otherSock, tcti_intel->tpmSock); |
| } |
| |
| TSS2_RC SocketReceiveTpmResponse( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| size_t *response_size, |
| unsigned char *response_buffer, |
| int32_t timeout |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| UINT32 trash; |
| TSS2_RC rval = TSS2_RC_SUCCESS; |
| fd_set readFds; |
| struct timeval tv, *tvPtr; |
| int32_t timeoutMsecs = timeout % 1000; |
| int iResult; |
| unsigned char responseSizeDelta = 0; |
| |
| rval = tcti_receive_checks (tctiContext, response_size, response_buffer); |
| if (rval != TSS2_RC_SUCCESS) { |
| goto retSocketReceiveTpmResponse; |
| } |
| |
| if (timeout == TSS2_TCTI_TIMEOUT_BLOCK) { |
| tvPtr = 0; |
| } else { |
| tv.tv_sec = timeout / 1000; |
| tv.tv_usec = timeoutMsecs * 1000; |
| tvPtr = &tv; |
| } |
| |
| FD_ZERO (&readFds); |
| FD_SET (tcti_intel->tpmSock, &readFds); |
| |
| iResult = select (tcti_intel->tpmSock + 1, &readFds, 0, 0, tvPtr); |
| if (iResult == 0) { |
| LOG_ERROR("select failed due to timeout, socket #: 0x%x", |
| tcti_intel->tpmSock); |
| rval = TSS2_TCTI_RC_TRY_AGAIN; |
| goto retSocketReceiveTpmResponse; |
| } else if (iResult == SOCKET_ERROR) { |
| LOG_ERROR("select failed with socket error: %d", |
| WSAGetLastError ()); |
| rval = TSS2_TCTI_RC_IO_ERROR; |
| goto retSocketReceiveTpmResponse; |
| } else if (iResult != 1) { |
| LOG_ERROR("select failed, read the wrong # of bytes: %d", |
| iResult); |
| rval = TSS2_TCTI_RC_IO_ERROR; |
| goto retSocketReceiveTpmResponse; |
| } |
| |
| if (tcti_intel->status.protocolResponseSizeReceived != 1) { |
| /* Receive the size of the response. */ |
| rval = tctiRecvBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)&tcti_intel->responseSize, |
| 4); |
| if (rval != TSS2_RC_SUCCESS) { |
| goto retSocketReceiveTpmResponse; |
| } |
| |
| tcti_intel->responseSize = BE_TO_HOST_32 (tcti_intel->responseSize); |
| tcti_intel->status.protocolResponseSizeReceived = 1; |
| } |
| |
| if (response_buffer == NULL) { |
| *response_size = tcti_intel->responseSize; |
| tcti_intel->status.protocolResponseSizeReceived = 1; |
| goto retSocketReceiveTpmResponse; |
| } |
| |
| if (*response_size < tcti_intel->responseSize) { |
| *response_size = tcti_intel->responseSize; |
| rval = TSS2_TCTI_RC_INSUFFICIENT_BUFFER; |
| |
| /* If possible, receive tag from TPM. */ |
| if (*response_size >= sizeof (TPM2_ST) && |
| tcti_intel->status.tagReceived == 0) |
| { |
| rval = tctiRecvBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)&tcti_intel->tag, |
| 2); |
| if (rval != TSS2_RC_SUCCESS) { |
| goto retSocketReceiveTpmResponse; |
| } else { |
| tcti_intel->status.tagReceived = 1; |
| } |
| } |
| |
| /* If possible, receive response size from TPM */ |
| if (*response_size >= (sizeof (TPM2_ST) + sizeof (TPM2_RC)) && |
| tcti_intel->status.responseSizeReceived == 0) |
| { |
| rval = tctiRecvBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)&tcti_intel->responseSize, |
| 4); |
| if (rval != TSS2_RC_SUCCESS) { |
| goto retSocketReceiveTpmResponse; |
| } else { |
| tcti_intel->responseSize = BE_TO_HOST_32 (tcti_intel->responseSize); |
| tcti_intel->status.responseSizeReceived = 1; |
| } |
| } |
| } else { |
| if (tcti_intel->responseSize > 0) |
| { |
| LOG_DEBUG("Response Received: "); |
| LOG_DEBUG("from socket #0x%x:", |
| tcti_intel->tpmSock); |
| } |
| |
| if (tcti_intel->status.tagReceived == 1) { |
| *(TPM2_ST *)response_buffer = tcti_intel->tag; |
| responseSizeDelta += sizeof (TPM2_ST); |
| response_buffer += sizeof (TPM2_ST); |
| } |
| |
| if (tcti_intel->status.responseSizeReceived == 1) { |
| *(TPM2_RC *)response_buffer = HOST_TO_BE_32 (tcti_intel->responseSize); |
| responseSizeDelta += sizeof (TPM2_RC); |
| response_buffer += sizeof (TPM2_RC); |
| } |
| |
| /* Receive the TPM response. */ |
| rval = tctiRecvBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)response_buffer, |
| tcti_intel->responseSize - responseSizeDelta); |
| if (rval != TSS2_RC_SUCCESS) { |
| goto retSocketReceiveTpmResponse; |
| } |
| LOGBLOB_DEBUG(response_buffer, tcti_intel->responseSize, |
| "Received response buffer="); |
| |
| /* Receive the appended four bytes of 0's */ |
| rval = tctiRecvBytes (tctiContext, |
| tcti_intel->tpmSock, |
| (unsigned char *)&trash, |
| 4); |
| if (rval != TSS2_RC_SUCCESS) { |
| goto retSocketReceiveTpmResponse; |
| } |
| } |
| |
| if (tcti_intel->responseSize < *response_size) { |
| *response_size = tcti_intel->responseSize; |
| } |
| |
| tcti_intel->status.commandSent = 0; |
| |
| /* Turn cancel off. */ |
| if (rval == TSS2_RC_SUCCESS) { |
| rval = PlatformCommand (tctiContext, MS_SIM_CANCEL_OFF); |
| } else { |
| /* Ignore return value so earlier error code is preserved. */ |
| PlatformCommand (tctiContext, MS_SIM_CANCEL_OFF); |
| } |
| |
| retSocketReceiveTpmResponse: |
| if (rval == TSS2_RC_SUCCESS && response_buffer != NULL) { |
| tcti_intel->previousStage = TCTI_STAGE_RECEIVE_RESPONSE; |
| } |
| |
| return rval; |
| } |
| |
| #define HOSTNAME_LENGTH 200 |
| #define PORT_LENGTH 4 |
| |
| /** |
| * This function sends the Microsoft simulator the MS_SIM_POWER_ON and |
| * MS_SIM_NV_ON commands using the PlatformCommand mechanism. Without |
| * these the simulator will respond with zero sized buffer which causes |
| * the TSS to freak out. Sending this command more than once is harmelss |
| * so it's advisable to call this function as part of the TCTI context |
| * initialization just to be sure. |
| * |
| * NOTE: The caller will still need to call Tss2_Sys_Startup. If they |
| * don't, an error will be returned from each call till they do but |
| * the error will at least be meaningful (TPM2_RC_INITIALIZE). |
| */ |
| static TSS2_RC InitializeMsTpm2Simulator( |
| TSS2_TCTI_CONTEXT *tctiContext |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| TSS2_RC rval; |
| |
| rval = PlatformCommand (tctiContext ,MS_SIM_POWER_ON); |
| if (rval != TSS2_RC_SUCCESS) { |
| CloseSockets (tcti_intel->otherSock, tcti_intel->tpmSock); |
| return rval; |
| } |
| |
| rval = PlatformCommand (tctiContext, MS_SIM_NV_ON); |
| if (rval != TSS2_RC_SUCCESS) { |
| CloseSockets (tcti_intel->otherSock, tcti_intel->tpmSock); |
| } |
| |
| return rval; |
| } |
| |
| TSS2_RC InitSocketTcti ( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| size_t *contextSize, |
| const TCTI_SOCKET_CONF *conf, |
| const uint8_t serverSockets |
| ) |
| { |
| TSS2_TCTI_CONTEXT_INTEL *tcti_intel = tcti_context_intel_cast (tctiContext); |
| TSS2_RC rval = TSS2_RC_SUCCESS; |
| SOCKET otherSock; |
| SOCKET tpmSock; |
| |
| if (tctiContext == NULL && contextSize == NULL) { |
| return TSS2_TCTI_RC_BAD_VALUE; |
| } else if( tctiContext == NULL ) { |
| *contextSize = sizeof (TSS2_TCTI_CONTEXT_INTEL); |
| return TSS2_RC_SUCCESS; |
| } else if( conf == NULL ) { |
| return TSS2_TCTI_RC_BAD_VALUE; |
| } |
| |
| TSS2_TCTI_MAGIC (tctiContext) = TCTI_MAGIC; |
| TSS2_TCTI_VERSION (tctiContext) = TCTI_VERSION; |
| TSS2_TCTI_TRANSMIT (tctiContext) = SocketSendTpmCommand; |
| TSS2_TCTI_RECEIVE (tctiContext) = SocketReceiveTpmResponse; |
| TSS2_TCTI_FINALIZE (tctiContext) = SocketFinalize; |
| TSS2_TCTI_CANCEL (tctiContext) = SocketCancel; |
| TSS2_TCTI_GET_POLL_HANDLES (tctiContext) = SocketGetPollHandles; |
| TSS2_TCTI_SET_LOCALITY (tctiContext) = SocketSetLocality; |
| tcti_intel->status.locality = 3; |
| tcti_intel->status.commandSent = 0; |
| tcti_intel->status.tagReceived = 0; |
| tcti_intel->status.responseSizeReceived = 0; |
| tcti_intel->status.protocolResponseSizeReceived = 0; |
| tcti_intel->currentTctiContext = 0; |
| tcti_intel->previousStage = TCTI_STAGE_INITIALIZE; |
| |
| rval = (TSS2_RC) InitSockets (conf->hostname, |
| conf->port, |
| &otherSock, |
| &tpmSock); |
| if (rval == TSS2_RC_SUCCESS) { |
| tcti_intel->otherSock = otherSock; |
| tcti_intel->tpmSock = tpmSock; |
| rval = InitializeMsTpm2Simulator (tctiContext); |
| } else { |
| CloseSockets (otherSock, tpmSock); |
| } |
| |
| return rval; |
| } |
| /* |
| * This is a utility function to extract a TCP port number from a string. |
| * The string must be 6 characters long. If the supplied string contains an |
| * invalid port number then 0 is returned. |
| */ |
| static uint16_t |
| string_to_port (char port_str[6]) |
| { |
| uint32_t port = 0; |
| |
| if (sscanf (port_str, "%" SCNu32, &port) == EOF || port > UINT16_MAX) { |
| return 0; |
| } |
| return port; |
| } |
| |
| TSS2_RC Tss2_Tcti_Socket_Init ( |
| TSS2_TCTI_CONTEXT *tctiContext, |
| size_t *size, |
| const char *conf |
| ) |
| { |
| TCTI_SOCKET_CONF sock_conf = { 0 }; |
| TSS2_RC rc; |
| UriParserStateA state; |
| UriUriA uri; |
| const char *uri_str = conf != NULL ? conf : TCTI_SOCKET_DEFAULT; |
| size_t range; |
| /* maximum 5 digits in uint16_t + 1 for \0 */ |
| char port[6] = { 0 }; |
| char hostname[HOST_NAME_MAX + 1] = { 0 }; |
| |
| state.uri = &uri; |
| if (uriParseUriA (&state, uri_str) != URI_SUCCESS) { |
| rc = TSS2_TCTI_RC_BAD_VALUE; |
| goto out; |
| } |
| |
| /* extract host & domain name / fqdn */ |
| range = uri.hostText.afterLast - uri.hostText.first; |
| if (range > HOST_NAME_MAX) { |
| rc = TSS2_TCTI_RC_BAD_VALUE; |
| goto out; |
| } |
| strncpy (hostname, uri.hostText.first, range); |
| sock_conf.hostname = hostname; |
| |
| /* extract port number */ |
| range = uri.portText.afterLast - uri.portText.first; |
| if (range <= 5 && range > 0) { |
| strncpy (port, uri.portText.first, range); |
| sock_conf.port = string_to_port (port); |
| if (sock_conf.port == 0) { |
| rc = TSS2_TCTI_RC_BAD_VALUE; |
| goto out; |
| } |
| } else if (range == 0) { |
| sock_conf.port = DEFAULT_SIMULATOR_TPM_PORT; |
| } else { /* range > 5 */ |
| rc = TSS2_TCTI_RC_BAD_VALUE; |
| goto out; |
| } |
| rc = InitSocketTcti (tctiContext, size, &sock_conf, 0); |
| out: |
| uriFreeUriMembersA (&uri); |
| return rc; |
| } |
| |
| /* public info structure */ |
| const static TSS2_TCTI_INFO tss2_tcti_info = { |
| .name = "tcti-socket", |
| .description = "TCTI module for communication with the Microsoft TPM2 Simulator.", |
| .config_help = "Connection URI in the form tcp://ip_address[:port]. Default is tcp://127.0.0.1:2321.", |
| .init = Tss2_Tcti_Socket_Init, |
| }; |
| |
| const TSS2_TCTI_INFO* |
| Tss2_Tcti_Info (void) |
| { |
| return &tss2_tcti_info; |
| } |