diff --git a/CMakeLists.txt b/CMakeLists.txt index 70600ed08..148034bde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -577,6 +577,7 @@ if(WOLFTPM_FWTPM) add_executable(fwtpm_unit_test tests/fwtpm_unit_tests.c ${FWTPM_CORE_SOURCES} + tests/fwtpm_io_test_adapter.c ) target_compile_definitions(fwtpm_unit_test PRIVATE "WOLFTPM_FWTPM" @@ -594,6 +595,8 @@ if(WOLFTPM_FWTPM) if(UNIX) target_link_libraries(fwtpm_unit_test PRIVATE ${WOLFTPM_FWTPM_THREAD_LIBS}) + elseif(WIN32) + target_link_libraries(fwtpm_unit_test PRIVATE ws2_32) endif() if(WOLFTPM_FWTPM_TIS) diff --git a/docs/fwtpm/hal-and-porting.md b/docs/fwtpm/hal-and-porting.md index b16b75c0f..6733c8095 100644 --- a/docs/fwtpm/hal-and-porting.md +++ b/docs/fwtpm/hal-and-porting.md @@ -1,26 +1,29 @@ # HAL and Porting -The fwTPM provides hardware abstraction layers (HALs) so it can be ported to embedded targets without modifying the core logic. There is an IO HAL for the transport, an NV HAL for persistent storage, and an optional clock HAL. Full reference ports for several boards live in the [wolftpm-examples](https://github.com/wolfSSL/wolftpm-examples) repository. +The fwTPM provides hardware abstraction layers (HALs) for the server transport, persistent storage, and the platform clock. Full reference ports for several boards live in the [wolftpm-examples](https://github.com/wolfSSL/wolftpm-examples) repository. ## IO HAL (Transport) -The IO HAL abstracts the transport between the fwTPM server and its clients. The default implementation uses TCP sockets (SWTPM protocol). For embedded targets, replace it with SPI, I2C, UART, or shared memory callbacks. +The IO HAL abstracts the transport between the fwTPM server and its clients. Socket builds use TCP sockets (SWTPM protocol) by default. Registering an `FWTPM_IO_HAL` replaces the socket transport with callbacks for an embedded or in-process transport. TIS builds use their separate TIS HAL. **Callback structure** (defined as `FWTPM_IO_HAL` in `fwtpm.h`): | Callback | Signature | Description | |----------|-----------|-------------| -| `send` | `int (*)(void* ctx, const void* buf, int sz)` | Send data to client | -| `recv` | `int (*)(void* ctx, void* buf, int sz)` | Receive data from client | -| `wait` | `int (*)(void* ctx)` | Wait for data or connections. Returns a bitmask: `0x01`=command data, `0x02`=platform data, `0x04`=new command connection, `0x08`=new platform connection | +| `send` | `int (*)(void* ctx, int type, const void* buf, int sz)` | Send data to the command or platform client | +| `recv` | `int (*)(void* ctx, int type, void* buf, int sz)` | Receive data from the command or platform client | +| `wait` | `int (*)(void* ctx, int timeoutMs)` | Wait up to `timeoutMs` for data or connections. Returns 0 on timeout or a bitmask: `0x01`=command data, `0x02`=platform data, `0x04`=new command connection, `0x08`=new platform connection | | `accept` | `int (*)(void* ctx, int type)` | Accept new connection (type: 0=command, 1=platform) | | `close_conn` | `void (*)(void* ctx, int type)` | Close connection (type: 0=command, 1=platform) | | `ctx` | `void*` | User context pointer | +The `type` argument is `FWTPM_IO_TYPE_COMMAND` or `FWTPM_IO_TYPE_PLATFORM`. The `send`, `recv`, and `wait` callbacks are required. `wait` must return by its requested timeout so stop requests are observed; override `FWTPM_IO_WAIT_TIMEOUT_MS` to change the default 1000 ms interval. Connectionless transports may omit `accept` and `close_conn`; a transport that provides `accept` must also provide `close_conn` and clean up any partial connection state before returning an accept error. + **Registration:** ```c FWTPM_IO_HAL myHal; +XMEMSET(&myHal, 0, sizeof(myHal)); myHal.send = my_send; myHal.recv = my_recv; myHal.wait = my_wait; @@ -31,6 +34,8 @@ myHal.ctx = &myTransportCtx; FWTPM_IO_SetHAL(&ctx, &myHal); ``` +Register the IO HAL before `FWTPM_Init()`, or after `FWTPM_Init()` but before `FWTPM_IO_Init()`. + ## NV HAL (Persistent Storage) The NV HAL abstracts persistent storage. The default implementation uses a local file (`fwtpm_nv.bin`). For embedded targets, replace it with flash, EEPROM, or other non-volatile storage callbacks. @@ -101,10 +106,12 @@ The clock HAL is optional. It supplies `get_ms()`, which returns milliseconds si ## Porting Example -A bare-metal embedded target with SPI transport and SPI flash NV: +A bare-metal target can register SPI transport and SPI flash NV callbacks before initialization: ```c FWTPM_CTX ctx; +int cleanupRc; +int rc; XMEMSET(&ctx, 0, sizeof(ctx)); /* Set custom NV storage before FWTPM_Init, which loads NV state through it */ @@ -115,25 +122,29 @@ FWTPM_NV_HAL nvHal = { }; FWTPM_NV_SetHAL(&ctx, &nvHal); -FWTPM_Init(&ctx); - -/* Set custom IO transport after FWTPM_Init, which does not preserve the IO HAL */ +/* Register a connectionless IO transport before initialization. */ FWTPM_IO_HAL ioHal = { .send = spi_slave_send, .recv = spi_slave_recv, .wait = spi_slave_poll, - .accept = NULL, /* not connection-oriented */ + .accept = NULL, .close_conn = NULL, .ctx = &spiHandle }; FWTPM_IO_SetHAL(&ctx, &ioHal); -/* Initialize IO and run */ -FWTPM_IO_Init(&ctx); -FWTPM_IO_ServerLoop(&ctx); /* blocks */ - -FWTPM_IO_Cleanup(&ctx); -FWTPM_Cleanup(&ctx); +rc = FWTPM_Init(&ctx); +if (rc == 0) { + rc = FWTPM_IO_Init(&ctx); + if (rc == 0) { + rc = FWTPM_IO_ServerLoop(&ctx); /* blocks */ + FWTPM_IO_Cleanup(&ctx); + } + cleanupRc = FWTPM_Cleanup(&ctx); + if (rc == 0) + rc = cleanupRc; +} +return rc; ``` ## Available Ports diff --git a/docs/fwtpm/usage.md b/docs/fwtpm/usage.md index 4498d4fb8..a2c704792 100644 --- a/docs/fwtpm/usage.md +++ b/docs/fwtpm/usage.md @@ -241,9 +241,9 @@ scripts/tpm2_tools_test.sh | Function | Description | |----------|-------------| | `int FWTPM_IO_SetHAL(FWTPM_CTX* ctx, FWTPM_IO_HAL* hal)` | Register custom IO transport callbacks | -| `int FWTPM_IO_Init(FWTPM_CTX* ctx)` | Initialize transport (sockets or custom HAL) | +| `int FWTPM_IO_Init(FWTPM_CTX* ctx)` | Initialize the custom HAL, TCP socket, or TIS transport | | `void FWTPM_IO_Cleanup(FWTPM_CTX* ctx)` | Close transport and release resources | -| `int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx)` | Main server loop; blocks until `ctx->running` is cleared | +| `int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx)` | Run the selected transport loop; blocks until `ctx->running` is cleared | ### NV Storage (`fwtpm_nv.h`) diff --git a/docs/ja/fwtpm/hal-and-porting.md b/docs/ja/fwtpm/hal-and-porting.md index 3d8dd58ee..bf54504cf 100644 --- a/docs/ja/fwtpm/hal-and-porting.md +++ b/docs/ja/fwtpm/hal-and-porting.md @@ -1,26 +1,29 @@ # HAL と移植 -fwTPM はハードウェア抽象化レイヤー (HAL) を提供しており、コアロジックを変更せずに組み込みターゲットへ移植できます。トランスポート用の IO HAL、永続ストレージ用の NV HAL、およびオプションのクロック HAL があります。複数のボード向けの完全なリファレンス移植は、[wolftpm-examples](https://github.com/wolfSSL/wolftpm-examples) リポジトリにあります。 +fwTPM は、サーバートランスポート、永続ストレージ、プラットフォームクロック用のハードウェア抽象化レイヤー (HAL) を提供します。複数のボード向けの完全なリファレンス移植は、[wolftpm-examples](https://github.com/wolfSSL/wolftpm-examples) リポジトリにあります。 ## IO HAL (トランスポート) -IO HAL は、fwTPM サーバーとそのクライアント間のトランスポートを抽象化します。デフォルトの実装は TCP ソケット (SWTPM プロトコル) を使用します。組み込みターゲットでは、SPI、I2C、UART、または共有メモリのコールバックに置き換えます。 +IO HAL は、fwTPM サーバーとクライアント間のトランスポートを抽象化します。ソケットビルドでは、デフォルトで TCP ソケット (SWTPM プロトコル) を使用します。`FWTPM_IO_HAL` を登録すると、ソケットトランスポートは組み込み環境またはインプロセストランスポート用のコールバックに置き換わります。TIS ビルドでは、別の TIS HAL を使用します。 **コールバック構造体** (`fwtpm.h` で `FWTPM_IO_HAL` として定義): | コールバック | シグネチャ | 説明 | |----------|-----------|-------------| -| `send` | `int (*)(void* ctx, const void* buf, int sz)` | クライアントへデータを送信 | -| `recv` | `int (*)(void* ctx, void* buf, int sz)` | クライアントからデータを受信 | -| `wait` | `int (*)(void* ctx)` | データまたは接続を待機。ビットマスクを返す: `0x01`=コマンドデータ、`0x02`=プラットフォームデータ、`0x04`=新しいコマンド接続、`0x08`=新しいプラットフォーム接続 | +| `send` | `int (*)(void* ctx, int type, const void* buf, int sz)` | コマンドまたはプラットフォームクライアントへデータを送信 | +| `recv` | `int (*)(void* ctx, int type, void* buf, int sz)` | コマンドまたはプラットフォームクライアントからデータを受信 | +| `wait` | `int (*)(void* ctx, int timeoutMs)` | `timeoutMs` までデータまたは接続を待機。タイムアウト時は 0、それ以外はビットマスクを返す: `0x01`=コマンドデータ、`0x02`=プラットフォームデータ、`0x04`=新しいコマンド接続、`0x08`=新しいプラットフォーム接続 | | `accept` | `int (*)(void* ctx, int type)` | 新しい接続を受け入れる (type: 0=コマンド、1=プラットフォーム) | | `close_conn` | `void (*)(void* ctx, int type)` | 接続を閉じる (type: 0=コマンド、1=プラットフォーム) | | `ctx` | `void*` | ユーザーコンテキストポインタ | +`type` 引数には `FWTPM_IO_TYPE_COMMAND` または `FWTPM_IO_TYPE_PLATFORM` を指定します。`send`、`recv`、`wait` コールバックは必須です。停止要求を検出できるように、`wait` は指定されたタイムアウトまでに戻る必要があります。デフォルトの 1000 ms 間隔は `FWTPM_IO_WAIT_TIMEOUT_MS` で変更できます。コネクションレス型トランスポートでは `accept` と `close_conn` を省略できます。`accept` を指定する場合は `close_conn` も指定し、`accept` がエラーを返す前に未完了の接続状態を解放する必要があります。 + **登録:** ```c FWTPM_IO_HAL myHal; +XMEMSET(&myHal, 0, sizeof(myHal)); myHal.send = my_send; myHal.recv = my_recv; myHal.wait = my_wait; @@ -31,6 +34,8 @@ myHal.ctx = &myTransportCtx; FWTPM_IO_SetHAL(&ctx, &myHal); ``` +IO HAL は `FWTPM_Init()` の前、または `FWTPM_Init()` の後かつ `FWTPM_IO_Init()` の前に登録してください。 + ## NV HAL (永続ストレージ) NV HAL は永続ストレージを抽象化します。デフォルトの実装はローカルファイル (`fwtpm_nv.bin`) を使用します。組み込みターゲットでは、フラッシュ、EEPROM、その他の不揮発性ストレージのコールバックに置き換えます。 @@ -101,10 +106,12 @@ FWTPM_NV_SetHAL(&ctx, &hal); /* before FWTPM_Init() */ ## 移植の例 -SPI トランスポートと SPI フラッシュの NV を使用するベアメタルの組み込みターゲットの例です。 +ベアメタルターゲットでは、初期化前に SPI トランスポートと SPI フラッシュ NV のコールバックを登録できます。 ```c FWTPM_CTX ctx; +int cleanupRc; +int rc; XMEMSET(&ctx, 0, sizeof(ctx)); /* Set custom NV storage before FWTPM_Init, which loads NV state through it */ @@ -115,25 +122,29 @@ FWTPM_NV_HAL nvHal = { }; FWTPM_NV_SetHAL(&ctx, &nvHal); -FWTPM_Init(&ctx); - -/* Set custom IO transport after FWTPM_Init, which does not preserve the IO HAL */ +/* Register a connectionless IO transport before initialization. */ FWTPM_IO_HAL ioHal = { .send = spi_slave_send, .recv = spi_slave_recv, .wait = spi_slave_poll, - .accept = NULL, /* not connection-oriented */ + .accept = NULL, .close_conn = NULL, .ctx = &spiHandle }; FWTPM_IO_SetHAL(&ctx, &ioHal); -/* Initialize IO and run */ -FWTPM_IO_Init(&ctx); -FWTPM_IO_ServerLoop(&ctx); /* blocks */ - -FWTPM_IO_Cleanup(&ctx); -FWTPM_Cleanup(&ctx); +rc = FWTPM_Init(&ctx); +if (rc == 0) { + rc = FWTPM_IO_Init(&ctx); + if (rc == 0) { + rc = FWTPM_IO_ServerLoop(&ctx); /* blocks */ + FWTPM_IO_Cleanup(&ctx); + } + cleanupRc = FWTPM_Cleanup(&ctx); + if (rc == 0) + rc = cleanupRc; +} +return rc; ``` ## 利用可能な移植 diff --git a/docs/ja/fwtpm/usage.md b/docs/ja/fwtpm/usage.md index 55f1a5040..b7aa9112d 100644 --- a/docs/ja/fwtpm/usage.md +++ b/docs/ja/fwtpm/usage.md @@ -240,10 +240,10 @@ scripts/tpm2_tools_test.sh | 関数 | 説明 | |----------|-------------| -| `int FWTPM_IO_SetHAL(FWTPM_CTX* ctx, FWTPM_IO_HAL* hal)` | カスタム IO トランスポートのコールバックを登録 | -| `int FWTPM_IO_Init(FWTPM_CTX* ctx)` | トランスポート (ソケットまたはカスタム HAL) を初期化 | +| `int FWTPM_IO_SetHAL(FWTPM_CTX* ctx, FWTPM_IO_HAL* hal)` | カスタム IO トランスポートコールバックを登録 | +| `int FWTPM_IO_Init(FWTPM_CTX* ctx)` | カスタム HAL、TCP ソケット、または TIS トランスポートを初期化 | | `void FWTPM_IO_Cleanup(FWTPM_CTX* ctx)` | トランスポートを閉じ、リソースを解放 | -| `int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx)` | メインのサーバーループ。`ctx->running` がクリアされるまでブロック | +| `int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx)` | 選択されたトランスポートのサーバーループを実行。`ctx->running` がクリアされるまでブロック | ### NV ストレージ (`fwtpm_nv.h`) diff --git a/hal/tpm_io_atmel.c b/hal/tpm_io_atmel.c index 1ea478b2f..5e7e0093c 100644 --- a/hal/tpm_io_atmel.c +++ b/hal/tpm_io_atmel.c @@ -52,6 +52,10 @@ #define SPI_BAUD_RATE_4M 21 #define CS_SPI_TPM 2 + #ifndef WOLFTPM_ATMEL_SPI_POLL_TRIES + #define WOLFTPM_ATMEL_SPI_POLL_TRIES TPM_TIMEOUT_TRIES + #endif + /* Atmel ATSAM3X8EA Chip Selects */ static const byte Spi_CS[] = { 0, 1, 2, 3 }; static const byte Spi_PCS[] = { 0x0E, 0x0D, 0x0B, 0x07 }; @@ -89,16 +93,38 @@ return pcs; } + static int WaitSPI_TPM(word32 statusMask) + { + word32 tries = WOLFTPM_ATMEL_SPI_POLL_TRIES; + + while ((SPI0->SPI_SR & statusMask) == 0U) { + if (tries == 0U) { + return TPM_RC_FAILURE; + } + tries--; + } + return TPM_RC_SUCCESS; + } + static int XferSPI_TPM(byte pcs, const byte* pSendBuf, byte* pReadBuf, word16 wLen) { int ret = TPM_RC_SUCCESS; word16 i; for (i = 0; i < wLen; i++) { - while ((SPI0->SPI_SR & SPI_SR_TXEMPTY) == 0); - SPI0->SPI_TDR = (word16)pSendBuf[i] | (pcs << 16); - while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + ret = WaitSPI_TPM(SPI_SR_TXEMPTY); + if (ret != TPM_RC_SUCCESS) { + break; + } + SPI0->SPI_TDR = (word16)pSendBuf[i] | (pcs << 16); + ret = WaitSPI_TPM(SPI_SR_TDRE); + if (ret != TPM_RC_SUCCESS) { + break; + } + ret = WaitSPI_TPM(SPI_SR_RDRF); + if (ret != TPM_RC_SUCCESS) { + break; + } pReadBuf[i] = SPI0->SPI_RDR & 0x00FF; } @@ -136,7 +162,7 @@ #ifdef WOLFTPM_DEBUG_TIMEOUT printf("SPI Ready Wait %d\n", TPM_SPI_WAIT_RETRY - timeout); #endif - if (timeout <= 0) { + if (ret != TPM_RC_SUCCESS || timeout <= 0) { SPI0->SPI_CR = SPI_CR_SPIDIS; return TPM_RC_FAILURE; } diff --git a/hal/tpm_io_espressif.c b/hal/tpm_io_espressif.c index a68374631..da7a14519 100644 --- a/hal/tpm_io_espressif.c +++ b/hal/tpm_io_espressif.c @@ -460,8 +460,8 @@ int TPM2_IoCb_Espressif_I2C(TPM2_CTX* ctx, int isRead, word32 addr, /* If failures are encountered, try hardware reset of TPM device. */ #if defined(CONFIG_IDF_TARGET_ESP32S3) - #ifndef ESP_SPI_MAX_TRANSFER - #define ESP_SPI_MAX_TRANSFER_SIZE MAX_SPI_FRAMESIZE + 4 + #ifndef ESP_SPI_MAX_TRANSFER_SIZE + #define ESP_SPI_MAX_TRANSFER_SIZE (MAX_SPI_FRAMESIZE + 4) #endif /* FSPI (HOST_SPI2) on esp32-s3-wroom */ #ifndef PIN_NUM_MISO @@ -477,8 +477,8 @@ int TPM2_IoCb_Espressif_I2C(TPM2_CTX* ctx, int isRead, word32 addr, #define PIN_NUM_CS 10 #endif #elif defined(CONFIG_IDF_TARGET_ESP32C6) - #ifndef ESP_SPI_MAX_TRANSFER - #define ESP_SPI_MAX_TRANSFER_SIZE 1024 + #ifndef ESP_SPI_MAX_TRANSFER_SIZE + #define ESP_SPI_MAX_TRANSFER_SIZE (1024) #endif #ifndef PIN_NUM_MISO #define PIN_NUM_MISO 2 @@ -497,8 +497,8 @@ int TPM2_IoCb_Espressif_I2C(TPM2_CTX* ctx, int isRead, word32 addr, #define PIN_NUM_SPICS_IO -1 #endif #else - #ifndef ESP_SPI_MAX_TRANSFER - #define ESP_SPI_MAX_TRANSFER_SIZE MAX_SPI_FRAMESIZE + #ifndef ESP_SPI_MAX_TRANSFER_SIZE + #define ESP_SPI_MAX_TRANSFER_SIZE (MAX_SPI_FRAMESIZE) #endif #ifndef PIN_NUM_MISO #error PIN_NUM_MISO undefined diff --git a/hal/tpm_io_fwtpm.c b/hal/tpm_io_fwtpm.c index 780d3beb0..43e73bb0c 100644 --- a/hal/tpm_io_fwtpm.c +++ b/hal/tpm_io_fwtpm.c @@ -161,9 +161,9 @@ int FWTPM_TIS_ClientConnect(FWTPM_TIS_CLIENT_CTX* client) (void)fdFlags; #endif - /* Authenticate the data-bearing endpoint before mapping it. Named - * semaphores carry wakeups only and are derived from this validated file - * owner below. */ + /* Authenticate the data-bearing endpoint before mapping it. The named + * semaphores carry wakeups only and use fixed, host-wide names; trust + * comes from validating the shared-memory file below. */ if (fstat(fd, &st) != 0) { #ifdef DEBUG_WOLFTPM printf("fwTPM HAL: fstat(%s) failed: %d (%s)\n", @@ -255,7 +255,7 @@ int FWTPM_TIS_ClientConnect(FWTPM_TIS_CLIENT_CTX* client) } /* Reject a replaced pathname or metadata change after opening the - * per-owner semaphore pair. */ + * fixed-name semaphore pair. */ if (fstat(fd, &st) != 0 || FWTPM_TIS_ClientValidateShm(&st) != TPM_RC_SUCCESS || lstat(FWTPM_TIS_SHM_PATH, &pathSt) != 0 || diff --git a/hal/tpm_io_infineon.c b/hal/tpm_io_infineon.c index 3ba116b6d..edda19086 100644 --- a/hal/tpm_io_infineon.c +++ b/hal/tpm_io_infineon.c @@ -54,6 +54,11 @@ #if defined(CY_USING_HAL) #include "cyhal_i2c.h" + #include "cyhal_system.h" + + #define TPM_I2C_STS_REG 0x18u + #define TPM_I2C_GUARD_TIME_US 250 + #define TPM_I2C_RETRY_DELAY_MS 1 static int tpm_ifx_i2c_read(void* userCtx, word32 reg, byte* data, int len) { @@ -75,8 +80,18 @@ /* Write address to read from - retry until ack */ result = cyhal_i2c_master_write(i2c, TPM2_I2C_ADDR, buf, sizeof(buf), 0, true); - /* for read we always need this guard time (success wake or real read) */ - XSLEEP_MS(1); /* guard time - should be 250us */ + if (result == CY_RSLT_SUCCESS) { + /* Status polling during command execution requires 1 ms. */ + if (buf[0] == TPM_I2C_STS_REG) { + XSLEEP_MS(TPM_I2C_RETRY_DELAY_MS); + } + else { + cyhal_system_delay_us(TPM_I2C_GUARD_TIME_US); + } + } + else { + XSLEEP_MS(TPM_I2C_RETRY_DELAY_MS); + } } while (result != CY_RSLT_SUCCESS && --timeout > 0); if (result == CY_RSLT_SUCCESS) { @@ -85,7 +100,7 @@ result = cyhal_i2c_master_read(i2c, TPM2_I2C_ADDR, data, len, 0, true); if (result != CY_RSLT_SUCCESS) { - XSLEEP_MS(1); /* guard time - should be 250us */ + XSLEEP_MS(TPM_I2C_RETRY_DELAY_MS); } } while (result != CY_RSLT_SUCCESS && --timeout > 0); } @@ -121,7 +136,7 @@ result = cyhal_i2c_master_write(i2c, TPM2_I2C_ADDR, buf, len+1, 0, true); if (result != CY_RSLT_SUCCESS) { - XSLEEP_MS(1); /* guard time - should be 250us */ + XSLEEP_MS(TPM_I2C_RETRY_DELAY_MS); } } while (result != CY_RSLT_SUCCESS && --timeout > 0); if (result == CY_RSLT_SUCCESS) { diff --git a/hal/tpm_io_microchip.c b/hal/tpm_io_microchip.c index 751921800..ad53f5785 100644 --- a/hal/tpm_io_microchip.c +++ b/hal/tpm_io_microchip.c @@ -201,6 +201,7 @@ if (!queued) { printf("error: i2c_read: I2C_BB_Read failed\n"); + TPM2_ForceZero(i2cRdBuf, sizeof(i2cRdBuf)); return -1; } @@ -376,12 +377,13 @@ int TPM2_IoCb_Microchip_SPI(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf, setup.dataBits = DRV_SPI_DATA_BITS_8; setup.chipSelect = TPM_SPI_PIN; setup.csPolarity = DRV_SPI_CS_POLARITY_ACTIVE_LOW; - DRV_SPI_TransferSetup(handle, &setup); - /* Send Entire Message blocking - no wait states */ - if (DRV_SPI_WriteReadTransfer(handle, (byte*)txBuf, xferSz, rxBuf, - xferSz) == true) { - ret = TPM_RC_SUCCESS; + if (DRV_SPI_TransferSetup(handle, &setup) == true) { + /* Send Entire Message blocking - no wait states */ + if (DRV_SPI_WriteReadTransfer(handle, (byte*)txBuf, xferSz, rxBuf, + xferSz) == true) { + ret = TPM_RC_SUCCESS; + } } (void)ctx; diff --git a/src/fwtpm/fwtpm.c b/src/fwtpm/fwtpm.c index fb7e8c0a9..893d930ed 100644 --- a/src/fwtpm/fwtpm.c +++ b/src/fwtpm/fwtpm.c @@ -48,6 +48,8 @@ int FWTPM_Init(FWTPM_CTX* ctx) #endif #ifdef WOLFTPM_FWTPM_TIS FWTPM_TIS_HAL savedTisHal; +#else + FWTPM_IO_HAL savedIoHal; #endif if (ctx == NULL) { @@ -62,6 +64,8 @@ int FWTPM_Init(FWTPM_CTX* ctx) #endif #ifdef WOLFTPM_FWTPM_TIS XMEMCPY(&savedTisHal, &ctx->tisHal, sizeof(savedTisHal)); +#else + XMEMCPY(&savedIoHal, &ctx->ioHal, sizeof(savedIoHal)); #endif XMEMSET(ctx, 0, sizeof(FWTPM_CTX)); @@ -82,6 +86,10 @@ int FWTPM_Init(FWTPM_CTX* ctx) if (savedTisHal.init != NULL) { XMEMCPY(&ctx->tisHal, &savedTisHal, sizeof(savedTisHal)); } +#else + if (savedIoHal.wait != NULL) { + XMEMCPY(&ctx->ioHal, &savedIoHal, sizeof(savedIoHal)); + } #endif #ifndef WOLFTPM_FWTPM_TIS diff --git a/src/fwtpm/fwtpm_io.c b/src/fwtpm/fwtpm_io.c index f25584276..2137dd999 100644 --- a/src/fwtpm/fwtpm_io.c +++ b/src/fwtpm/fwtpm_io.c @@ -85,6 +85,7 @@ int FWTPM_IO_IsStopRequested(void) /* --- Low-level socket helpers --- */ #define FWTPM_MAX_COMMAND_CLIENTS 8 +#define FWTPM_IO_CONNECTION_CLOSED (-1) static int SocketSend(SOCKET_T fd, const void* buf, int sz) { @@ -157,6 +158,24 @@ static int SocketRecv(SOCKET_T fd, void* buf, int sz) return TPM_RC_SUCCESS; } +static int TransportSend(FWTPM_CTX* ctx, int type, SOCKET_T fd, + const void* buf, int sz) +{ + if (ctx->ioHal.wait != NULL) { + return ctx->ioHal.send(ctx->ioHal.ctx, type, buf, sz); + } + return SocketSend(fd, buf, sz); +} + +static int TransportRecv(FWTPM_CTX* ctx, int type, SOCKET_T fd, + void* buf, int sz) +{ + if (ctx->ioHal.wait != NULL) { + return ctx->ioHal.recv(ctx->ioHal.ctx, type, buf, sz); + } + return SocketRecv(fd, buf, sz); +} + static SOCKET_T CreateListenSocket(int port) { SOCKET_T fd; @@ -225,7 +244,7 @@ static int HandlePlatformCommand(FWTPM_CTX* ctx, SOCKET_T clientFd) UINT32 cmd; UINT32 ack = 0; - rc = SocketRecv(clientFd, &cmd, 4); + rc = TransportRecv(ctx, FWTPM_IO_TYPE_PLATFORM, clientFd, &cmd, 4); if (rc != TPM_RC_SUCCESS) { return rc; } @@ -295,7 +314,7 @@ static int HandlePlatformCommand(FWTPM_CTX* ctx, SOCKET_T clientFd) #ifdef DEBUG_WOLFTPM printf("fwTPM: Platform SESSION_END\n"); #endif - return TPM_RC_SUCCESS; /* no ack for session end */ + return FWTPM_IO_CONNECTION_CLOSED; /* no ack */ case FWTPM_TCP_STOP: #ifdef DEBUG_WOLFTPM @@ -313,7 +332,7 @@ static int HandlePlatformCommand(FWTPM_CTX* ctx, SOCKET_T clientFd) /* Send ack (0 = success) */ ack = FwTpmSwapU32(0); - rc = SocketSend(clientFd, &ack, 4); + rc = TransportSend(ctx, FWTPM_IO_TYPE_PLATFORM, clientFd, &ack, 4); return rc; } @@ -321,7 +340,30 @@ static int HandlePlatformCommand(FWTPM_CTX* ctx, SOCKET_T clientFd) /* --- Handle mssim signal on command port --- */ static int HandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, UINT32 tssCmd) { + int rc; + UINT32 dataSize; UINT32 netVal; + + if (tssCmd == FWTPM_TCP_SIGNAL_HASH_DATA) { + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &netVal, + (int)sizeof(netVal)); + if (rc != TPM_RC_SUCCESS) { + return rc; + } + dataSize = FwTpmSwapU32(netVal); + if (dataSize > FWTPM_MAX_COMMAND_SIZE) { + return TPM_RC_COMMAND_SIZE; + } + if (dataSize > 0U) { + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, + ctx->cmdBuf, (int)dataSize); + TPM2_ForceZero(ctx->cmdBuf, dataSize); + if (rc != TPM_RC_SUCCESS) { + return rc; + } + } + } + /* State-mutating signals (POWER_OFF/RESET) are rejected before reaching * here on the command port; only POWER_ON is handled. */ if (tssCmd == FWTPM_TCP_SIGNAL_POWER_ON) @@ -329,9 +371,20 @@ static int HandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, UINT32 tssCmd) #ifdef DEBUG_WOLFTPM printf("fwTPM: Cmd-port signal %u (ack)\n", tssCmd); #endif - netVal = FwTpmSwapU32(0); - return SocketSend(clientFd, &netVal, 4); + netVal = FwTpmSwapU32(tssCmd == FWTPM_TCP_SIGNAL_HASH_DATA ? 1U : 0U); + return TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &netVal, 4); +} + +#ifdef WOLFTPM_FWTPM_UNIT_TEST +int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, + UINT32 tssCmd); + +int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, + UINT32 tssCmd) +{ + return HandleMssimSignal(ctx, clientFd, tssCmd); } +#endif /* --- Process and send TPM command response --- */ static int DispatchAndRespond(FWTPM_CTX* ctx, UINT32 cmdSize, int locality, @@ -402,18 +455,21 @@ static int DispatchAndRespond(FWTPM_CTX* ctx, UINT32 cmdSize, int locality, if (isSwtpm) { /* swtpm protocol: raw TPM response only (no framing) */ - rc = SocketSend(clientFd, ctx->rspBuf, rspSize); + rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, + ctx->rspBuf, rspSize); } else { /* mssim protocol: size(4) + response + ack(4) */ netVal = FwTpmSwapU32((UINT32)rspSize); - rc = SocketSend(clientFd, &netVal, 4); + rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &netVal, 4); if (rc == TPM_RC_SUCCESS) { - rc = SocketSend(clientFd, ctx->rspBuf, rspSize); + rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, + ctx->rspBuf, rspSize); } if (rc == TPM_RC_SUCCESS) { netVal = FwTpmSwapU32(0); /* ack = 0 (success) */ - rc = SocketSend(clientFd, &netVal, 4); + rc = TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, + &netVal, 4); } } @@ -457,7 +513,7 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) UINT32 netVal; /* Read first 4 bytes to determine protocol */ - rc = SocketRecv(clientFd, &firstWord, 4); + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &firstWord, 4); if (rc != TPM_RC_SUCCESS) { return rc; } @@ -476,7 +532,8 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) /* We already have 4 bytes (tag + start of size). Read 6 more to * complete the 10-byte TPM header (tag(2) + size(4) + cc(4)). */ XMEMCPY(ctx->cmdBuf, &firstWord, 4); - rc = SocketRecv(clientFd, ctx->cmdBuf + 4, 6); + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, + ctx->cmdBuf + 4, 6); if (rc != TPM_RC_SUCCESS) { return rc; } @@ -492,8 +549,8 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) /* Read remaining command bytes */ remaining = cmdSize - TPM2_HEADER_SIZE; if (remaining > 0) { - rc = SocketRecv(clientFd, ctx->cmdBuf + TPM2_HEADER_SIZE, - (int)remaining); + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, + ctx->cmdBuf + TPM2_HEADER_SIZE, (int)remaining); if (rc != TPM_RC_SUCCESS) { return rc; } @@ -513,7 +570,7 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) #ifdef DEBUG_WOLFTPM printf("fwTPM: Command SESSION_END\n"); #endif - return TPM_RC_SUCCESS; + return FWTPM_IO_CONNECTION_CLOSED; } /* State-mutating platform signals belong on the platform port, not the @@ -537,12 +594,12 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) } /* mssim SEND_COMMAND: locality(1) + size(4) + command */ - rc = SocketRecv(clientFd, &locality, 1); + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &locality, 1); if (rc != TPM_RC_SUCCESS) { return rc; } - rc = SocketRecv(clientFd, &netVal, 4); + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &netVal, 4); if (rc != TPM_RC_SUCCESS) { return rc; } @@ -555,7 +612,8 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) return TPM_RC_COMMAND_SIZE; } - rc = SocketRecv(clientFd, ctx->cmdBuf, (int)cmdSize); + rc = TransportRecv(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, ctx->cmdBuf, + (int)cmdSize); if (rc != TPM_RC_SUCCESS) { return rc; } @@ -563,6 +621,90 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) return DispatchAndRespond(ctx, cmdSize, (int)locality, clientFd, 0); } +static void CustomOpen(FWTPM_CTX* ctx, int type) +{ + ctx->io.customConnMask |= 1U << (unsigned int)type; +} + +static void CustomClose(FWTPM_CTX* ctx, int type) +{ + unsigned int mask = 1U << (unsigned int)type; + + if ((ctx->io.customConnMask & mask) != 0U) { + ctx->io.customConnMask &= ~mask; + if (ctx->ioHal.close_conn != NULL) { + ctx->ioHal.close_conn(ctx->ioHal.ctx, type); + } + } +} + +static int CustomServerLoop(FWTPM_CTX* ctx) +{ + int rc = TPM_RC_SUCCESS; + int events; + + ctx->running = 1; + while (ctx->running) { + if (g_stopRequested) { + ctx->running = 0; + break; + } + + events = ctx->ioHal.wait(ctx->ioHal.ctx, + FWTPM_IO_WAIT_TIMEOUT_MS); + if (g_stopRequested) { + ctx->running = 0; + break; + } + if (events < 0 || + (events & ~(FWTPM_IO_READY_COMMAND | + FWTPM_IO_READY_PLATFORM | + FWTPM_IO_ACCEPT_COMMAND | + FWTPM_IO_ACCEPT_PLATFORM)) != 0) { + rc = TPM_RC_FAILURE; + break; + } + + if ((events & FWTPM_IO_ACCEPT_PLATFORM) != 0 && + ctx->ioHal.accept != NULL) { + if (ctx->ioHal.accept(ctx->ioHal.ctx, + FWTPM_IO_TYPE_PLATFORM) != TPM_RC_SUCCESS) { + events &= ~FWTPM_IO_READY_PLATFORM; + } + else { + CustomOpen(ctx, FWTPM_IO_TYPE_PLATFORM); + } + } + if ((events & FWTPM_IO_ACCEPT_COMMAND) != 0 && + ctx->ioHal.accept != NULL) { + if (ctx->ioHal.accept(ctx->ioHal.ctx, + FWTPM_IO_TYPE_COMMAND) != TPM_RC_SUCCESS) { + events &= ~FWTPM_IO_READY_COMMAND; + } + else { + CustomOpen(ctx, FWTPM_IO_TYPE_COMMAND); + } + } + + if ((events & FWTPM_IO_READY_PLATFORM) != 0) { + CustomOpen(ctx, FWTPM_IO_TYPE_PLATFORM); + if (HandlePlatformCommand(ctx, FWTPM_INVALID_FD) != + TPM_RC_SUCCESS) { + CustomClose(ctx, FWTPM_IO_TYPE_PLATFORM); + } + } + if ((events & FWTPM_IO_READY_COMMAND) != 0) { + CustomOpen(ctx, FWTPM_IO_TYPE_COMMAND); + if (HandleCommandConnection(ctx, FWTPM_INVALID_FD) != + TPM_RC_SUCCESS) { + CustomClose(ctx, FWTPM_IO_TYPE_COMMAND); + } + } + } + + return rc; +} + #endif /* !WOLFTPM_FWTPM_TIS */ /* --- Public API --- */ @@ -570,7 +712,9 @@ static int HandleCommandConnection(FWTPM_CTX* ctx, SOCKET_T clientFd) #ifndef WOLFTPM_FWTPM_TIS int FWTPM_IO_SetHAL(FWTPM_CTX* ctx, FWTPM_IO_HAL* hal) { - if (ctx == NULL || hal == NULL) { + if (ctx == NULL || hal == NULL || hal->send == NULL || + hal->recv == NULL || hal->wait == NULL || + (hal->accept != NULL && hal->close_conn == NULL)) { return BAD_FUNC_ARG; } XMEMCPY(&ctx->ioHal, hal, sizeof(FWTPM_IO_HAL)); @@ -591,6 +735,17 @@ int FWTPM_IO_Init(FWTPM_CTX* ctx) #ifdef WOLFTPM_FWTPM_TIS return FWTPM_TIS_Init(ctx); #else + if (ctx->ioHal.wait != NULL) { + if (ctx->ioHal.send == NULL || ctx->ioHal.recv == NULL) { + return BAD_FUNC_ARG; + } + ctx->io.customConnMask = 0U; + if (ctx->ioHal.accept == NULL) { + CustomOpen(ctx, FWTPM_IO_TYPE_COMMAND); + CustomOpen(ctx, FWTPM_IO_TYPE_PLATFORM); + } + return TPM_RC_SUCCESS; + } #ifdef _WIN32 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { fprintf(stderr, "fwTPM: WSAStartup failed\n"); @@ -643,6 +798,11 @@ void FWTPM_IO_Cleanup(FWTPM_CTX* ctx) #ifdef WOLFTPM_FWTPM_TIS FWTPM_TIS_Cleanup(ctx); #else + if (ctx->ioHal.wait != NULL) { + CustomClose(ctx, FWTPM_IO_TYPE_COMMAND); + CustomClose(ctx, FWTPM_IO_TYPE_PLATFORM); + return; + } if (ctx->io.clientFd != FWTPM_INVALID_FD) { CloseSocket(ctx->io.clientFd); ctx->io.clientFd = FWTPM_INVALID_FD; @@ -688,6 +848,10 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) #ifdef WOLFTPM_FWTPM_TIS return FWTPM_TIS_ServerLoop(ctx); #else + if (ctx->ioHal.wait != NULL) { + return CustomServerLoop(ctx); + } + ctx->running = 1; for (i = 0; i < FWTPM_MAX_COMMAND_CLIENTS; i++) { cmdFds[i] = FWTPM_INVALID_FD; diff --git a/src/fwtpm/fwtpm_main.c b/src/fwtpm/fwtpm_main.c index 80804d650..56ef4358d 100644 --- a/src/fwtpm/fwtpm_main.c +++ b/src/fwtpm/fwtpm_main.c @@ -44,6 +44,7 @@ #include #include #include +#include /* Signal handler does only async-signal-safe work: it calls * FWTPM_IO_RequestStop(), which sets a volatile sig_atomic_t. The server @@ -303,7 +304,11 @@ int main(int argc, char* argv[]) /* Delete NV state file if --clear was requested */ if (clearNv) { printf("Clearing NV state file: %s\n", FWTPM_NV_FILE); - (void)remove(FWTPM_NV_FILE); + if (remove(FWTPM_NV_FILE) != 0 && errno != ENOENT) { + fprintf(stderr, "Failed to clear NV state file %s: %s\n", + FWTPM_NV_FILE, strerror(errno)); + return 1; + } } /* Initialize fwTPM */ diff --git a/src/fwtpm/fwtpm_tis.c b/src/fwtpm/fwtpm_tis.c index 0169081db..e6f4730a8 100644 --- a/src/fwtpm/fwtpm_tis.c +++ b/src/fwtpm/fwtpm_tis.c @@ -131,8 +131,11 @@ static void TisHandleRegAccess(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs) } break; + case FWTPM_TIS_BURST_COUNT_REG: + /* The burst count is read-only. */ + break; + case FWTPM_TIS_STS: - case FWTPM_TIS_STS + 1: /* burst count write (ignored) */ /* A non-owning locality cannot ready/execute the FIFO */ if (!isOwner) { break; diff --git a/tests/fwtpm_hal_unit_tests.c b/tests/fwtpm_hal_unit_tests.c index 08ce281e8..5c3f942ba 100644 --- a/tests/fwtpm_hal_unit_tests.c +++ b/tests/fwtpm_hal_unit_tests.c @@ -27,6 +27,9 @@ #include #endif +#define WOLFTPM_FWTPM +#define WOLFTPM_FWTPM_TIS + #include #include @@ -88,6 +91,51 @@ static sem_t* TestSemOpen(const char* name, int oflag, ...) #undef sem_open #undef TPM2_ForceZero +#include + +void TestSetDefaultHAL(FWTPM_CTX* ctx); +UINT32 TestByteArrayToU32(const BYTE* data); + +#define TPM2_ForceZero TestForceZero +#define FWTPM_ProcessCommand TestProcessCommand +#define FWTPM_IO_IsStopRequested TestStopRequested +#define FWTPM_TIS_SetDefaultHAL TestSetDefaultHAL +#define TPM2_Packet_ByteArrayToU32 TestByteArrayToU32 +#include "../src/fwtpm/fwtpm_tis.c" +#undef TPM2_Packet_ByteArrayToU32 +#undef FWTPM_TIS_SetDefaultHAL +#undef FWTPM_IO_IsStopRequested +#undef FWTPM_ProcessCommand +#undef TPM2_ForceZero + +int TestProcessCommand(FWTPM_CTX* ctx, const byte* cmdBuf, int cmdSize, + byte* rspBuf, int* rspSize, int locality) +{ + (void)ctx; + (void)cmdBuf; + (void)cmdSize; + (void)rspBuf; + (void)rspSize; + (void)locality; + return TPM_RC_FAILURE; +} + +int TestStopRequested(void) +{ + return 0; +} + +void TestSetDefaultHAL(FWTPM_CTX* ctx) +{ + (void)ctx; +} + +UINT32 TestByteArrayToU32(const BYTE* data) +{ + return ((UINT32)data[0] << 24) | ((UINT32)data[1] << 16) | + ((UINT32)data[2] << 8) | (UINT32)data[3]; +} + static void CleanupEndpoint(void) { (void)sem_unlink(gTestSemRsp); @@ -421,6 +469,45 @@ static int TestMetadataValidation(void) return 0; } +static int TestBurstCountWriteIgnored(void) +{ + FWTPM_CTX ctx; + FWTPM_TIS_REGS regs; + UINT32 initialSts = 0x12345678U; + int failed = 0; + + XMEMSET(&ctx, 0, sizeof(ctx)); + XMEMSET(®s, 0, sizeof(regs)); + ctx.tisLocality = 0; + regs.sts = initialSts; + regs.cmd_len = 7; + regs.fifo_write_pos = 6; + regs.fifo_read_pos = 5; + regs.rsp_len = 4; + regs.reg_addr = FWTPM_TIS_BURST_COUNT_REG; + regs.reg_len = 1; + regs.reg_is_write = 1; + regs.reg_data[0] = FWTPM_STS_COMMAND_READY; + + TisHandleRegAccess(&ctx, ®s); + if (regs.sts != initialSts || regs.cmd_len != 7 || + regs.fifo_write_pos != 6 || regs.fifo_read_pos != 5 || + regs.rsp_len != 4) { + failed = 1; + } + + regs.reg_addr = FWTPM_TIS_STS; + TisHandleRegAccess(&ctx, ®s); + if (regs.cmd_len != 0 || regs.fifo_write_pos != 0 || + regs.fifo_read_pos != 0 || regs.rsp_len != 0) { + failed = 1; + } + + printf("%s: ignored writes to read-only burst count\n", + failed ? "FAIL" : "PASS"); + return failed; +} + int main(void) { int failures = 0; @@ -455,6 +542,7 @@ int main(void) failures += ExpectAccepted(); failures += ExpectDisconnectZeroized(); failures += TestMetadataValidation(); + failures += TestBurstCountWriteIgnored(); CleanupEndpoint(); printf("fwTPM HAL endpoint tests: %s\n", diff --git a/tests/fwtpm_io_test_adapter.c b/tests/fwtpm_io_test_adapter.c new file mode 100644 index 000000000..d9a3c838d --- /dev/null +++ b/tests/fwtpm_io_test_adapter.c @@ -0,0 +1,16 @@ +/* Compile the socket transport for focused unit testing without pulling the + * optional SPDM responder into the core fwTPM unit-test target. */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef HAVE_CONFIG_H + #undef HAVE_CONFIG_H +#endif +#ifdef WOLFTPM_SPDM_RESPONDER + #undef WOLFTPM_SPDM_RESPONDER +#endif + +#include "../src/fwtpm/fwtpm_io.c" diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index 4b3bc1324..47c81fab9 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -39,15 +39,30 @@ #include #include #include +#include #include #include #include +#include #include #include #include +#ifndef _WIN32 + #include + #include + #include + #include + #include +#endif + +#ifdef WOLFTPM_FWTPM_UNIT_TEST +int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, + UINT32 tssCmd); +#endif + /* Test Fail Helpers (same as unit_tests.c) */ #ifndef NO_ABORT #ifndef XABORT @@ -17273,6 +17288,257 @@ static void test_fwtpm_nv_append_rebuild_probe(void) #endif } +#if defined(WOLFTPM_FWTPM_UNIT_TEST) && !defined(_WIN32) +static void test_fwtpm_mssim_hash_data_framing(void) +{ + static const byte payload[] = { 0x11, 0x22, 0x33, 0x44 }; + FWTPM_CTX ctx; + UINT32 netLen = htonl((UINT32)sizeof(payload)); + UINT32 ack = 1; + byte extra; + int sockets[2]; + int flags; + int rc; + int got; + + XMEMSET(&ctx, 0, sizeof(ctx)); + AssertIntEQ(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), 0); + AssertIntEQ((int)write(sockets[1], &netLen, sizeof(netLen)), + (int)sizeof(netLen)); + AssertIntEQ((int)write(sockets[1], payload, sizeof(payload)), + (int)sizeof(payload)); + + rc = FWTPM_IO_TestHandleMssimSignal(&ctx, sockets[0], + FWTPM_TCP_SIGNAL_HASH_DATA); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ((int)read(sockets[1], &ack, sizeof(ack)), + (int)sizeof(ack)); + AssertIntEQ((int)ntohl(ack), 1); + + flags = fcntl(sockets[0], F_GETFL, 0); + AssertIntNE(flags, -1); + AssertIntEQ(fcntl(sockets[0], F_SETFL, flags | O_NONBLOCK), 0); + errno = 0; + got = (int)read(sockets[0], &extra, sizeof(extra)); + AssertIntEQ(got, -1); + AssertTrue(errno == EAGAIN || errno == EWOULDBLOCK); + + (void)close(sockets[1]); + (void)close(sockets[0]); + fwtpm_pass("MSSIM unsupported hash-data framing:", 0); +} +#endif + +#ifdef WOLFTPM_FWTPM_UNIT_TEST +typedef struct FWTPM_IO_HAL_TEST_CTX { + FWTPM_CTX* fwtpm; + byte input[sizeof(UINT32)]; + int inputPos; + byte output[sizeof(UINT32)]; + int outputPos; + int waitCount; + int waitTimeoutMs; + int recvType; + int sendType; + int closeCount[2]; + int acceptCount[2]; + int failAcceptNumber; + int failRecv; + int stopWaitNumber; + int stopServerWaitNumber; + int waitEvent; +} FWTPM_IO_HAL_TEST_CTX; + +static int test_io_hal_send(void* userCtx, int type, const void* buf, int sz) +{ + FWTPM_IO_HAL_TEST_CTX* test = (FWTPM_IO_HAL_TEST_CTX*)userCtx; + + if (sz < 0 || test->outputPos + sz > (int)sizeof(test->output)) + return TPM_RC_FAILURE; + XMEMCPY(test->output + test->outputPos, buf, (size_t)sz); + test->outputPos += sz; + test->sendType = type; + return TPM_RC_SUCCESS; +} + +static int test_io_hal_recv(void* userCtx, int type, void* buf, int sz) +{ + FWTPM_IO_HAL_TEST_CTX* test = (FWTPM_IO_HAL_TEST_CTX*)userCtx; + + if (test->failRecv) + return TPM_RC_FAILURE; + if (sz < 0 || test->inputPos + sz > (int)sizeof(test->input)) + return TPM_RC_FAILURE; + XMEMCPY(buf, test->input + test->inputPos, (size_t)sz); + test->inputPos += sz; + test->recvType = type; + return TPM_RC_SUCCESS; +} + +static int test_io_hal_wait(void* userCtx, int timeoutMs) +{ + FWTPM_IO_HAL_TEST_CTX* test = (FWTPM_IO_HAL_TEST_CTX*)userCtx; + + test->waitCount++; + test->waitTimeoutMs = timeoutMs; + if (test->waitCount == test->stopWaitNumber) { + FWTPM_IO_RequestStop(); + return TPM_RC_FAILURE; + } + if (test->waitCount == test->stopServerWaitNumber) { + test->fwtpm->running = 0; + return 0; + } + if (test->waitEvent != 0) + return test->waitEvent; + return FWTPM_IO_READY_PLATFORM; +} + +static void test_io_hal_close(void* userCtx, int type) +{ + FWTPM_IO_HAL_TEST_CTX* test = (FWTPM_IO_HAL_TEST_CTX*)userCtx; + + test->closeCount[type]++; +} + +static int test_io_hal_accept(void* userCtx, int type) +{ + FWTPM_IO_HAL_TEST_CTX* test = (FWTPM_IO_HAL_TEST_CTX*)userCtx; + + test->acceptCount[type]++; + if (test->acceptCount[type] == test->failAcceptNumber) + return TPM_RC_FAILURE; + return TPM_RC_SUCCESS; +} + +static void test_fwtpm_custom_io_hal(void) +{ + FWTPM_CTX ctx; + FWTPM_IO_HAL hal; + FWTPM_IO_HAL_TEST_CTX test; + int rc; + int type; + + XMEMSET(&ctx, 0, sizeof(ctx)); + XMEMSET(&hal, 0, sizeof(hal)); + XMEMSET(&test, 0, sizeof(test)); + PutU32BE(test.input, FWTPM_TCP_STOP); + hal.send = test_io_hal_send; + hal.recv = test_io_hal_recv; + hal.wait = test_io_hal_wait; + hal.close_conn = test_io_hal_close; + hal.ctx = &test; + + hal.accept = test_io_hal_accept; + hal.close_conn = NULL; + rc = FWTPM_IO_SetHAL(&ctx, &hal); + AssertIntEQ(rc, BAD_FUNC_ARG); + hal.accept = NULL; + hal.close_conn = test_io_hal_close; + + rc = FWTPM_IO_SetHAL(&ctx, &hal); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertTrue(ctx.ioHal.wait == test_io_hal_wait); + rc = FWTPM_IO_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_IO_ServerLoop(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(test.waitCount, 1); + AssertIntEQ(test.waitTimeoutMs, FWTPM_IO_WAIT_TIMEOUT_MS); + AssertIntEQ(test.recvType, FWTPM_IO_TYPE_PLATFORM); + AssertIntEQ(test.sendType, FWTPM_IO_TYPE_PLATFORM); + AssertIntEQ(test.inputPos, (int)sizeof(UINT32)); + AssertIntEQ(test.outputPos, (int)sizeof(UINT32)); + AssertIntEQ((int)test.output[0] | (int)test.output[1] | + (int)test.output[2] | (int)test.output[3], 0); + FWTPM_IO_Cleanup(&ctx); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_COMMAND], 1); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_PLATFORM], 1); + rc = FWTPM_Cleanup(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + XMEMSET(&ctx, 0, sizeof(ctx)); + XMEMSET(&test, 0, sizeof(test)); + test.fwtpm = &ctx; + test.failAcceptNumber = 2; + test.stopServerWaitNumber = 3; + test.waitEvent = FWTPM_IO_ACCEPT_PLATFORM; + hal.accept = test_io_hal_accept; + hal.ctx = &test; + rc = FWTPM_IO_SetHAL(&ctx, &hal); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_IO_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_IO_ServerLoop(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(test.acceptCount[FWTPM_IO_TYPE_PLATFORM], 2); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_PLATFORM], 0); + FWTPM_IO_Cleanup(&ctx); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_COMMAND], 0); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_PLATFORM], 1); + rc = FWTPM_Cleanup(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + hal.accept = NULL; + + for (type = FWTPM_IO_TYPE_COMMAND; + type <= FWTPM_IO_TYPE_PLATFORM; type++) { + XMEMSET(&ctx, 0, sizeof(ctx)); + XMEMSET(&test, 0, sizeof(test)); + PutU32BE(test.input, FWTPM_TCP_SESSION_END); + test.fwtpm = &ctx; + test.stopServerWaitNumber = 2; + test.waitEvent = type == FWTPM_IO_TYPE_COMMAND ? + FWTPM_IO_READY_COMMAND : FWTPM_IO_READY_PLATFORM; + hal.ctx = &test; + rc = FWTPM_IO_SetHAL(&ctx, &hal); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_IO_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_IO_ServerLoop(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(test.waitCount, 2); + FWTPM_IO_Cleanup(&ctx); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_COMMAND], 1); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_PLATFORM], 1); + rc = FWTPM_Cleanup(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + } + + XMEMSET(&ctx, 0, sizeof(ctx)); + XMEMSET(&test, 0, sizeof(test)); + test.failRecv = 1; + test.stopWaitNumber = 2; + hal.ctx = &test; + rc = FWTPM_IO_SetHAL(&ctx, &hal); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_IO_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = FWTPM_IO_ServerLoop(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(test.waitCount, 2); + AssertIntEQ(test.waitTimeoutMs, FWTPM_IO_WAIT_TIMEOUT_MS); + FWTPM_IO_Cleanup(&ctx); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_COMMAND], 1); + AssertIntEQ(test.closeCount[FWTPM_IO_TYPE_PLATFORM], 1); + rc = FWTPM_IO_Init(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertTrue(FWTPM_IO_IsStopRequested()); + FWTPM_IO_Cleanup(&ctx); + rc = FWTPM_Cleanup(&ctx); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + fwtpm_pass("Custom IO HAL transport:", 0); +} +#endif + /* ================================================================== */ /* main */ /* ================================================================== */ @@ -17288,6 +17554,20 @@ int fwtpm_unit_tests(int argc, char *argv[]) /* Remove stale NV state to ensure clean test runs */ (void)remove(FWTPM_NV_FILE); +#ifdef WOLFTPM_FWTPM_UNIT_TEST + if (argc == 2 && XSTRCMP(argv[1], "custom-io-hal") == 0) { + test_fwtpm_custom_io_hal(); + return 0; + } +#endif + +#if defined(WOLFTPM_FWTPM_UNIT_TEST) && !defined(_WIN32) + if (argc == 2 && XSTRCMP(argv[1], "mssim-hash-data") == 0) { + test_fwtpm_mssim_hash_data_framing(); + return 0; + } +#endif + if (argc == 2 && XSTRCMP(argv[1], "clear-seed") == 0) { test_fwtpm_clear(); return 0; @@ -17752,6 +18032,13 @@ int fwtpm_unit_tests(int argc, char *argv[]) #endif /* !FWTPM_NO_NV */ test_fwtpm_clear(); +#if defined(WOLFTPM_FWTPM_UNIT_TEST) && !defined(_WIN32) + test_fwtpm_mssim_hash_data_framing(); +#endif +#ifdef WOLFTPM_FWTPM_UNIT_TEST + test_fwtpm_custom_io_hal(); +#endif + printf("\nAll fwTPM unit tests passed!\n"); return 0; } diff --git a/tests/include.am b/tests/include.am index 50597edcf..b704f0b6c 100644 --- a/tests/include.am +++ b/tests/include.am @@ -26,6 +26,7 @@ tests_fwtpm_unit_test_SOURCES = \ src/fwtpm/fwtpm.c \ src/fwtpm/fwtpm_command.c \ src/fwtpm/fwtpm_crypto.c \ + tests/fwtpm_io_test_adapter.c \ src/fwtpm/fwtpm_nv.c \ src/tpm2_util.c \ src/tpm2_packet.c \ diff --git a/wolftpm/fwtpm/fwtpm.h b/wolftpm/fwtpm/fwtpm.h index 90ed66233..b983b2efa 100644 --- a/wolftpm/fwtpm/fwtpm.h +++ b/wolftpm/fwtpm/fwtpm.h @@ -647,20 +647,31 @@ typedef struct FWTPM_PrimaryCache { int privKeySize; } FWTPM_PrimaryCache; -/* IO transport HAL callbacks — socket transport only (not used in TIS mode) */ +/* IO transport HAL callbacks - socket transport only (not used in TIS mode) */ #ifndef WOLFTPM_FWTPM_TIS +#define FWTPM_IO_TYPE_COMMAND 0 +#define FWTPM_IO_TYPE_PLATFORM 1 +#define FWTPM_IO_READY_COMMAND 0x01 +#define FWTPM_IO_READY_PLATFORM 0x02 +#define FWTPM_IO_ACCEPT_COMMAND 0x04 +#define FWTPM_IO_ACCEPT_PLATFORM 0x08 +#ifndef FWTPM_IO_WAIT_TIMEOUT_MS +#define FWTPM_IO_WAIT_TIMEOUT_MS 1000 +#endif + typedef struct FWTPM_IO_HAL_S { /* Send data to client. Returns 0 on success. */ - int (*send)(void* ctx, const void* buf, int sz); + int (*send)(void* ctx, int type, const void* buf, int sz); /* Receive data from client. Returns 0 on success. */ - int (*recv)(void* ctx, void* buf, int sz); - /* Wait for connections/data. Returns bitmask: + int (*recv)(void* ctx, int type, void* buf, int sz); + /* Wait up to timeoutMs for connections/data. Returns bitmask: * 0x01 = command data ready, 0x02 = platform data ready, * 0x04 = new command connection, 0x08 = new platform connection. - * Negative on error. */ - int (*wait)(void* ctx); - /* Accept a new connection (for connection-oriented transports). - * type: 0=command, 1=platform. Returns 0 on success. */ + * Zero on timeout, negative on error. */ + int (*wait)(void* ctx, int timeoutMs); + /* Accept a new connection (requires close_conn). + * type: 0=command, 1=platform. Returns 0 on success and owns cleanup + * internally when returning an error. */ int (*accept)(void* ctx, int type); /* Close a connection. type: 0=command, 1=platform. */ void (*close_conn)(void* ctx, int type); @@ -679,6 +690,7 @@ typedef struct FWTPM_IO_CTX { SOCKET_T platListenFd; /* Listening socket for platform port */ SOCKET_T clientFd; /* Accepted client connection */ SOCKET_T platClientFd; /* Accepted platform client connection */ + unsigned int customConnMask; /* Active custom HAL connections */ } FWTPM_IO_CTX; #endif /* !WOLFTPM_FWTPM_TIS */ @@ -738,7 +750,7 @@ typedef struct FWTPM_CTX { /* Socket transport configuration (not used in TIS mode) */ int cmdPort; /* Command port (default 2321) */ int platPort; /* Platform port (default 2322) */ - FWTPM_IO_HAL ioHal; /* IO transport HAL callbacks */ + FWTPM_IO_HAL ioHal; /* IO transport HAL callbacks */ FWTPM_IO_CTX io; /* Socket IO state */ #endif diff --git a/wolftpm/fwtpm/fwtpm_io.h b/wolftpm/fwtpm/fwtpm_io.h index 2bb39db40..f2aba0c1a 100644 --- a/wolftpm/fwtpm/fwtpm_io.h +++ b/wolftpm/fwtpm/fwtpm_io.h @@ -51,9 +51,8 @@ #ifndef WOLFTPM_FWTPM_TIS /*! \ingroup wolfTPM_fwTPM - \brief Register a custom socket-transport HAL. Call before - FWTPM_IO_Init to replace the default POSIX socket implementation - (e.g. for a mocked or in-process transport). + \brief Register a custom transport HAL. Call before FWTPM_Init or after + FWTPM_Init but before FWTPM_IO_Init to replace the default socket transport. \return 0 on success \return BAD_FUNC_ARG if ctx or hal is NULL @@ -68,12 +67,12 @@ WOLFTPM_API int FWTPM_IO_SetHAL(FWTPM_CTX* ctx, FWTPM_IO_HAL* hal); /*! \ingroup wolfTPM_fwTPM - \brief Initialize the fwTPM transport. When no HAL has been - registered, binds default TCP sockets on ctx->cmdPort and - ctx->platPort. + \brief Initialize the fwTPM transport. Uses a registered IO HAL when + present; otherwise initializes the built-in TCP socket or TIS transport. \return 0 on success - \return negative on socket/bind error + \return BAD_FUNC_ARG if ctx is NULL + \return TPM_RC_FAILURE on transport initialization error \param ctx pointer to an initialized FWTPM_CTX @@ -84,7 +83,7 @@ WOLFTPM_API int FWTPM_IO_Init(FWTPM_CTX* ctx); /*! \ingroup wolfTPM_fwTPM - \brief Release transport resources (close sockets / release HAL). + \brief Release custom HAL, socket, or TIS transport resources. \param ctx pointer to an initialized FWTPM_CTX @@ -94,11 +93,12 @@ WOLFTPM_API void FWTPM_IO_Cleanup(FWTPM_CTX* ctx); /*! \ingroup wolfTPM_fwTPM - \brief Run the main fwTPM server loop. Blocks until ctx->running - is cleared (by FWTPM_IO_RequestStop or a signal handler). + \brief Run the selected fwTPM transport loop. Blocks until ctx->running is + cleared or FWTPM_IO_RequestStop is called. \return 0 on clean shutdown - \return negative on fatal transport error + \return BAD_FUNC_ARG if ctx is NULL + \return TPM_RC_FAILURE on fatal transport error \param ctx pointer to an initialized FWTPM_CTX diff --git a/wolftpm/fwtpm/fwtpm_tis.h b/wolftpm/fwtpm/fwtpm_tis.h index c7cf4b52d..52f70f129 100644 --- a/wolftpm/fwtpm/fwtpm_tis.h +++ b/wolftpm/fwtpm/fwtpm_tis.h @@ -179,9 +179,10 @@ typedef struct FWTPM_TIS_HAL { * Returns 0 on success. */ int (*init)(void* ctx, FWTPM_TIS_REGS** regs); - /* Block until a register access request is available. - * Returns 0 on success, negative on error/shutdown. - * Return -1 with errno==EINTR to continue the loop. */ + /* Block until a register access request is available. Returns 0 when a + * request is ready; -1 is reserved for a transient/interrupted wait and + * makes the server retry without examining errno. Any other negative + * value is fatal and ends the server loop. */ int (*wait_request)(void* ctx); /* Signal that a register access response is ready. */ @@ -220,7 +221,8 @@ WOLFTPM_API int FWTPM_TIS_SetHAL(struct FWTPM_CTX* ctx, HAL has been set, installs the default POSIX shared-memory HAL. \return 0 on success - \return negative on transport initialization error + \return BAD_FUNC_ARG if ctx is NULL + \return TPM_RC_FAILURE on transport initialization error \param ctx pointer to an initialized FWTPM_CTX @@ -246,7 +248,8 @@ WOLFTPM_API void FWTPM_TIS_Cleanup(struct FWTPM_CTX* ctx); dispatches completed commands to FWTPM_ProcessCommand. \return 0 on clean shutdown - \return negative on fatal transport error + \return BAD_FUNC_ARG if ctx is NULL or the HAL is incomplete + \return TPM_RC_FAILURE on fatal transport error \param ctx pointer to an initialized FWTPM_CTX diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index 449cb2340..b37d80d82 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -686,7 +686,8 @@ typedef int64_t INT64; #include #define XSLEEP_MS(ms) k_msleep(ms) #elif defined(WOLFSSL_ESPIDF) - #define XSLEEP_MS(ms) vTaskDelay(pdMS_TO_TICKS(ms)) + #include + #define XSLEEP_MS(ms) esp_rom_delay_us((word32)(ms) * 1000U) #elif defined(__linux__) || defined(__ARM_EABI__) #include static inline void XSLEEP_MS(unsigned int ms) {