diff --git a/benchmark/bench_modules/wh_bench_mod_all.h b/benchmark/bench_modules/wh_bench_mod_all.h index c31f6b769..0dff6c419 100644 --- a/benchmark/bench_modules/wh_bench_mod_all.h +++ b/benchmark/bench_modules/wh_bench_mod_all.h @@ -165,6 +165,12 @@ int wh_Bench_Mod_HmacSha3256Dma(whClientContext* client, whBenchOpContext* ctx, int wh_Bench_Mod_HkdfSha256(whClientContext* client, whBenchOpContext* ctx, int id, void* params); +/* + * CMAC KDF benchmark module prototypes (wh_bench_mod_cmac_kdf.c) + */ +int wh_Bench_Mod_CmacKdf(whClientContext* client, whBenchOpContext* ctx, int id, + void* params); + /* * ECC benchmark module prototypes (wh_bench_mod_ecc.c) */ diff --git a/benchmark/bench_modules/wh_bench_mod_cmac_kdf.c b/benchmark/bench_modules/wh_bench_mod_cmac_kdf.c new file mode 100644 index 000000000..6b30eb880 --- /dev/null +++ b/benchmark/bench_modules/wh_bench_mod_cmac_kdf.c @@ -0,0 +1,111 @@ +/* + * 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 . + */ + +#include "wh_bench_mod.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_client_crypto.h" + +#include "wolfssl/wolfcrypt/cmac.h" +#include "wolfssl/wolfcrypt/kdf.h" + +#if defined(WOLFHSM_CFG_BENCH_ENABLE) + +#if defined(HAVE_CMAC_KDF) && defined(WOLFSSL_CMAC) + +#define WH_BENCH_CMAC_KDF_OUT_SIZE 40 + +static int _benchCmacKdf(whClientContext* client, whBenchOpContext* ctx, int id, + int devId) +{ + /* Derivation inputs mirror the unit test vectors to provide realistic + * message sizes while keeping the benchmark deterministic. */ + static const uint8_t cmacKdfSalt[] = { + 0x20, 0x51, 0xaf, 0x34, 0x76, 0x2e, 0xbe, 0x55, 0x6f, 0x72, 0xa5, 0xc6, + 0xed, 0xc7, 0x77, 0x1e, 0xb9, 0x24, 0x5f, 0xad, 0x76, 0xf0, 0x34, 0xbe}; + static const uint8_t cmacKdfZ[] = { + 0xae, 0x8e, 0x93, 0xc9, 0xc9, 0x91, 0xcf, 0x89, 0x6a, 0x49, 0x1a, + 0x89, 0x07, 0xdf, 0x4e, 0x4b, 0xe5, 0x18, 0x6a, 0xe4, 0x96, 0xcd, + 0x34, 0x0d, 0xc1, 0x9b, 0x23, 0x78, 0x21, 0xdb, 0x7b, 0x60}; + static const uint8_t cmacKdfFixedInfo[] = { + 0xa2, 0x59, 0xca, 0xe2, 0xc4, 0xa3, 0x6b, 0x89, 0x56, 0x3c, 0xb1, 0x48, + 0xc7, 0x82, 0x51, 0x34, 0x3b, 0xbf, 0xab, 0xdc, 0x13, 0xca, 0x7a, 0xc2, + 0x17, 0x1c, 0x2e, 0xb6, 0x02, 0x1f, 0x44, 0x77, 0xfe, 0xa3, 0x3b, 0x28, + 0x72, 0x4d, 0xa7, 0x21, 0xee, 0x08, 0x7b, 0xff, 0xd7, 0x94, 0xa1, 0x56, + 0x37, 0x54, 0xb4, 0x25, 0xa8, 0xd0, 0x9b, 0x3e, 0x0d, 0xa5, 0xff, 0xed}; + static const uint8_t label[] = "cmac-kdf-bench"; + + int ret = 0; + whKeyId keyId; + int i; + + (void)devId; + + for (i = 0; i < WOLFHSM_CFG_BENCH_KG_ITERS && ret == 0; i++) { + int benchStartRet; + int benchStopRet; + + keyId = WH_KEYID_ERASED; + + benchStartRet = wh_Bench_StartOp(ctx, id); + ret = wh_Client_CmacKdfMakeCacheKey( + client, WH_KEYID_ERASED, cmacKdfSalt, (uint32_t)sizeof(cmacKdfSalt), + WH_KEYID_ERASED, cmacKdfZ, (uint32_t)sizeof(cmacKdfZ), + cmacKdfFixedInfo, (uint32_t)sizeof(cmacKdfFixedInfo), &keyId, + WH_NVM_FLAGS_NONE, label, (uint32_t)sizeof(label), + WH_BENCH_CMAC_KDF_OUT_SIZE); + benchStopRet = wh_Bench_StopOp(ctx, id); + + if (benchStartRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet); + ret = benchStartRet; + break; + } + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_CmacKdfMakeCacheKey %d\n", + ret); + break; + } + if (benchStopRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet); + ret = benchStopRet; + break; + } + + /* Evict the cached key to free resources for next iteration */ + ret = wh_Client_KeyEvict(client, keyId); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_KeyEvict %d\n", ret); + break; + } + } + + return ret; +} + +int wh_Bench_Mod_CmacKdf(whClientContext* client, whBenchOpContext* ctx, int id, + void* params) +{ + (void)params; + return _benchCmacKdf(client, ctx, id, WH_DEV_ID); +} + +#endif /* HAVE_CMAC_KDF && WOLFSSL_CMAC */ + +#endif /* WOLFHSM_CFG_BENCH_ENABLE */ diff --git a/benchmark/bench_modules/wh_bench_mod_hkdf.c b/benchmark/bench_modules/wh_bench_mod_hkdf.c index 774d7c939..1bd5d0b19 100644 --- a/benchmark/bench_modules/wh_bench_mod_hkdf.c +++ b/benchmark/bench_modules/wh_bench_mod_hkdf.c @@ -19,6 +19,8 @@ #include "wh_bench_mod.h" #include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_client_crypto.h" #if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_BENCH_ENABLE) #include "wolfssl/wolfcrypt/hmac.h" @@ -44,24 +46,26 @@ static int _benchHkdf(whClientContext* client, whBenchOpContext* ctx, int id, 0x0a, 0x0b, 0x0c}; static const uint8_t hkdf_info[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}; - + static const uint8_t label[] = "hkdf-bench"; int ret = 0; - uint8_t okm[WH_BENCH_HKDF_OKM_SIZE]; + whKeyId keyId; int i; - (void)client; + (void)devId; for (i = 0; i < WOLFHSM_CFG_BENCH_KG_ITERS && ret == 0; i++) { int benchStartRet; int benchStopRet; + keyId = WH_KEYID_ERASED; + benchStartRet = wh_Bench_StartOp(ctx, id); - ret = wc_HKDF_ex(WC_SHA256, hkdf_ikm, (word32)sizeof(hkdf_ikm), - hkdf_salt, (word32)sizeof(hkdf_salt), hkdf_info, - (word32)sizeof(hkdf_info), okm, (word32)sizeof(okm), - NULL, /* heap */ - devId); + ret = wh_Client_HkdfMakeCacheKey( + client, WC_SHA256, WH_KEYID_ERASED, hkdf_ikm, + (uint32_t)sizeof(hkdf_ikm), hkdf_salt, (uint32_t)sizeof(hkdf_salt), + hkdf_info, (uint32_t)sizeof(hkdf_info), &keyId, WH_NVM_FLAGS_NONE, + label, (uint32_t)sizeof(label), WH_BENCH_HKDF_OKM_SIZE); benchStopRet = wh_Bench_StopOp(ctx, id); if (benchStartRet != 0) { @@ -70,7 +74,7 @@ static int _benchHkdf(whClientContext* client, whBenchOpContext* ctx, int id, break; } if (ret != 0) { - WH_BENCH_PRINTF("Failed to wc_HKDF_ex %d\n", ret); + WH_BENCH_PRINTF("Failed to wh_Client_HkdfMakeCacheKey %d\n", ret); break; } if (benchStopRet != 0) { @@ -78,6 +82,13 @@ static int _benchHkdf(whClientContext* client, whBenchOpContext* ctx, int id, ret = benchStopRet; break; } + + /* Evict the cached key to free resources for next iteration */ + ret = wh_Client_KeyEvict(client, keyId); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_KeyEvict %d\n", ret); + break; + } } return ret; diff --git a/benchmark/config/user_settings.h b/benchmark/config/user_settings.h index 3426ab01b..8a463fe7d 100644 --- a/benchmark/config/user_settings.h +++ b/benchmark/config/user_settings.h @@ -157,6 +157,7 @@ extern "C" { /** Composite features */ #define HAVE_HKDF +#define HAVE_CMAC_KDF /* Remove unneeded crypto */ #define NO_DSA diff --git a/benchmark/wh_bench.c b/benchmark/wh_bench.c index 555f75935..60e79e69d 100644 --- a/benchmark/wh_bench.c +++ b/benchmark/wh_bench.c @@ -167,6 +167,11 @@ typedef enum BenchModuleIdx { BENCH_MODULE_IDX_HKDF_SHA2_256, #endif /* HAVE_HKDF */ +/* CMAC KDF */ +#if defined(HAVE_CMAC_KDF) && defined(WOLFSSL_CMAC) + BENCH_MODULE_IDX_CMAC_KDF, +#endif /* HAVE_CMAC_KDF && WOLFSSL_CMAC */ + /* ECC */ #if defined(HAVE_ECC) BENCH_MODULE_IDX_ECC_P256_SIGN, @@ -335,6 +340,11 @@ static BenchModule g_benchModules[] = { [BENCH_MODULE_IDX_HKDF_SHA2_256] = {"HKDF-SHA2-256", wh_Bench_Mod_HkdfSha256, BENCH_THROUGHPUT_OPS, 0, NULL}, #endif /* HAVE_HKDF */ + /* CMAC KDF */ +#if defined(HAVE_CMAC_KDF) && defined(WOLFSSL_CMAC) + [BENCH_MODULE_IDX_CMAC_KDF] = {"CMAC-KDF-AES", wh_Bench_Mod_CmacKdf, BENCH_THROUGHPUT_OPS, 0, NULL}, +#endif /* HAVE_CMAC_KDF && WOLFSSL_CMAC */ + /* ECC */ #if defined(HAVE_ECC) [BENCH_MODULE_IDX_ECC_P256_SIGN] = {"ECC-P256-SIGN", wh_Bench_Mod_EccP256Sign, BENCH_THROUGHPUT_OPS, 0, NULL}, diff --git a/benchmark/wh_bench_ops.h b/benchmark/wh_bench_ops.h index 1b26853d6..d0da0d89d 100644 --- a/benchmark/wh_bench_ops.h +++ b/benchmark/wh_bench_ops.h @@ -26,7 +26,7 @@ #include /* Maximum number of operations that can be registered */ -#define MAX_BENCH_OPS 88 +#define MAX_BENCH_OPS 89 /* Maximum length of operation name */ #define MAX_OP_NAME 64 diff --git a/examples/demo/client/wh_demo_client_all.c b/examples/demo/client/wh_demo_client_all.c index 31eaa086a..15ee86d78 100644 --- a/examples/demo/client/wh_demo_client_all.c +++ b/examples/demo/client/wh_demo_client_all.c @@ -132,6 +132,23 @@ int wh_DemoClient_All(whClientContext* clientContext) } #endif /* HAVE_HKDF */ +#if defined(HAVE_CMAC_KDF) && defined(WOLFSSL_CMAC) + rc = wh_DemoClient_CryptoCmacKdfExport(clientContext); + if (rc != 0) { + return rc; + } + + rc = wh_DemoClient_CryptoCmacKdfCache(clientContext); + if (rc != 0) { + return rc; + } + + rc = wh_DemoClient_CryptoCmacKdfCacheInputs(clientContext); + if (rc != 0) { + return rc; + } +#endif /* HAVE_CMAC_KDF && WOLFSSL_CMAC */ + #if defined(WOLFSSL_CMAC) rc = wh_DemoClient_CryptoCmac(clientContext); if (rc != 0) { diff --git a/examples/demo/client/wh_demo_client_crypto.c b/examples/demo/client/wh_demo_client_crypto.c index d970ef742..c21d0d10a 100644 --- a/examples/demo/client/wh_demo_client_crypto.c +++ b/examples/demo/client/wh_demo_client_crypto.c @@ -40,6 +40,10 @@ #include "wolfssl/wolfcrypt/cmac.h" #endif +#if defined(HAVE_HKDF) || defined(HAVE_CMAC_KDF) +#include "wolfssl/wolfcrypt/kdf.h" +#endif + #include "wh_demo_client_crypto.h" #if !defined(NO_RSA) @@ -1503,4 +1507,157 @@ int wh_DemoClient_CryptoHkdfCacheInputKey(whClientContext* clientContext) return ret; } #endif /* HAVE_HKDF */ + +#if defined(HAVE_CMAC_KDF) && defined(WOLFSSL_CMAC) + +#define WH_DEMO_CMAC_KDF_OUT_SZ 40 + +/* Test vectors based on wolfSSL CMAC KDF implementation test vectors for + * NIST SP 800-108 KDF in Counter Mode using CMAC */ +static const uint8_t demoCmacKdfSalt[] = { + 0x20, 0x51, 0xAF, 0x34, 0x76, 0x2E, 0xBE, 0x55, 0x6F, 0x72, 0xA5, 0xC6, + 0xED, 0xC7, 0x77, 0x1E, 0xB9, 0x24, 0x5F, 0xAD, 0x76, 0xF0, 0x34, 0xBE}; +static const uint8_t demoCmacKdfZ[] = { + 0xAE, 0x8E, 0x93, 0xC9, 0xC9, 0x91, 0xCF, 0x89, 0x6A, 0x49, 0x1A, + 0x89, 0x07, 0xDF, 0x4E, 0x4B, 0xE5, 0x18, 0x6A, 0xE4, 0x96, 0xCD, + 0x34, 0x0D, 0xC1, 0x9B, 0x23, 0x78, 0x21, 0xDB, 0x7B, 0x60}; +static const uint8_t demoCmacKdfFixedInfo[] = { + 0xA2, 0x59, 0xCA, 0xE2, 0xC4, 0xA3, 0x6B, 0x89, 0x56, 0x3C, 0xB1, 0x48, + 0xC7, 0x82, 0x51, 0x34, 0x3B, 0xBF, 0xAB, 0xDC, 0x13, 0xCA, 0x7A, 0xC2, + 0x17, 0x1C, 0x2E, 0xB6, 0x02, 0x1F, 0x44, 0x77, 0xFE, 0xA3, 0x3B, 0x28, + 0x72, 0x4D, 0xA7, 0x21, 0xEE, 0x08, 0x7B, 0xFF, 0xD7, 0x94, 0xA1, 0x56, + 0x37, 0x54, 0xB4, 0x25, 0xA8, 0xD0, 0x9B, 0x3E, 0x0D, 0xA5, 0xFF, 0xED}; + +/* + * Demonstrates deriving key material using the two-step CMAC KDF through the + * wolfCrypt API. The HSM performs the derivation and returns the output + * directly to the client buffer. + */ +int wh_DemoClient_CryptoCmacKdfExport(whClientContext* clientContext) +{ + int ret = 0; + uint8_t derived[WH_DEMO_CMAC_KDF_OUT_SZ]; + + (void)clientContext; + + memset(derived, 0, sizeof(derived)); + + ret = wc_KDA_KDF_twostep_cmac( + demoCmacKdfSalt, (word32)sizeof(demoCmacKdfSalt), demoCmacKdfZ, + (word32)sizeof(demoCmacKdfZ), demoCmacKdfFixedInfo, + (word32)sizeof(demoCmacKdfFixedInfo), derived, (word32)sizeof(derived), + NULL, WH_DEV_ID); + if (ret != 0) { + printf("Failed to wc_KDA_KDF_twostep_cmac %d\n", ret); + } + + /* The key has now been derived and is stored in the 'derived' array */ + + return ret; +} + +/* + * Demonstrates deriving key material using the CMAC KDF and storing it in the + * HSM key cache. The derived key is kept non-exportable and referenced by its + * assigned keyId. + */ +int wh_DemoClient_CryptoCmacKdfCache(whClientContext* clientContext) +{ + int ret = 0; + whKeyId keyId = WH_KEYID_ERASED; + whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE; + const char label[] = "cmac-kdf cache"; + + ret = wh_Client_CmacKdfMakeCacheKey( + clientContext, WH_KEYID_ERASED, demoCmacKdfSalt, + (uint32_t)sizeof(demoCmacKdfSalt), WH_KEYID_ERASED, demoCmacKdfZ, + (uint32_t)sizeof(demoCmacKdfZ), demoCmacKdfFixedInfo, + (uint32_t)sizeof(demoCmacKdfFixedInfo), &keyId, flags, + (const uint8_t*)label, (uint32_t)strlen(label), + WH_DEMO_CMAC_KDF_OUT_SZ); + if (ret != WH_ERROR_OK) { + printf("Failed to wh_Client_CmacKdfMakeCacheKey %d\n", ret); + return ret; + } + + /* The key has now been derived and can be referenced by the client via its + * keyId in subsequent wolfCrypt or wolfHSM API calls */ + + /* Example: evict the key from cache once we are done with it */ + ret = wh_Client_KeyEvict(clientContext, keyId); + if (ret != 0) { + printf("Failed to wh_Client_KeyEvict %d\n", evictRet); + } + + return ret; +} + +/* + * Demonstrates deriving CMAC KDF output using cached salt and shared secret + * inputs and caching the derived output for subsequent use by keyId + */ +int wh_DemoClient_CryptoCmacKdfCacheInputs(whClientContext* clientContext) +{ + int ret = 0; + whKeyId saltKeyId = WH_KEYID_ERASED; + whKeyId zKeyId = WH_KEYID_ERASED; + whKeyId outKeyId = WH_KEYID_ERASED; + uint8_t derived[WH_DEMO_CMAC_KDF_OUT_SZ]; + const char label[] = "cmac-kdf inputs"; + + /* Cache the input to be used as salt. This is typically not necessary, + * as the salt is usually not sensitive information. If it is desired to use + * HSM-only information as salt, then this would likely be provisioned + * as an offline step */ + ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_NONE, NULL, 0, + demoCmacKdfSalt, (uint32_t)sizeof(demoCmacKdfSalt), + &saltKeyId); + if (ret != WH_ERROR_OK) { + printf("Failed to cache CMAC KDF salt %d\n", ret); + return ret; + } + + /* Cache the Z input. This would typically be done offline during the HSM + * provisioning step, but is shown here for completeness */ + ret = wh_Client_KeyCache(clientContext, WH_NVM_FLAGS_NONE, NULL, 0, + demoCmacKdfZ, (uint32_t)sizeof(demoCmacKdfZ), + &zKeyId); + if (ret != WH_ERROR_OK) { + printf("Failed to cache CMAC KDF Z input %d\n", ret); + /* Optionally evict the salt key if not needed anymore */ + (void)wh_Client_KeyEvict(clientContext, saltKeyId); + return ret; + } + + /* Cached inputs are now present on the HSM. At a later time, they can be + * used to derive a key */ + + + /* Derive and cache the new key based on the cached input */ + memset(derived, 0, sizeof(derived)); + + ret = wh_Client_CmacKdfMakeCacheKey( + clientContext, saltKeyId, NULL, 0, zKeyId, NULL, 0, + demoCmacKdfFixedInfo, (uint32_t)sizeof(demoCmacKdfFixedInfo), &outKeyId, + WH_NVM_FLAGS_NONE, (const uint8_t*)label, (uint32_t)strlen(label), + WH_DEMO_CMAC_KDF_OUT_SZ); + + if (ret != WH_ERROR_OK) { + printf("Failed to CMAC KDF with cached inputs %d\n", ret); + } + + + /* We can now refer to the generated key by its keyID from the client */ + + /* Optionally evict the keys from the cache if not needed anymore. + * Ignore failure checking for readability */ + (void)wh_Client_KeyEvict(clientContext, outKeyId); + (void)wh_Client_KeyEvict(clientContext, zKeyId); + (void)wh_Client_KeyEvict(clientContext, saltKeyId); + + return ret; +} + +#endif /* HAVE_CMAC_KDF && WOLFSSL_CMAC */ + #endif /* WOLFHSM_CFG_NO_CRYPTO */ diff --git a/examples/demo/client/wh_demo_client_crypto.h b/examples/demo/client/wh_demo_client_crypto.h index db143a47c..72fbb187d 100644 --- a/examples/demo/client/wh_demo_client_crypto.h +++ b/examples/demo/client/wh_demo_client_crypto.h @@ -26,4 +26,8 @@ int wh_DemoClient_CryptoHkdfExport(whClientContext* clientContext); int wh_DemoClient_CryptoHkdfCache(whClientContext* clientContext); int wh_DemoClient_CryptoHkdfCacheInputKey(whClientContext* clientContext); +int wh_DemoClient_CryptoCmacKdfExport(whClientContext* clientContext); +int wh_DemoClient_CryptoCmacKdfCache(whClientContext* clientContext); +int wh_DemoClient_CryptoCmacKdfCacheInputs(whClientContext* clientContext); + #endif /* !DEMO_CLIENT_CRYPTO_H_ */ diff --git a/examples/posix/wh_posix_server/user_settings.h b/examples/posix/wh_posix_server/user_settings.h index 45bf0167d..3241ee996 100644 --- a/examples/posix/wh_posix_server/user_settings.h +++ b/examples/posix/wh_posix_server/user_settings.h @@ -146,6 +146,7 @@ extern "C" { /** Composite features */ #define HAVE_HKDF +#define HAVE_CMAC_KDF /* Remove unneeded crypto */ #define NO_DSA diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index 815b8ad15..ff3a8a1a2 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -100,6 +100,16 @@ static int _HkdfMakeKey(whClientContext* ctx, int hashType, whKeyId keyIdIn, whKeyId* inout_key_id, uint8_t* out, uint32_t outSz); #endif +#if defined(HAVE_CMAC_KDF) +static int _CmacKdfMakeKey(whClientContext* ctx, whKeyId saltKeyId, + const uint8_t* salt, uint32_t saltSz, whKeyId zKeyId, + const uint8_t* z, uint32_t zSz, + const uint8_t* fixedInfo, uint32_t fixedInfoSz, + whNvmFlags flags, uint32_t label_len, + const uint8_t* label, whKeyId* inout_key_id, + uint8_t* out, uint32_t outSz); +#endif + #ifdef HAVE_DILITHIUM /* Make a ML-DSA key on the server based on the flags */ static int _MlDsaMakeKey(whClientContext* ctx, int size, int level, @@ -2597,8 +2607,8 @@ static int _HkdfMakeKey(whClientContext* ctx, int hashType, whKeyId keyIdIn, } /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_HkdfRequest*)_createCryptoRequest(dataPtr, - WC_ALGO_TYPE_KDF); + req = (whMessageCrypto_HkdfRequest*)_createCryptoRequestWithSubtype( + dataPtr, WC_ALGO_TYPE_KDF, WC_KDF_TYPE_HKDF); /* Calculate request length including variable-length data */ uint16_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + @@ -2741,6 +2751,160 @@ int wh_Client_HkdfMakeExportKey(whClientContext* ctx, int hashType, #endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF +static int _CmacKdfMakeKey(whClientContext* ctx, whKeyId saltKeyId, + const uint8_t* salt, uint32_t saltSz, whKeyId zKeyId, + const uint8_t* z, uint32_t zSz, + const uint8_t* fixedInfo, uint32_t fixedInfoSz, + whNvmFlags flags, uint32_t label_len, + const uint8_t* label, whKeyId* inout_key_id, + uint8_t* out, uint32_t outSz) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_CmacKdfRequest* req = NULL; + whMessageCrypto_CmacKdfResponse* res = NULL; + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WC_ALGO_TYPE_KDF; + whKeyId key_id = WH_KEYID_ERASED; + + if ((ctx == NULL) || (outSz == 0)) { + return WH_ERROR_BADARGS; + } + + if ((saltSz > 0 && salt == NULL) || (zSz > 0 && z == NULL) || + (fixedInfoSz > 0 && fixedInfo == NULL)) { + return WH_ERROR_BADARGS; + } + + /* Retrieve the shared communication buffer */ + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + /* Prepare request structure with subtype information */ + req = (whMessageCrypto_CmacKdfRequest*)_createCryptoRequestWithSubtype( + dataPtr, WC_ALGO_TYPE_KDF, WC_KDF_TYPE_TWOSTEP_CMAC); + + uint32_t total_len = sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + saltSz + zSz + fixedInfoSz; + + if (total_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + uint16_t req_len = (uint16_t)total_len; + + if (inout_key_id != NULL) { + key_id = *inout_key_id; + } + + req->flags = flags; + req->keyIdSalt = saltKeyId; + req->keyIdZ = zKeyId; + req->keyIdOut = key_id; + req->saltSz = saltSz; + req->zSz = zSz; + req->fixedInfoSz = fixedInfoSz; + req->outSz = outSz; + + memset(req->label, 0, WH_NVM_LABEL_LEN); + if ((label != NULL) && (label_len > 0)) { + if (label_len > WH_NVM_LABEL_LEN) { + label_len = WH_NVM_LABEL_LEN; + } + memcpy(req->label, label, label_len); + } + + uint8_t* payload = (uint8_t*)(req + 1); + + if (saltSz > 0 && salt != NULL) { + memcpy(payload, salt, saltSz); + payload += saltSz; + } + + if (zSz > 0 && z != NULL) { + memcpy(payload, z, zSz); + payload += zSz; + } + + if (fixedInfoSz > 0 && fixedInfo != NULL) { + memcpy(payload, fixedInfo, fixedInfoSz); + payload += fixedInfoSz; + } + + /* squash unused warning */ + (void)payload; + + ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); + if (ret != WH_ERROR_OK) { + return ret; + } + + uint16_t res_len = 0; + do { + ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); + } while (ret == WH_ERROR_NOTREADY); + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_ALGO_TYPE_KDF, (uint8_t**)&res); + } + + if (ret == WH_ERROR_OK) { + key_id = (whKeyId)(res->keyIdOut); + + if (inout_key_id != NULL) { + *inout_key_id = key_id; + } + + if (out != NULL) { + if (res->outSz <= outSz) { + uint8_t* out_data = (uint8_t*)(res + 1); + memcpy(out, out_data, res->outSz); + } + else { + ret = WH_ERROR_ABORTED; + } + } + } + + return ret; +} + +int wh_Client_CmacKdfMakeCacheKey(whClientContext* ctx, whKeyId saltKeyId, + const uint8_t* salt, uint32_t saltSz, + whKeyId zKeyId, const uint8_t* z, + uint32_t zSz, const uint8_t* fixedInfo, + uint32_t fixedInfoSz, whKeyId* inout_key_id, + whNvmFlags flags, const uint8_t* label, + uint32_t label_len, uint32_t outSz) +{ + if ((ctx == NULL) || (inout_key_id == NULL)) { + return WH_ERROR_BADARGS; + } + + return _CmacKdfMakeKey(ctx, saltKeyId, salt, saltSz, zKeyId, z, zSz, + fixedInfo, fixedInfoSz, flags, label_len, label, + inout_key_id, NULL, outSz); +} + +int wh_Client_CmacKdfMakeExportKey(whClientContext* ctx, whKeyId saltKeyId, + const uint8_t* salt, uint32_t saltSz, + whKeyId zKeyId, const uint8_t* z, + uint32_t zSz, const uint8_t* fixedInfo, + uint32_t fixedInfoSz, uint8_t* out, + uint32_t outSz) +{ + if ((ctx == NULL) || (out == NULL)) { + return WH_ERROR_BADARGS; + } + + return _CmacKdfMakeKey(ctx, saltKeyId, salt, saltSz, zKeyId, z, zSz, + fixedInfo, fixedInfoSz, WH_NVM_FLAGS_EPHEMERAL, 0, + NULL, NULL, out, outSz); +} +#endif /* HAVE_CMAC_KDF */ + #ifndef NO_AES int wh_Client_AesSetKeyId(Aes* key, whNvmId keyId) { diff --git a/src/wh_client_cryptocb.c b/src/wh_client_cryptocb.c index 4054077ce..c4169ba1a 100644 --- a/src/wh_client_cryptocb.c +++ b/src/wh_client_cryptocb.c @@ -471,10 +471,11 @@ int wh_Client_CryptoCb(int devId, wc_CryptoInfo* info, void* inCtx) } } break; /* case WC_ALGO_TYPE_HASH */ -#ifdef HAVE_HKDF +#if defined(HAVE_HKDF) || defined(HAVE_CMAC_KDF) case WC_ALGO_TYPE_KDF: { /* Handle different KDF types */ switch (info->kdf.type) { +#ifdef HAVE_HKDF case WC_KDF_TYPE_HKDF: { /* Extract HKDF-specific parameters */ int hashType = info->kdf.hkdf.hashType; @@ -491,12 +492,29 @@ int wh_Client_CryptoCb(int devId, wc_CryptoInfo* info, void* inCtx) ctx, hashType, WH_KEYID_ERASED, inKey, inKeySz, salt, saltSz, kdf_info, infoSz, out, outSz); } break; +#endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF + case WC_KDF_TYPE_TWOSTEP_CMAC: { + const byte* salt = info->kdf.twostep_cmac.salt; + word32 saltSz = info->kdf.twostep_cmac.saltSz; + const byte* z = info->kdf.twostep_cmac.z; + word32 zSz = info->kdf.twostep_cmac.zSz; + const byte* fixedInfo = info->kdf.twostep_cmac.fixedInfo; + word32 fixedInfoSz = info->kdf.twostep_cmac.fixedInfoSz; + byte* out = info->kdf.twostep_cmac.out; + word32 outSz = info->kdf.twostep_cmac.outSz; + + ret = wh_Client_CmacKdfMakeExportKey( + ctx, WH_KEYID_ERASED, salt, saltSz, WH_KEYID_ERASED, z, zSz, + fixedInfo, fixedInfoSz, out, outSz); + } break; +#endif /* HAVE_CMAC_KDF */ default: ret = CRYPTOCB_UNAVAILABLE; break; } } break; /* case WC_ALGO_TYPE_KDF */ -#endif /* HAVE_HKDF */ +#endif /* HAVE_HKDF || HAVE_CMAC_KDF */ case WC_ALGO_TYPE_NONE: default: diff --git a/src/wh_message_crypto.c b/src/wh_message_crypto.c index fea340c6f..009ea2193 100644 --- a/src/wh_message_crypto.c +++ b/src/wh_message_crypto.c @@ -309,6 +309,39 @@ int wh_MessageCrypto_TranslateHkdfResponse( return 0; } +int wh_MessageCrypto_TranslateCmacKdfRequest( + uint16_t magic, const whMessageCrypto_CmacKdfRequest* src, + whMessageCrypto_CmacKdfRequest* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + WH_T32(magic, dest, src, flags); + WH_T32(magic, dest, src, keyIdSalt); + WH_T32(magic, dest, src, keyIdZ); + WH_T32(magic, dest, src, keyIdOut); + WH_T32(magic, dest, src, saltSz); + WH_T32(magic, dest, src, zSz); + WH_T32(magic, dest, src, fixedInfoSz); + WH_T32(magic, dest, src, outSz); + if (src != dest) { + memcpy(dest->label, src->label, WH_NVM_LABEL_LEN); + } + return 0; +} + +int wh_MessageCrypto_TranslateCmacKdfResponse( + uint16_t magic, const whMessageCrypto_CmacKdfResponse* src, + whMessageCrypto_CmacKdfResponse* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + WH_T32(magic, dest, src, keyIdOut); + WH_T32(magic, dest, src, outSz); + return 0; +} + /* ECC Key Generation Request translation */ int wh_MessageCrypto_TranslateEccKeyGenRequest( uint16_t magic, const whMessageCrypto_EccKeyGenRequest* src, diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 6713b5f4d..c9b56f2fb 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -44,6 +44,7 @@ #include "wolfssl/wolfcrypt/cmac.h" #include "wolfssl/wolfcrypt/dilithium.h" #include "wolfssl/wolfcrypt/hmac.h" +#include "wolfssl/wolfcrypt/kdf.h" #include "wolfhsm/wh_error.h" #include "wolfhsm/wh_crypto.h" @@ -1190,6 +1191,45 @@ int wh_Server_HkdfKeyCacheImport(whServerContext* ctx, const uint8_t* keyData, return ret; } +#endif /* HAVE_HKDF */ + +#ifdef HAVE_CMAC_KDF +int wh_Server_CmacKdfKeyCacheImport(whServerContext* ctx, + const uint8_t* keyData, uint32_t keySize, + whKeyId keyId, whNvmFlags flags, + uint16_t label_len, uint8_t* label) +{ + int ret = WH_ERROR_OK; + uint8_t* cacheBuf; + whNvmMetadata* cacheMeta; + + if ((ctx == NULL) || (keyData == NULL) || WH_KEYID_ISERASED(keyId) || + ((label != NULL) && (label_len > sizeof(cacheMeta->label)))) { + return WH_ERROR_BADARGS; + } + + ret = wh_Server_KeystoreGetCacheSlot(ctx, keyId, keySize, &cacheBuf, + &cacheMeta); + if (ret == WH_ERROR_OK) { + memcpy(cacheBuf, keyData, keySize); + } + + if (ret == WH_ERROR_OK) { + cacheMeta->id = keyId; + cacheMeta->len = keySize; + cacheMeta->flags = flags; + cacheMeta->access = WH_NVM_ACCESS_ANY; + + if ((label != NULL) && (label_len > 0)) { + memcpy(cacheMeta->label, label, label_len); + } + } + + return ret; +} +#endif /* HAVE_CMAC_KDF */ + +#ifdef HAVE_HKDF static int _HandleHkdf(whServerContext* ctx, uint16_t magic, const void* cryptoDataIn, uint16_t inSize, void* cryptoDataOut, uint16_t* outSize) @@ -1313,6 +1353,127 @@ static int _HandleHkdf(whServerContext* ctx, uint16_t magic, } #endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF +static int _HandleCmacKdf(whServerContext* ctx, uint16_t magic, + const void* cryptoDataIn, uint16_t inSize, + void* cryptoDataOut, uint16_t* outSize) +{ + (void)inSize; + + int ret = WH_ERROR_OK; + whMessageCrypto_CmacKdfRequest req; + whMessageCrypto_CmacKdfResponse res; + + memset(&res, 0, sizeof(res)); + + ret = wh_MessageCrypto_TranslateCmacKdfRequest( + magic, (const whMessageCrypto_CmacKdfRequest*)cryptoDataIn, &req); + if (ret != 0) { + return ret; + } + + uint32_t saltSz = req.saltSz; + uint32_t zSz = req.zSz; + uint32_t fixedInfoSz = req.fixedInfoSz; + uint32_t outSz = req.outSz; + whKeyId keyIdOut = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdOut); + whKeyId saltKeyId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdSalt); + whKeyId zKeyId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyIdZ); + whNvmFlags flags = (whNvmFlags)req.flags; + uint8_t* label = req.label; + uint16_t label_size = WH_NVM_LABEL_LEN; + + const uint8_t* salt = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_CmacKdfRequest); + const uint8_t* z = salt + saltSz; + const uint8_t* fixedInfo = z + zSz; + + uint8_t* cachedSaltBuf = NULL; + whNvmMetadata* cachedSaltMeta = NULL; + uint8_t* cachedZBuf = NULL; + whNvmMetadata* cachedZMeta = NULL; + + if (saltSz == 0) { + if (WH_KEYID_ISERASED(saltKeyId)) { + return WH_ERROR_BADARGS; + } + ret = wh_Server_KeystoreFreshenKey(ctx, saltKeyId, &cachedSaltBuf, + &cachedSaltMeta); + if (ret != WH_ERROR_OK) { + return ret; + } + salt = cachedSaltBuf; + saltSz = cachedSaltMeta->len; + } + + if (zSz == 0) { + if (WH_KEYID_ISERASED(zKeyId)) { + return WH_ERROR_BADARGS; + } + ret = wh_Server_KeystoreFreshenKey(ctx, zKeyId, &cachedZBuf, + &cachedZMeta); + if (ret != WH_ERROR_OK) { + return ret; + } + z = cachedZBuf; + zSz = cachedZMeta->len; + } + + if ((salt == NULL) || (z == NULL) || (outSz == 0)) { + return WH_ERROR_BADARGS; + } + + uint8_t* out = + (uint8_t*)cryptoDataOut + sizeof(whMessageCrypto_CmacKdfResponse); + uint16_t max_size = (uint16_t)(WOLFHSM_CFG_COMM_DATA_LEN - + ((uint8_t*)out - (uint8_t*)cryptoDataOut)); + + if (outSz > max_size) { + return WH_ERROR_BADARGS; + } + + ret = wc_KDA_KDF_twostep_cmac( + salt, saltSz, z, zSz, (fixedInfoSz > 0) ? fixedInfo : NULL, fixedInfoSz, + out, outSz, NULL, ctx->crypto->devId); + if (ret == 0) { + if (flags & WH_NVM_FLAGS_EPHEMERAL) { + keyIdOut = WH_KEYID_ERASED; + res.keyIdOut = WH_KEYID_ERASED; + res.outSz = outSz; + } + else { + if (WH_KEYID_ISERASED(keyIdOut)) { + ret = wh_Server_KeystoreGetUniqueId(ctx, &keyIdOut); + if (ret != WH_ERROR_OK) { + return ret; + } + } + + ret = wh_Server_CmacKdfKeyCacheImport(ctx, out, outSz, keyIdOut, + flags, label_size, label); + if (ret == WH_ERROR_OK) { + res.keyIdOut = wh_KeyId_TranslateToClient(keyIdOut); + res.outSz = 0; + memset(out, 0, outSz); + } + } + } + + if (ret == WH_ERROR_OK) { + ret = wh_MessageCrypto_TranslateCmacKdfResponse( + magic, &res, (whMessageCrypto_CmacKdfResponse*)cryptoDataOut); + if (ret == 0) { + *outSize = sizeof(whMessageCrypto_CmacKdfResponse) + res.outSz; + } + } + + return ret; +} +#endif /* HAVE_CMAC_KDF */ + #ifdef HAVE_CURVE25519 static int _HandleCurve25519KeyGen(whServerContext* ctx, uint16_t magic, const void* cryptoDataIn, uint16_t inSize, @@ -3212,12 +3373,27 @@ int wh_Server_HandleCryptoRequest(whServerContext* ctx, uint16_t magic, break; #endif /* !WC_NO_RNG */ -#ifdef HAVE_HKDF +#if defined(HAVE_HKDF) || defined(HAVE_CMAC_KDF) case WC_ALGO_TYPE_KDF: - ret = _HandleHkdf(ctx, magic, cryptoDataIn, cryptoInSize, - cryptoDataOut, &cryptoOutSize); - break; + switch (rqstHeader.algoSubType) { +#ifdef HAVE_HKDF + case WC_KDF_TYPE_HKDF: + ret = _HandleHkdf(ctx, magic, cryptoDataIn, cryptoInSize, + cryptoDataOut, &cryptoOutSize); + break; #endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF + case WC_KDF_TYPE_TWOSTEP_CMAC: + ret = _HandleCmacKdf(ctx, magic, cryptoDataIn, cryptoInSize, + cryptoDataOut, &cryptoOutSize); + break; +#endif /* HAVE_CMAC_KDF */ + default: + ret = NOT_COMPILED_IN; + break; + } + break; +#endif /* HAVE_HKDF || HAVE_CMAC_KDF */ #ifdef WOLFSSL_CMAC case WC_ALGO_TYPE_CMAC: diff --git a/test/config/user_settings.h b/test/config/user_settings.h index 838d050ff..1d341c218 100644 --- a/test/config/user_settings.h +++ b/test/config/user_settings.h @@ -154,6 +154,7 @@ /** Composite features */ #define HAVE_HKDF +#define HAVE_CMAC_KDF /* Remove unneeded crypto */ #define NO_DSA diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 1175e9e76..35310c6ea 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -31,6 +31,7 @@ #include "wolfssl/wolfcrypt/settings.h" #include "wolfssl/wolfcrypt/types.h" +#include "wolfssl/wolfcrypt/kdf.h" #include "wolfhsm/wh_error.h" @@ -1246,6 +1247,193 @@ static int whTest_CryptoHkdf(whClientContext* ctx, int devId, WC_RNG* rng) } #endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF +#define WH_TEST_CMAC_KDF_SALT_SIZE 24 +#define WH_TEST_CMAC_KDF_Z_SIZE 32 +#define WH_TEST_CMAC_KDF_FIXED_INFO_SIZE 60 +#define WH_TEST_CMAC_KDF_OUT_SIZE 40 + +static int whTest_CryptoCmacKdf(whClientContext* ctx, int devId, WC_RNG* rng) +{ + (void)rng; + + int ret = WH_ERROR_OK; + + /* Test vectors based on wolfSSL CMAC KDF implementation test vectors for + * NIST SP 800-108 KDF in Counter Mode using CMAC */ + static const uint8_t salt[WH_TEST_CMAC_KDF_SALT_SIZE] = { + 0x20, 0x51, 0xaf, 0x34, 0x76, 0x2e, 0xbe, 0x55, 0x6f, 0x72, 0xa5, 0xc6, + 0xed, 0xc7, 0x77, 0x1e, 0xb9, 0x24, 0x5f, 0xad, 0x76, 0xf0, 0x34, 0xbe}; + static const uint8_t z[WH_TEST_CMAC_KDF_Z_SIZE] = { + 0xae, 0x8e, 0x93, 0xc9, 0xc9, 0x91, 0xcf, 0x89, 0x6a, 0x49, 0x1a, + 0x89, 0x07, 0xdf, 0x4e, 0x4b, 0xe5, 0x18, 0x6a, 0xe4, 0x96, 0xcd, + 0x34, 0x0d, 0xc1, 0x9b, 0x23, 0x78, 0x21, 0xdb, 0x7b, 0x60}; + static const uint8_t fixedInfo[WH_TEST_CMAC_KDF_FIXED_INFO_SIZE] = { + 0xa2, 0x59, 0xca, 0xe2, 0xc4, 0xa3, 0x6b, 0x89, 0x56, 0x3c, 0xb1, 0x48, + 0xc7, 0x82, 0x51, 0x34, 0x3b, 0xbf, 0xab, 0xdc, 0x13, 0xca, 0x7a, 0xc2, + 0x17, 0x1c, 0x2e, 0xb6, 0x02, 0x1f, 0x44, 0x77, 0xfe, 0xa3, 0x3b, 0x28, + 0x72, 0x4d, 0xa7, 0x21, 0xee, 0x08, 0x7b, 0xff, 0xd7, 0x94, 0xa1, 0x56, + 0x37, 0x54, 0xb4, 0x25, 0xa8, 0xd0, 0x9b, 0x3e, 0x0d, 0xa5, 0xff, 0xed}; + static const uint8_t expected[WH_TEST_CMAC_KDF_OUT_SIZE] = { + 0xb4, 0x0c, 0x32, 0xbe, 0x01, 0x27, 0x93, 0xba, 0xfd, 0xf7, + 0x78, 0xc5, 0xf4, 0x54, 0x43, 0xf4, 0xc9, 0x71, 0x23, 0x93, + 0x17, 0x63, 0xd8, 0x3a, 0x59, 0x27, 0x07, 0xbf, 0xf2, 0xd3, + 0x60, 0x59, 0x50, 0x27, 0x29, 0xca, 0xb8, 0x8b, 0x29, 0x38}; + + uint8_t out[WH_TEST_CMAC_KDF_OUT_SIZE]; + uint8_t out_direct[WH_TEST_CMAC_KDF_OUT_SIZE]; + uint8_t out_cached_input[WH_TEST_CMAC_KDF_OUT_SIZE]; + uint8_t exported[WH_TEST_CMAC_KDF_OUT_SIZE]; + uint8_t exportLabel[12] = {0}; + uint16_t export_len = WH_TEST_CMAC_KDF_OUT_SIZE; + whKeyId key_id = WH_KEYID_ERASED; + uint8_t keyLabel[] = "CMAC KDF Key"; + + /* 1. Direct wolfCrypt API call that routes through the callback */ + memset(out, 0, sizeof(out)); + ret = wc_KDA_KDF_twostep_cmac(salt, WH_TEST_CMAC_KDF_SALT_SIZE, z, + WH_TEST_CMAC_KDF_Z_SIZE, fixedInfo, + WH_TEST_CMAC_KDF_FIXED_INFO_SIZE, out, + WH_TEST_CMAC_KDF_OUT_SIZE, NULL, devId); + if (ret != 0) { + WH_ERROR_PRINT("Failed wc_KDA_KDF_twostep_cmac: %d\n", ret); + return ret; + } + if (memcmp(out, expected, sizeof(out)) != 0) { + WH_ERROR_PRINT("CMAC KDF output mismatch (direct wolfCrypt)\n"); + return -1; + } + + /* 2. Client export path using direct input buffers */ + memset(out_direct, 0, sizeof(out_direct)); + ret = wh_Client_CmacKdfMakeExportKey( + ctx, WH_KEYID_ERASED, salt, WH_TEST_CMAC_KDF_SALT_SIZE, WH_KEYID_ERASED, + z, WH_TEST_CMAC_KDF_Z_SIZE, fixedInfo, WH_TEST_CMAC_KDF_FIXED_INFO_SIZE, + out_direct, sizeof(out_direct)); + if (ret != 0) { + WH_ERROR_PRINT("Failed wh_Client_CmacKdfMakeExportKey: %d\n", ret); + return ret; + } + if (memcmp(out_direct, expected, sizeof(out_direct)) != 0) { + WH_ERROR_PRINT("CMAC KDF output mismatch (export key)\n"); + return -1; + } + + /* 3. Client cache path with direct input buffers */ + key_id = WH_KEYID_ERASED; + ret = wh_Client_CmacKdfMakeCacheKey( + ctx, WH_KEYID_ERASED, salt, WH_TEST_CMAC_KDF_SALT_SIZE, WH_KEYID_ERASED, + z, WH_TEST_CMAC_KDF_Z_SIZE, fixedInfo, WH_TEST_CMAC_KDF_FIXED_INFO_SIZE, + &key_id, WH_NVM_FLAGS_NONE, keyLabel, sizeof(keyLabel), + WH_TEST_CMAC_KDF_OUT_SIZE); + if (ret != 0) { + WH_ERROR_PRINT("Failed wh_Client_CmacKdfMakeCacheKey: %d\n", ret); + return ret; + } + if (key_id == WH_KEYID_ERASED) { + WH_ERROR_PRINT("CMAC KDF cache did not return a key id\n"); + return -1; + } + + memset(exported, 0, sizeof(exported)); + export_len = (uint16_t)sizeof(exported); + memset(exportLabel, 0, sizeof(exportLabel)); + ret = wh_Client_KeyExport(ctx, key_id, exportLabel, sizeof(exportLabel), + exported, &export_len); + if (ret != 0) { + WH_ERROR_PRINT("Failed to export cached CMAC KDF key: %d\n", ret); + (void)wh_Client_KeyEvict(ctx, key_id); + return ret; + } + if ((export_len != WH_TEST_CMAC_KDF_OUT_SIZE) || + (memcmp(exported, expected, sizeof(exported)) != 0)) { + WH_ERROR_PRINT("Exported CMAC KDF key mismatch\n"); + (void)wh_Client_KeyEvict(ctx, key_id); + return -1; + } + ret = wh_Client_KeyEvict(ctx, key_id); + if (ret != 0) { + WH_ERROR_PRINT("Failed to evict cached CMAC KDF key: %d\n", ret); + return ret; + } + + /* 4. Use cached salt and Z inputs */ + whKeyId saltKeyId = WH_KEYID_ERASED; + whKeyId zKeyId = WH_KEYID_ERASED; + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_NONE, NULL, 0, salt, + WH_TEST_CMAC_KDF_SALT_SIZE, &saltKeyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to cache CMAC KDF salt: %d\n", ret); + return ret; + } + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_NONE, NULL, 0, z, + WH_TEST_CMAC_KDF_Z_SIZE, &zKeyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to cache CMAC KDF Z input: %d\n", ret); + (void)wh_Client_KeyEvict(ctx, saltKeyId); + return ret; + } + + memset(out_cached_input, 0, sizeof(out_cached_input)); + ret = wh_Client_CmacKdfMakeExportKey( + ctx, saltKeyId, NULL, 0, zKeyId, NULL, 0, fixedInfo, + WH_TEST_CMAC_KDF_FIXED_INFO_SIZE, out_cached_input, + sizeof(out_cached_input)); + if (ret != 0) { + WH_ERROR_PRINT("Failed CMAC KDF export with cached inputs: %d\n", ret); + goto cleanup_inputs; + } + if (memcmp(out_cached_input, expected, sizeof(out_cached_input)) != 0) { + WH_ERROR_PRINT("CMAC KDF mismatch (cached inputs export)\n"); + ret = -1; + goto cleanup_inputs; + } + + key_id = WH_KEYID_ERASED; + ret = wh_Client_CmacKdfMakeCacheKey( + ctx, saltKeyId, NULL, 0, zKeyId, NULL, 0, fixedInfo, + WH_TEST_CMAC_KDF_FIXED_INFO_SIZE, &key_id, WH_NVM_FLAGS_NONE, keyLabel, + sizeof(keyLabel), WH_TEST_CMAC_KDF_OUT_SIZE); + if (ret != 0) { + WH_ERROR_PRINT("Failed CMAC KDF cache with cached inputs: %d\n", ret); + goto cleanup_inputs; + } + if (key_id == WH_KEYID_ERASED) { + WH_ERROR_PRINT( + "CMAC KDF cache (cached inputs) did not return key id\n"); + ret = -1; + goto cleanup_inputs; + } + + memset(exported, 0, sizeof(exported)); + export_len = (uint16_t)sizeof(exported); + memset(exportLabel, 0, sizeof(exportLabel)); + ret = wh_Client_KeyExport(ctx, key_id, exportLabel, sizeof(exportLabel), + exported, &export_len); + if ((ret != 0) || (export_len != WH_TEST_CMAC_KDF_OUT_SIZE) || + (memcmp(exported, expected, sizeof(exported)) != 0)) { + WH_ERROR_PRINT("Export mismatch for CMAC KDF cached inputs (ret=%d)\n", + ret); + if (ret == 0) { + ret = -1; + } + } + + (void)wh_Client_KeyEvict(ctx, key_id); + +cleanup_inputs: + (void)wh_Client_KeyEvict(ctx, saltKeyId); + (void)wh_Client_KeyEvict(ctx, zKeyId); + + if (ret != WH_ERROR_OK) { + return ret; + } + + printf("CMAC KDF SUCCESS\n"); + return 0; +} +#endif /* HAVE_CMAC_KDF */ + static int whTest_CacheExportKey(whClientContext* ctx, whKeyId* inout_key_id, uint8_t* label_in, uint8_t* label_out, uint16_t label_len, uint8_t* key_in, uint8_t* key_out, uint16_t key_len) @@ -3691,6 +3879,12 @@ int whTest_CryptoClientConfig(whClientConfig* config) } #endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoCmacKdf(client, WH_DEV_ID, rng); + } +#endif /* HAVE_CMAC_KDF */ + #ifdef HAVE_DILITHIUM #if !defined(WOLFSSL_DILITHIUM_NO_VERIFY) && \ diff --git a/wolfhsm/wh_client_crypto.h b/wolfhsm/wh_client_crypto.h index dccda70c1..096d4d1b7 100644 --- a/wolfhsm/wh_client_crypto.h +++ b/wolfhsm/wh_client_crypto.h @@ -426,6 +426,74 @@ int wh_Client_HkdfMakeExportKey(whClientContext* ctx, int hashType, #endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF +/** + * @brief Generate CMAC two-step KDF output and store it in the server cache + * + * This function requests the server to run the NIST SP 800-56C two-step CMAC + * KDF. The derived key material is cached on the server and not returned to the + * client. + * + * @param[in] ctx Pointer to the client context. + * @param[in] saltKeyId Key ID of the salt material. Set to WH_KEYID_ERASED to + * use the salt buffer instead. + * @param[in] salt Pointer to the salt buffer. May be NULL when saltKeyId is + * provided. + * @param[in] saltSz Size of the salt buffer in bytes. + * @param[in] zKeyId Key ID of the Z shared secret. Set to WH_KEYID_ERASED to + * use the z buffer instead. + * @param[in] z Pointer to the shared secret buffer. May be NULL when zKeyId is + * provided. + * @param[in] zSz Size of the shared secret buffer in bytes. + * @param[in] fixedInfo Optional fixed info buffer (may be NULL). + * @param[in] fixedInfoSz Size of the fixed info buffer in bytes. + * @param[in,out] inout_key_id Pointer to the key ID to use or update. Set to + * WH_KEYID_ERASED to have the server allocate one. + * @param[in] flags NVM flags to associate with the generated key. + * @param[in] label Optional label metadata to store alongside the key. + * @param[in] label_len Length of the optional label in bytes. + * @param[in] outSz Desired size of the derived key material. + * @return int Returns 0 on success or a negative error code on failure. + */ +int wh_Client_CmacKdfMakeCacheKey(whClientContext* ctx, whKeyId saltKeyId, + const uint8_t* salt, uint32_t saltSz, + whKeyId zKeyId, const uint8_t* z, + uint32_t zSz, const uint8_t* fixedInfo, + uint32_t fixedInfoSz, whKeyId* inout_key_id, + whNvmFlags flags, const uint8_t* label, + uint32_t label_len, uint32_t outSz); + +/** + * @brief Generate CMAC two-step KDF output and export to the client + * + * This function requests the server to run the NIST SP 800-56C two-step CMAC + * KDF and return the derived key material directly to the client. + * + * @param[in] ctx Pointer to the client context. + * @param[in] saltKeyId Key ID of the salt material. Set to WH_KEYID_ERASED to + * use the salt buffer instead. + * @param[in] salt Pointer to the salt buffer. May be NULL when saltKeyId is + * provided. + * @param[in] saltSz Size of the salt buffer in bytes. + * @param[in] zKeyId Key ID of the Z shared secret. Set to WH_KEYID_ERASED to + * use the z buffer instead. + * @param[in] z Pointer to the shared secret buffer. May be NULL when zKeyId is + * provided. + * @param[in] zSz Size of the shared secret buffer in bytes. + * @param[in] fixedInfo Optional fixed info buffer (may be NULL). + * @param[in] fixedInfoSz Size of the fixed info buffer in bytes. + * @param[out] out Output buffer for the derived key material. + * @param[in] outSz Size of the output buffer in bytes. + * @return int Returns 0 on success or a negative error code on failure. + */ +int wh_Client_CmacKdfMakeExportKey(whClientContext* ctx, whKeyId saltKeyId, + const uint8_t* salt, uint32_t saltSz, + whKeyId zKeyId, const uint8_t* z, + uint32_t zSz, const uint8_t* fixedInfo, + uint32_t fixedInfoSz, uint8_t* out, + uint32_t outSz); +#endif /* HAVE_CMAC_KDF */ + #ifndef NO_AES /** * @brief Associates an AES key with a specific key ID. diff --git a/wolfhsm/wh_message_crypto.h b/wolfhsm/wh_message_crypto.h index 23e1f5fa4..6b4272c12 100644 --- a/wolfhsm/wh_message_crypto.h +++ b/wolfhsm/wh_message_crypto.h @@ -415,6 +415,43 @@ int wh_MessageCrypto_TranslateHkdfResponse( uint16_t magic, const whMessageCrypto_HkdfResponse* src, whMessageCrypto_HkdfResponse* dest); +/* + * CMAC KDF + */ + +typedef struct { + uint32_t flags; /* NVM flags */ + uint32_t keyIdSalt; /* Key ID for salt material (from cache) */ + uint32_t keyIdZ; /* Key ID for Z material (from cache) */ + uint32_t keyIdOut; /* Key ID if caching output */ + uint32_t saltSz; /* Salt size (0 if using keyIdSalt) */ + uint32_t zSz; /* Z input size (0 if using keyIdZ) */ + uint32_t fixedInfoSz; /* Fixed info size (0 if none) */ + uint32_t outSz; /* Output size */ + uint8_t label[WH_NVM_LABEL_LEN]; + /* Data follows: + * uint8_t salt[saltSz] + * uint8_t z[zSz] + * uint8_t fixedInfo[fixedInfoSz] + */ +} whMessageCrypto_CmacKdfRequest; + +typedef struct { + uint32_t keyIdOut; /* Assigned key ID */ + uint32_t outSz; /* Output size */ + /* Data follows: + * uint8_t out[outSz] + */ +} whMessageCrypto_CmacKdfResponse; + +int wh_MessageCrypto_TranslateCmacKdfRequest( + uint16_t magic, const whMessageCrypto_CmacKdfRequest* src, + whMessageCrypto_CmacKdfRequest* dest); + +int wh_MessageCrypto_TranslateCmacKdfResponse( + uint16_t magic, const whMessageCrypto_CmacKdfResponse* src, + whMessageCrypto_CmacKdfResponse* dest); + /* * ECC */ diff --git a/wolfhsm/wh_server_crypto.h b/wolfhsm/wh_server_crypto.h index 9f2dd829e..439dd112a 100644 --- a/wolfhsm/wh_server_crypto.h +++ b/wolfhsm/wh_server_crypto.h @@ -101,6 +101,14 @@ int wh_Server_HkdfKeyCacheImport(whServerContext* ctx, const uint8_t* keyData, uint8_t* label); #endif /* HAVE_HKDF */ +#ifdef HAVE_CMAC_KDF +/* Store CMAC KDF output into a server key cache with optional metadata */ +int wh_Server_CmacKdfKeyCacheImport(whServerContext* ctx, + const uint8_t* keyData, uint32_t keySize, + whKeyId keyId, whNvmFlags flags, + uint16_t label_len, uint8_t* label); +#endif /* HAVE_CMAC_KDF */ + #endif /* !WOLFHSM_CFG_NO_CRYPTO */ #endif /* !WOLFHSM_WH_SERVER_CRYPTO_H_ */