Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/wh_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ int wh_Client_Init(whClientContext* c, const whClientConfig* config)
c->cancelCb = config->cancelCb;
#endif

#ifdef WOLFHSM_CFG_ENABLE_TIMEOUT
if (config->respTimeoutConfig != NULL) {
rc = wh_Timeout_Init(c->respTimeout, config->respTimeoutConfig);
if (rc != 0) {
return rc;
}
}
#endif

rc = wh_CommClient_Init(c->comm, config->comm);

#ifndef WOLFHSM_CFG_NO_CRYPTO
Expand Down Expand Up @@ -199,6 +208,31 @@ int wh_Client_RecvResponse(whClientContext *c,
return rc;
}

int wh_Client_RecvResponseTimeout(whClientContext *c,
uint16_t *out_group, uint16_t *out_action,
uint16_t *out_size, void* data, whTimeoutCtx *timeout)
{
int ret;

if ((c == NULL) || (timeout == NULL)) {
return WH_ERROR_BADARGS;
}

ret = wh_Timeout_Start(timeout);
if (ret != WH_ERROR_OK) {
return ret;
}
Comment on lines +217 to +224
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wh_Client_RecvResponseTimeout assumes the timeout context is already configured with a non-zero timeoutUs, but when WOLFHSM_CFG_ENABLE_TIMEOUT is defined it is legal for whClientConfig.respTimeoutConfig to be NULL, leaving timeoutUs at 0. In that case this function will never time out and will behave like the original infinite wait loop, which can be surprising to integrators who enable timeout support but forget to provide a configuration; consider either validating that timeout->timeoutUs is non-zero here (and failing fast) or documenting/enforcing that a valid respTimeoutConfig must be supplied when timeout support is enabled.

Copilot uses AI. Check for mistakes.

do {
ret = wh_Client_RecvResponse(c, out_group, out_action, out_size, data);
if ((ret == WH_ERROR_NOTREADY) && wh_Timeout_Expired(timeout)) {
return WH_ERROR_TIMEOUT;
}
} while (ret == WH_ERROR_NOTREADY);

return ret;
}

int wh_Client_CommInitRequest(whClientContext* c)
{
whMessageCommInitRequest msg = {0};
Expand Down
279 changes: 100 additions & 179 deletions src/wh_client_crypto.c

Large diffs are not rendered by default.

96 changes: 96 additions & 0 deletions src/wh_timeout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfHSM.
*
* wolfHSM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfHSM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wolfHSM. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* src/wh_timeout.c
*/

/* Pick up compile-time configuration */
#include "wolfhsm/wh_settings.h"

#include "wolfhsm/wh_timeout.h"
#include "wolfhsm/wh_error.h"

int wh_Timeout_Init(whTimeoutCtx* timeout, const whTimeoutConfig* config)
{
if ((timeout == NULL) || (config == NULL)) {
return WH_ERROR_BADARGS;
}

timeout->startUs = 0;
timeout->timeoutUs = config->timeoutUs;
timeout->expiredCb = config->expiredCb;
timeout->cbCtx = config->cbCtx;

return WH_ERROR_OK;
}

int wh_Timeout_Set(whTimeoutCtx* timeout, uint64_t timeoutUs)
{
if (timeout == NULL) {
return WH_ERROR_BADARGS;
}

timeout->timeoutUs = timeoutUs;

return WH_ERROR_OK;
}

int wh_Timeout_Start(whTimeoutCtx* timeout)
{
if (timeout == NULL) {
return WH_ERROR_BADARGS;
}

timeout->startUs = WH_GETTIME_US();

return WH_ERROR_OK;
}

int wh_Timeout_Stop(whTimeoutCtx* timeout)
{
if (timeout == NULL) {
return WH_ERROR_BADARGS;
}

timeout->startUs = 0;
timeout->timeoutUs = 0;

return WH_ERROR_OK;
}

int wh_Timeout_Expired(const whTimeoutCtx* timeout)
{
uint64_t nowUs = 0;
int expired = 0;

if (timeout == NULL) {
return 0;
}

if (timeout->timeoutUs == 0) {
return 0;
}

nowUs = WH_GETTIME_US();
expired = (nowUs - timeout->startUs) >= timeout->timeoutUs;
if (expired && (timeout->expiredCb != NULL)) {
timeout->expiredCb(timeout->cbCtx);
}
return expired;
Comment on lines +77 to +95
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As implemented, calling wh_Timeout_Expired on a context that has been initialized but never started (i.e., startUs is still 0 while timeoutUs > 0) will typically report the timeout as expired immediately, since the check uses nowUs - timeout->startUs with a zero start value. That behavior can be surprising for users who expect an unstarted or just-initialized timeout to be treated as "not expired"; consider either treating startUs == 0 as a disabled/not-started state in this function (return 0) or clearly documenting that wh_Timeout_Start must be called before wh_Timeout_Expired and that calling it beforehand is unsupported.

Copilot uses AI. Check for mistakes.
}
2 changes: 2 additions & 0 deletions test/config/wolfhsm_cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,6 @@
/* Allow persistent NVM artifacts in tests */
#define WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS

#define WOLFHSM_CFG_ENABLE_TIMEOUT

#endif /* WOLFHSM_CFG_H_ */
4 changes: 4 additions & 0 deletions test/wh_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "wh_test_keywrap.h"
#include "wh_test_multiclient.h"
#include "wh_test_log.h"
#include "wh_test_timeout.h"

#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER)
#include "wh_test_cert.h"
Expand Down Expand Up @@ -71,6 +72,9 @@ int whTest_Unit(void)
/* Component Tests */
WH_TEST_ASSERT(0 == whTest_Flash_RamSim());
WH_TEST_ASSERT(0 == whTest_NvmFlash());
#ifdef WOLFHSM_CFG_ENABLE_TIMEOUT
WH_TEST_ASSERT(0 == whTest_Timeout());
#endif
#ifdef WOLFHSM_CFG_LOGGING
WH_TEST_ASSERT(0 == whTest_Log());
#endif
Expand Down
74 changes: 74 additions & 0 deletions test/wh_test_timeout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2024 wolfSSL Inc.
*
* This file is part of wolfHSM.
*
* wolfHSM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfHSM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wolfHSM. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* test/wh_test_timeout.c
*
*/

#include <stdint.h>

#include "wolfhsm/wh_settings.h"
#include "wolfhsm/wh_timeout.h"
#include "wolfhsm/wh_error.h"

#include "wh_test_common.h"
#include "wh_test_timeout.h"

static void whTest_TimeoutCb(void* ctx)
{
int* counter = (int*)ctx;
if (counter != NULL) {
(*counter)++;
}
}

int whTest_Timeout(void)
{
int cb_count = 0;
whTimeoutConfig cfg;
whTimeoutCtx timeout[1];

cfg.timeoutUs = 1;
cfg.expiredCb = whTest_TimeoutCb;
cfg.cbCtx = &cb_count;

wh_Timeout_Init(timeout, &cfg);
WH_TEST_ASSERT_RETURN(timeout->startUs == 0);
WH_TEST_ASSERT_RETURN(timeout->timeoutUs == cfg.timeoutUs);
WH_TEST_ASSERT_RETURN(timeout->expiredCb == cfg.expiredCb);
WH_TEST_ASSERT_RETURN(timeout->cbCtx == cfg.cbCtx);

wh_Timeout_Start(timeout);
WH_TEST_ASSERT_RETURN(timeout->timeoutUs > 0);

wh_Timeout_Stop(timeout);
WH_TEST_ASSERT_RETURN(timeout->startUs == 0);
WH_TEST_ASSERT_RETURN(timeout->timeoutUs == 0);

/* No expiration when disabled */
WH_TEST_ASSERT_RETURN(wh_Timeout_Expired(timeout) == 0);

WH_TEST_ASSERT_RETURN(wh_Timeout_Init(0, 0) == WH_ERROR_BADARGS);
WH_TEST_ASSERT_RETURN(wh_Timeout_Set(0, 0) == WH_ERROR_BADARGS);
WH_TEST_ASSERT_RETURN(wh_Timeout_Start(0) == WH_ERROR_BADARGS);
WH_TEST_ASSERT_RETURN(wh_Timeout_Stop(0) == WH_ERROR_BADARGS);
WH_TEST_ASSERT_RETURN(wh_Timeout_Expired(0) == 0);
Comment on lines +64 to +71
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test only exercises the disabled/non-expiring path of wh_Timeout_Expired (after calling wh_Timeout_Stop) and the NULL-argument behavior, but never checks the normal expiration path or that expiredCb is actually invoked when the timeout elapses. To fully validate the timeout helper, consider adding a case that starts a timeout with a small timeoutUs, waits until it should have expired, and asserts both that wh_Timeout_Expired returns 1 and that cb_count has been incremented.

Copilot uses AI. Check for mistakes.

return 0;
}
34 changes: 34 additions & 0 deletions test/wh_test_timeout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2024 wolfSSL Inc.
*
* This file is part of wolfHSM.
*
* wolfHSM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfHSM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wolfHSM. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* test/wh_test_timeout.h
*
*/

#ifndef TEST_WH_TEST_TIMEOUT_H_
#define TEST_WH_TEST_TIMEOUT_H_

/**
* Runs timeout module tests.
*
* @return 0 on success and a non-zero error code on failure.
*/
int whTest_Timeout(void);

#endif /* TEST_WH_TEST_TIMEOUT_H_ */
24 changes: 24 additions & 0 deletions wolfhsm/wh_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

/* Component includes */
#include "wolfhsm/wh_comm.h"
#include "wolfhsm/wh_timeout.h"
#include "wolfhsm/wh_message_customcb.h"
#ifdef WOLFHSM_CFG_DMA
#include "wolfhsm/wh_dma.h"
Expand Down Expand Up @@ -121,6 +122,9 @@ struct whClientContext_t {
uint8_t cancelable;
whClientCancelCb cancelCb;
#endif
#ifdef WOLFHSM_CFG_ENABLE_TIMEOUT
whTimeoutCtx respTimeout[1];
#endif
#ifdef WOLFHSM_CFG_DMA
whClientDmaContext dma;
#endif /* WOLFHSM_CFG_DMA */
Expand All @@ -135,6 +139,9 @@ struct whClientConfig_t {
#ifdef WOLFHSM_CFG_DMA
whClientDmaConfig* dmaConfig;
#endif /* WOLFHSM_CFG_DMA */
#ifdef WOLFHSM_CFG_ENABLE_TIMEOUT
whTimeoutConfig* respTimeoutConfig;
#endif /* WOLFHSM_CFG_ENABLE_TIMEOUT*/
};
typedef struct whClientConfig_t whClientConfig;

Expand Down Expand Up @@ -193,6 +200,23 @@ int wh_Client_SendRequest(whClientContext* c, uint16_t group, uint16_t action,
int wh_Client_RecvResponse(whClientContext* c, uint16_t* out_group,
uint16_t* out_action, uint16_t* out_size,
void* data);
#ifdef WOLFHSM_CFG_ENABLE_TIMEOUT
/**
* Receives a response from the server with a timeout window.
*
* @param c The client context.
* @param out_group Pointer to store the received group value.
* @param out_action Pointer to store the received action value.
* @param out_size Pointer to store the received size value.
* @param data Pointer to store the received data.
* @param timeout The timeout context to use.
* @return 0 if successful, WH_ERROR_TIMEOUT on expiration, or a negative value
* if an error occurred.
*/
int wh_Client_RecvResponseTimeout(whClientContext* c, uint16_t* out_group,
uint16_t* out_action, uint16_t* out_size,
void* data, whTimeoutCtx* timeout);
#endif /* WOLFHSM_CFG_ENABLE_TIMEOUT */


/** Comm component functions */
Expand Down
1 change: 1 addition & 0 deletions wolfhsm/wh_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum WH_ERROR_ENUM {
compile-time configuration */
WH_ERROR_USAGE =
-2009, /* Operation not permitted based on object/key usage flags */
WH_ERROR_TIMEOUT = -2010, /* Timeout occurred. */

/* NVM and keystore specific status returns */
WH_ERROR_LOCKED = -2100, /* Unlock and retry if necessary */
Expand Down
3 changes: 3 additions & 0 deletions wolfhsm/wh_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
* WOLFHSM_CFG_ENABLE_SERVER - If defined, include server-specific
* functionality
*
* WOLFHSM_CFG_ENABLE_TIMEOUT - If defined, include timeout helpers and
* client response timeout support.
*
* WOLFHSM_CFG_NVM_OBJECT_COUNT - Number of objects in ram and disk directories
* Default: 32
*
Expand Down
Loading