From fc05260ce69d0388e0879ac584dfb34cb4b82554 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Fri, 3 Jul 2026 16:51:03 +0900 Subject: [PATCH] Route KDF key-exchange and MAC-signature through wolfProvider directly --- include/wolfprovider/internal.h | 1 + src/wp_hkdf.c | 62 +++++++++ src/wp_internal.c | 28 ++++ src/wp_kdf_exch.c | 236 ++++++++++++++++++++++---------- src/wp_mac_sig.c | 161 +++++++++++++++------- src/wp_tls1_prf.c | 39 ++++++ src/wp_wolfprov.c | 2 + test/test_hkdf.c | 178 ++++++++++++++++++++++++ test/test_tls1_prf.c | 83 +++++++++++ test/unit.c | 2 + test/unit.h | 2 + 11 files changed, 668 insertions(+), 126 deletions(-) diff --git a/include/wolfprovider/internal.h b/include/wolfprovider/internal.h index e0660520..04319cdd 100644 --- a/include/wolfprovider/internal.h +++ b/include/wolfprovider/internal.h @@ -217,6 +217,7 @@ int wp_init_cast(int algo); int wolfssl_prov_get_capabilities(void *provctx, const char *capability, OSSL_CALLBACK *cb, void *arg); +unsigned char* wp_octet_dup(const unsigned char* data, size_t len); int wp_name_to_nid(OSSL_LIB_CTX* libCtx, const char* name, const char* propQ); enum wc_HashType wp_name_to_wc_hash_type(OSSL_LIB_CTX* libCtx, const char* name, const char* propQ); diff --git a/src/wp_hkdf.c b/src/wp_hkdf.c index 43a53d5e..6e5e097d 100644 --- a/src/wp_hkdf.c +++ b/src/wp_hkdf.c @@ -193,6 +193,66 @@ static void wp_kdf_hkdf_reset(wp_HkdfCtx* ctx) } } +/** + * Duplicate the HKDF context object. + * + * Deep copies the configured key material so the copy derives identically. + * + * @param [in] src HKDF context object. + * @return New HKDF context object on success. + * @return NULL on failure. + */ +static wp_HkdfCtx* wp_kdf_hkdf_dup(wp_HkdfCtx* src) +{ + wp_HkdfCtx* dst = NULL; + + if (wolfssl_prov_is_running()) { + dst = wp_kdf_hkdf_new(src->provCtx); + } + if (dst != NULL) { + int ok = 1; + + dst->mode = src->mode; + dst->mdType = src->mdType; + dst->mdLen = src->mdLen; + dst->keySz = src->keySz; + dst->saltSz = src->saltSz; + dst->infoSz = src->infoSz; + dst->prefixLen = src->prefixLen; + dst->labelLen = src->labelLen; + dst->dataLen = src->dataLen; + XMEMCPY(dst->info, src->info, src->infoSz); + + if (ok && (src->key != NULL)) { + dst->key = wp_octet_dup(src->key, src->keySz); + ok = dst->key != NULL; + } + if (ok && (src->salt != NULL)) { + dst->salt = wp_octet_dup(src->salt, src->saltSz); + ok = dst->salt != NULL; + } + if (ok && (src->prefix != NULL)) { + dst->prefix = wp_octet_dup(src->prefix, src->prefixLen); + ok = dst->prefix != NULL; + } + if (ok && (src->label != NULL)) { + dst->label = wp_octet_dup(src->label, src->labelLen); + ok = dst->label != NULL; + } + if (ok && (src->data != NULL)) { + dst->data = wp_octet_dup(src->data, src->dataLen); + ok = dst->data != NULL; + } + + if (!ok) { + wp_kdf_hkdf_free(dst); + dst = NULL; + } + } + + return dst; +} + /** * Derive a key using HKDF. * @@ -528,6 +588,7 @@ static const OSSL_PARAM* wp_kdf_hkdf_gettable_ctx_params(wp_HkdfCtx* ctx, /** Dispatch table for HKDF functions implemented using wolfSSL. */ const OSSL_DISPATCH wp_kdf_hkdf_functions[] = { { OSSL_FUNC_KDF_NEWCTX, (DFUNC)wp_kdf_hkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (DFUNC)wp_kdf_hkdf_dup }, { OSSL_FUNC_KDF_FREECTX, (DFUNC)wp_kdf_hkdf_free }, { OSSL_FUNC_KDF_RESET, (DFUNC)wp_kdf_hkdf_reset }, { OSSL_FUNC_KDF_DERIVE, (DFUNC)wp_kdf_hkdf_derive }, @@ -795,6 +856,7 @@ static const OSSL_PARAM* wp_kdf_tls1_3_settable_ctx_params(wp_HkdfCtx* ctx, /** Dispatch table for TLS 1.3 HKDF functions implemented using wolfSSL. */ const OSSL_DISPATCH wp_kdf_tls1_3_kdf_functions[] = { { OSSL_FUNC_KDF_NEWCTX, (DFUNC)wp_kdf_hkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (DFUNC)wp_kdf_hkdf_dup }, { OSSL_FUNC_KDF_FREECTX, (DFUNC)wp_kdf_hkdf_free }, { OSSL_FUNC_KDF_RESET, (DFUNC)wp_kdf_hkdf_reset }, { OSSL_FUNC_KDF_DERIVE, (DFUNC)wp_kdf_tls1_3_derive }, diff --git a/src/wp_internal.c b/src/wp_internal.c index ceb2fce6..a604c929 100644 --- a/src/wp_internal.c +++ b/src/wp_internal.c @@ -469,6 +469,34 @@ int wp_unlock(wolfSSL_Mutex* mutex) } +/** + * Duplicate a configured octet buffer. + * + * OPENSSL_memdup() returns NULL for a zero length, so a 1-byte buffer is + * allocated for the empty case. This preserves the non-NULL, zero-length + * state that consumers may distinguish from a NULL (absent) buffer. + * + * @param [in] data Buffer to duplicate. May be NULL. + * @param [in] len Length of buffer in bytes. + * @return Allocated copy on success. + * @return NULL when data is NULL, or on allocation failure. + */ +unsigned char* wp_octet_dup(const unsigned char* data, size_t len) +{ + unsigned char* ret; + + if (data == NULL) { + ret = NULL; + } + else if (len == 0) { + ret = OPENSSL_zalloc(1); + } + else { + ret = OPENSSL_memdup(data, len); + } + return ret; +} + /** * Convert the string name of an object to an OpenSSL Numeric ID (NID). * diff --git a/src/wp_kdf_exch.c b/src/wp_kdf_exch.c index 6e4c330c..16eded69 100644 --- a/src/wp_kdf_exch.c +++ b/src/wp_kdf_exch.c @@ -23,9 +23,6 @@ #include #include #include -#include -#include -#include #include @@ -33,14 +30,30 @@ /** * Key Derivation Function (KDF) context. * - * Calls through to EVP API. + * Drives the wolfProvider KDF implementation directly through its dispatch + * table so the derivation cannot be satisfied by another provider. */ typedef struct wp_KdfCtx { /** Provider context - useful for duplication. */ WOLFPROV_CTX* provCtx; - /** EVP KDF context object. */ - EVP_KDF_CTX* kdfCtx; + /** wolfProvider KDF dispatch table - useful for duplication. */ + const OSSL_DISPATCH* kdfDisp; + /** wolfProvider KDF implementation context object. */ + void* kctx; + /** Duplicate the wolfProvider KDF context. */ + OSSL_FUNC_kdf_dupctx_fn* dupCtx; + /** Free the wolfProvider KDF context. */ + OSSL_FUNC_kdf_freectx_fn* freeCtx; + /** Derive a key with the wolfProvider KDF. */ + OSSL_FUNC_kdf_derive_fn* derive; + /** Set parameters into the wolfProvider KDF context. */ + OSSL_FUNC_kdf_set_ctx_params_fn* setParams; + /** Get parameters from the wolfProvider KDF context. */ + OSSL_FUNC_kdf_get_ctx_params_fn* getParams; + /** Get the list of gettable parameters from the wolfProvider KDF. */ + OSSL_FUNC_kdf_gettable_ctx_params_fn* gettableParams; + /** Dummy KDF key. */ wp_Kdf* key; /** Name of KDF. */ @@ -52,47 +65,94 @@ typedef struct wp_KdfCtx { static int wp_kdf_set_ctx_params(wp_KdfCtx* ctx, const OSSL_PARAM params[]); +/** + * Resolve the wolfProvider KDF implementation from its dispatch table. + * + * Creates the backing KDF context so the derivation is always performed by + * wolfProvider, never delegated to another provider via an EVP fetch. + * + * @param [in, out] ctx KDF key exchange context object. + * @param [in] kdfDisp wolfProvider KDF dispatch table. + * @return 1 on success. + * @return 0 when a required function is missing or context creation fails. + */ +static int wp_kdf_ctx_load(wp_KdfCtx* ctx, const OSSL_DISPATCH* kdfDisp) +{ + int ok = 1; + OSSL_FUNC_kdf_newctx_fn* newCtx = NULL; + const OSSL_DISPATCH* d; + + for (d = kdfDisp; d->function_id != 0; d++) { + switch (d->function_id) { + case OSSL_FUNC_KDF_NEWCTX: + newCtx = OSSL_FUNC_kdf_newctx(d); + break; + case OSSL_FUNC_KDF_DUPCTX: + ctx->dupCtx = OSSL_FUNC_kdf_dupctx(d); + break; + case OSSL_FUNC_KDF_FREECTX: + ctx->freeCtx = OSSL_FUNC_kdf_freectx(d); + break; + case OSSL_FUNC_KDF_DERIVE: + ctx->derive = OSSL_FUNC_kdf_derive(d); + break; + case OSSL_FUNC_KDF_SET_CTX_PARAMS: + ctx->setParams = OSSL_FUNC_kdf_set_ctx_params(d); + break; + case OSSL_FUNC_KDF_GET_CTX_PARAMS: + ctx->getParams = OSSL_FUNC_kdf_get_ctx_params(d); + break; + case OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS: + ctx->gettableParams = OSSL_FUNC_kdf_gettable_ctx_params(d); + break; + default: + break; + } + } + + if ((newCtx == NULL) || (ctx->dupCtx == NULL) || (ctx->freeCtx == NULL) || + (ctx->derive == NULL) || (ctx->setParams == NULL) || + (ctx->getParams == NULL)) { + ok = 0; + } + if (ok) { + ctx->kdfDisp = kdfDisp; + ctx->kctx = newCtx(ctx->provCtx); + if (ctx->kctx == NULL) { + ok = 0; + } + } + + return ok; +} + /** * Create a new KDF key exchange context object. * * @param [in] provCtx Provider context. * @param [in] name Name of KDF. + * @param [in] kdfDisp wolfProvider KDF dispatch table for name. * @return KDF key exchange object on success. * @return NULL on failure. */ -static wp_KdfCtx* wp_kdf_ctx_new(WOLFPROV_CTX* provCtx, const char* name) +static wp_KdfCtx* wp_kdf_ctx_new(WOLFPROV_CTX* provCtx, const char* name, + const OSSL_DISPATCH* kdfDisp) { wp_KdfCtx* ctx = NULL; - EVP_KDF* kdf = NULL; if (wolfssl_prov_is_running()) { ctx = OPENSSL_zalloc(sizeof(*ctx)); - } - if (ctx != NULL) { - int ok = 1; - - kdf = EVP_KDF_fetch(provCtx->libCtx, name, NULL); - if (kdf == NULL) { - ok = 0; - } - if (ok) { - ctx->kdfCtx = EVP_KDF_CTX_new(kdf); - if (ctx->kdfCtx == NULL) { - ok = 0; - } - } - if (ok) { - ctx->provCtx = provCtx; - ctx->name = name; - } - - if (!ok) { + } + if (ctx != NULL) { + ctx->provCtx = provCtx; + ctx->name = name; + /* On load failure kctx is always NULL, so there is nothing to free. */ + if (!wp_kdf_ctx_load(ctx, kdfDisp)) { OPENSSL_free(ctx); ctx = NULL; } } - EVP_KDF_free(kdf); return ctx; } @@ -105,7 +165,9 @@ static void wp_kdf_ctx_free(wp_KdfCtx* ctx) { if (ctx != NULL) { wp_kdf_free(ctx->key); - EVP_KDF_CTX_free(ctx->kdfCtx); + if ((ctx->freeCtx != NULL) && (ctx->kctx != NULL)) { + ctx->freeCtx(ctx->kctx); + } OPENSSL_free(ctx); } } @@ -122,12 +184,20 @@ static wp_KdfCtx* wp_kdf_ctx_dup(wp_KdfCtx* src) wp_KdfCtx* dst = NULL; if (wolfssl_prov_is_running()) { - dst = wp_kdf_ctx_new(src->provCtx, src->name); + dst = wp_kdf_ctx_new(src->provCtx, src->name, src->kdfDisp); } if (dst != NULL) { int ok = 1; - if ((src->key != NULL) && (!wp_kdf_up_ref(src->key))) { + /* Replace the fresh KDF context with a deep copy of the source's so + * the configured derivation state is preserved. dupCtx is required by + * wp_kdf_ctx_load, so it is always available here. */ + dst->freeCtx(dst->kctx); + dst->kctx = src->dupCtx(src->kctx); + if (dst->kctx == NULL) { + ok = 0; + } + if (ok && (src->key != NULL) && (!wp_kdf_up_ref(src->key))) { ok = 0; } if (ok) { @@ -201,9 +271,19 @@ static int wp_kdf_derive(wp_KdfCtx* ctx, unsigned char* secret, size_t* secLen, } if (ok && (secret == NULL)) { - *secLen = EVP_KDF_CTX_get_kdf_size(ctx->kdfCtx); + OSSL_PARAM params[2]; + size_t sz = 0; + + params[0] = OSSL_PARAM_construct_size_t(OSSL_KDF_PARAM_SIZE, &sz); + params[1] = OSSL_PARAM_construct_end(); + if (!ctx->getParams(ctx->kctx, params)) { + ok = 0; + } + else { + *secLen = sz; + } } - else if (ok && !EVP_KDF_derive(ctx->kdfCtx, secret, secSize, NULL)) { + else if (ok && !ctx->derive(ctx->kctx, secret, secSize, NULL)) { ok = 0; } @@ -221,7 +301,7 @@ static int wp_kdf_derive(wp_KdfCtx* ctx, unsigned char* secret, size_t* secLen, */ static int wp_kdf_set_ctx_params(wp_KdfCtx* ctx, const OSSL_PARAM params[]) { - return EVP_KDF_CTX_set_params(ctx->kdfCtx, params); + return ctx->setParams(ctx->kctx, params); } /** @@ -241,7 +321,7 @@ static int wp_kdf_get_ctx_params(wp_KdfCtx* ctx, OSSL_PARAM params[]) if (!wolfssl_prov_is_running()) { ok = 0; } - if (ok && !EVP_KDF_CTX_get_params(ctx->kdfCtx, params)) { + if (ok && !ctx->getParams(ctx->kctx, params)) { ok = 0; } @@ -262,11 +342,11 @@ static const OSSL_PARAM* wp_kdf_gettable_ctx_params(wp_KdfCtx* ctx, { const OSSL_PARAM* params = NULL; - (void)provCtx; (void)kdfName; - if (wolfssl_prov_is_running() && ctx != NULL && ctx->kdfCtx != NULL) { - params = EVP_KDF_CTX_gettable_params(ctx->kdfCtx); + if (wolfssl_prov_is_running() && (ctx != NULL) && (ctx->kctx != NULL) && + (ctx->gettableParams != NULL)) { + params = ctx->gettableParams(ctx->kctx, provCtx); } return params; @@ -297,28 +377,6 @@ static const OSSL_PARAM* wp_hkdf_settable_ctx_params(wp_KdfCtx* ctx, return settable_ctx_params; } -/** - * Return an array of supported settable parameters for the HKDF ke context. - * - * @param [in] ctx ECDH key exchange context object. Unused. - * @param [in] provCtx Provider context object. Unused. - * @return Array of parameters with data type. - */ -static const OSSL_PARAM* wp_tls1_prf_settable_ctx_params(wp_KdfCtx* ctx, - WOLFPROV_CTX* provCtx) -{ - (void)ctx; - (void)provCtx; - static const OSSL_PARAM settable_ctx_params[] = { - OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), - OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), - OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0), - OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SEED, NULL, 0), - OSSL_PARAM_END - }; - return settable_ctx_params; -} - /** * Return an array of supported gettable parameters for the HKDF ke context. * @@ -332,19 +390,6 @@ static const OSSL_PARAM* wp_hkdf_gettable_ctx_params(wp_KdfCtx* ctx, return wp_kdf_gettable_ctx_params(ctx, provCtx, "HKDF"); } -/** - * Return an array of supported gettable parameters for the TLS1-PRF ke context. - * - * @param [in] ctx KDF key exchange context object. Unused. - * @param [in] provCtx Provider context object. - * @return Array of parameters with data type. - */ -static const OSSL_PARAM* wp_tls1_prf_gettable_ctx_params(wp_KdfCtx* ctx, - WOLFPROV_CTX* provCtx) -{ - return wp_kdf_gettable_ctx_params(ctx, provCtx, "TLS1-PRF"); -} - /* * HKDF */ @@ -358,7 +403,7 @@ static const OSSL_PARAM* wp_tls1_prf_gettable_ctx_params(wp_KdfCtx* ctx, */ static wp_KdfCtx* wp_hkdf_ctx_new(WOLFPROV_CTX* provCtx) { - return wp_kdf_ctx_new(provCtx, "HKDF"); + return wp_kdf_ctx_new(provCtx, "HKDF", wp_kdf_hkdf_functions); } /** Dispatch table for HKDF key exchange. */ @@ -381,6 +426,43 @@ const OSSL_DISPATCH wp_hkdf_keyexch_functions[] = { * TLS1 PRF */ +#ifdef WP_HAVE_TLS1_PRF + +/** + * Return an array of supported settable parameters for the TLS1-PRF ke context. + * + * @param [in] ctx ECDH key exchange context object. Unused. + * @param [in] provCtx Provider context object. Unused. + * @return Array of parameters with data type. + */ +static const OSSL_PARAM* wp_tls1_prf_settable_ctx_params(wp_KdfCtx* ctx, + WOLFPROV_CTX* provCtx) +{ + (void)ctx; + (void)provCtx; + static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SEED, NULL, 0), + OSSL_PARAM_END + }; + return settable_ctx_params; +} + +/** + * Return an array of supported gettable parameters for the TLS1-PRF ke context. + * + * @param [in] ctx KDF key exchange context object. Unused. + * @param [in] provCtx Provider context object. + * @return Array of parameters with data type. + */ +static const OSSL_PARAM* wp_tls1_prf_gettable_ctx_params(wp_KdfCtx* ctx, + WOLFPROV_CTX* provCtx) +{ + return wp_kdf_gettable_ctx_params(ctx, provCtx, "TLS1-PRF"); +} + /** * Create a new TLS1 PRF key exchange context object. * @@ -390,7 +472,7 @@ const OSSL_DISPATCH wp_hkdf_keyexch_functions[] = { */ static wp_KdfCtx* wp_tls1_prf_ctx_new(WOLFPROV_CTX* provCtx) { - return wp_kdf_ctx_new(provCtx, "TLS1-PRF"); + return wp_kdf_ctx_new(provCtx, "TLS1-PRF", wp_kdf_tls1_prf_functions); } /** Dispatch table for TLS1 PRF key exchange. */ @@ -409,3 +491,5 @@ const OSSL_DISPATCH wp_tls1_prf_keyexch_functions[] = { { 0, NULL } }; +#endif /* WP_HAVE_TLS1_PRF */ + diff --git a/src/wp_mac_sig.c b/src/wp_mac_sig.c index 7565a97f..21c1a142 100644 --- a/src/wp_mac_sig.c +++ b/src/wp_mac_sig.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -38,36 +37,113 @@ typedef struct wp_MacSigCtx { /* wolfProvider context object. */ WOLFPROV_CTX *provCtx; - /* Library context object. */ - OSSL_LIB_CTX *libCtx; /* MAC key. */ wp_Mac* mac; - /* wolfProvider MAC object. */ - EVP_MAC_CTX* macCtx; + + /* wolfProvider MAC dispatch table - useful for duplication. */ + const OSSL_DISPATCH* macDisp; + /* wolfProvider MAC implementation context object. */ + void* macCtx; + /* Free the wolfProvider MAC context. */ + OSSL_FUNC_mac_freectx_fn* freeCtx; + /* Duplicate the wolfProvider MAC context. */ + OSSL_FUNC_mac_dupctx_fn* dupCtx; + /* Initialize the wolfProvider MAC context. */ + OSSL_FUNC_mac_init_fn* init; + /* Update the wolfProvider MAC context with data. */ + OSSL_FUNC_mac_update_fn* update; + /* Finalize the wolfProvider MAC and output the result. */ + OSSL_FUNC_mac_final_fn* final; + /* Set parameters into the wolfProvider MAC context. */ + OSSL_FUNC_mac_set_ctx_params_fn* setParams; + /* MAC name */ char name[WP_MAX_MAC_NAME_SIZE]; /* MAC type */ int type; - - /* Property query string. */ - char* propQuery; } wp_MacSigCtx; /* Prototype for wp_mac_signverify_init() to use. */ static int wp_mac_set_ctx_params(wp_MacSigCtx *ctx, const OSSL_PARAM params[]); +/** + * Resolve the wolfProvider MAC implementation from its dispatch table. + * + * Creates the backing MAC context so the MAC is always computed by + * wolfProvider. + * + * @param [in, out] ctx MAC signature context object. + * @param [in] macDisp wolfProvider MAC dispatch table. + * @return 1 on success. + * @return 0 when a required function is missing or context creation fails. + */ +static int wp_mac_ctx_load(wp_MacSigCtx* ctx, const OSSL_DISPATCH* macDisp) +{ + int ok = 1; + OSSL_FUNC_mac_newctx_fn* newCtx = NULL; + const OSSL_DISPATCH* d; + + for (d = macDisp; d->function_id != 0; d++) { + switch (d->function_id) { + case OSSL_FUNC_MAC_NEWCTX: + newCtx = OSSL_FUNC_mac_newctx(d); + break; + case OSSL_FUNC_MAC_FREECTX: + ctx->freeCtx = OSSL_FUNC_mac_freectx(d); + break; + case OSSL_FUNC_MAC_DUPCTX: + ctx->dupCtx = OSSL_FUNC_mac_dupctx(d); + break; + case OSSL_FUNC_MAC_INIT: + ctx->init = OSSL_FUNC_mac_init(d); + break; + case OSSL_FUNC_MAC_UPDATE: + ctx->update = OSSL_FUNC_mac_update(d); + break; + case OSSL_FUNC_MAC_FINAL: + ctx->final = OSSL_FUNC_mac_final(d); + break; + case OSSL_FUNC_MAC_SET_CTX_PARAMS: + ctx->setParams = OSSL_FUNC_mac_set_ctx_params(d); + break; + default: + break; + } + } + + if ((newCtx == NULL) || (ctx->freeCtx == NULL) || (ctx->dupCtx == NULL) || + (ctx->init == NULL) || (ctx->update == NULL) || + (ctx->final == NULL) || (ctx->setParams == NULL)) { + ok = 0; + } + if (ok) { + ctx->macDisp = macDisp; + ctx->macCtx = newCtx(ctx->provCtx); + if (ctx->macCtx == NULL) { + ok = 0; + } + } + + return ok; +} + /** * Create a new MAC signature context object. * + * The MAC is always wolfProvider's own implementation (bound via macDisp), so + * a caller property query does not affect selection and is not retained. + * * @param [in] provCtx wolfProvider context object. - * @param [in] propQuery Property query. + * @param [in] macName Name of MAC algorithm. + * @param [in] type MAC key type. + * @param [in] macDisp wolfProvider MAC dispatch table for macName. * @return NULL on failure. * @return MAC signature context object on success. */ static wp_MacSigCtx* wp_mac_ctx_new(WOLFPROV_CTX* provCtx, - const char* propQuery, const char* macName, int type) + const char* macName, int type, const OSSL_DISPATCH* macDisp) { wp_MacSigCtx* ctx = NULL; @@ -76,44 +152,22 @@ static wp_MacSigCtx* wp_mac_ctx_new(WOLFPROV_CTX* provCtx, } if (ctx != NULL) { int ok = 1; - char* p = NULL; - EVP_MAC* mac = NULL; - - if (propQuery != NULL) { - p = OPENSSL_strdup(propQuery); - if (p == NULL) { - OPENSSL_free(ctx); - ctx = NULL; - ok = 0; - } - } - if (ok) { - mac = EVP_MAC_fetch(provCtx->libCtx, macName, propQuery); - if (mac == NULL) { - ok = 0; - } - } - if (ok) { - ctx->macCtx = EVP_MAC_CTX_new(mac); - if (ctx->macCtx == NULL) { - ok = 0; - } + + ctx->provCtx = provCtx; + if (!wp_mac_ctx_load(ctx, macDisp)) { + ok = 0; } if (ok) { - ctx->propQuery = p; - ctx->provCtx = provCtx; - ctx->libCtx = provCtx->libCtx; XSTRNCPY(ctx->name, macName, WP_MAX_MAC_NAME_SIZE); ctx->name[WP_MAX_MAC_NAME_SIZE - 1] = '\0'; ctx->type = type; } + /* On load failure macCtx is always NULL, so there is nothing to free. */ if (!ok) { - OPENSSL_free(p); OPENSSL_free(ctx); ctx = NULL; } - EVP_MAC_free(mac); } return ctx; @@ -127,9 +181,10 @@ static wp_MacSigCtx* wp_mac_ctx_new(WOLFPROV_CTX* provCtx, static void wp_mac_ctx_free(wp_MacSigCtx* ctx) { if (ctx != NULL) { - EVP_MAC_CTX_free(ctx->macCtx); + if ((ctx->freeCtx != NULL) && (ctx->macCtx != NULL)) { + ctx->freeCtx(ctx->macCtx); + } wp_mac_free(ctx->mac); - OPENSSL_free(ctx->propQuery); OPENSSL_free(ctx); } } @@ -149,14 +204,14 @@ static wp_MacSigCtx* wp_mac_ctx_dup(wp_MacSigCtx* srcCtx) if (wolfssl_prov_is_running()) { int ok = 1; - dstCtx = wp_mac_ctx_new(srcCtx->provCtx, srcCtx->propQuery, - srcCtx->name, srcCtx->type); + dstCtx = wp_mac_ctx_new(srcCtx->provCtx, srcCtx->name, srcCtx->type, + srcCtx->macDisp); if (dstCtx == NULL) { ok = 0; } if (ok) { - EVP_MAC_CTX_free(dstCtx->macCtx); - dstCtx->macCtx = EVP_MAC_CTX_dup(srcCtx->macCtx); + dstCtx->freeCtx(dstCtx->macCtx); + dstCtx->macCtx = dstCtx->dupCtx(srcCtx->macCtx); if (dstCtx->macCtx == NULL) { ok = 0; } @@ -217,7 +272,9 @@ static int wp_mac_digest_sign_init(wp_MacSigCtx *ctx, const char *mdName, } } if (ok) { - EVP_MAC_CTX_set_params(ctx->macCtx, params); + if (!ctx->setParams(ctx->macCtx, params)) { + ok = 0; + } } if (ok && (ctx->type == WP_MAC_TYPE_CMAC)) { cipherName = wp_mac_get_ciphername(ctx->mac); @@ -253,7 +310,7 @@ static int wp_mac_digest_sign_init(wp_MacSigCtx *ctx, const char *mdName, } if (ok) { lParams[lParamSz++] = OSSL_PARAM_construct_end(); - if (!EVP_MAC_init(ctx->macCtx, priv, privLen, lParams)) { + if (!ctx->init(ctx->macCtx, priv, privLen, lParams)) { ok = 0; } } @@ -278,7 +335,7 @@ static int wp_mac_digest_sign_update(wp_MacSigCtx *ctx, WOLFPROV_ENTER(WP_LOG_COMP_MAC, "wp_mac_digest_sign_update"); - if (!EVP_MAC_update(ctx->macCtx, data, dataLen)) { + if (!ctx->update(ctx->macCtx, data, dataLen)) { ok = 0; } @@ -312,7 +369,7 @@ static int wp_mac_digest_sign_final(wp_MacSigCtx *ctx, unsigned char *sig, if (ok && (sigSize == MAX_SIZE_T) && (ctx->type == WP_MAC_TYPE_CMAC)) { sigSize = AES_BLOCK_SIZE; } - if (ok && (!EVP_MAC_final(ctx->macCtx, sig, sigLen, sigSize))) { + if (ok && (!ctx->final(ctx->macCtx, sig, sigLen, sigSize))) { ok = 0; } @@ -330,7 +387,7 @@ static int wp_mac_digest_sign_final(wp_MacSigCtx *ctx, unsigned char *sig, */ static int wp_mac_set_ctx_params(wp_MacSigCtx *ctx, const OSSL_PARAM params[]) { - return EVP_MAC_CTX_set_params(ctx->macCtx, params); + return ctx->setParams(ctx->macCtx, params); } /** @@ -366,7 +423,9 @@ static const OSSL_PARAM *wp_mac_settable_ctx_params(wp_MacSigCtx *ctx, static wp_MacSigCtx* wp_hmac_ctx_new(WOLFPROV_CTX* provCtx, const char* propQuery) { - return wp_mac_ctx_new(provCtx, propQuery, WP_NAMES_HMAC, WP_MAC_TYPE_HMAC); + (void)propQuery; + return wp_mac_ctx_new(provCtx, WP_NAMES_HMAC, WP_MAC_TYPE_HMAC, + wp_hmac_functions); } /** Dspatch table for HMAC signing. */ @@ -395,7 +454,9 @@ const OSSL_DISPATCH wp_hmac_signature_functions[] = { static wp_MacSigCtx* wp_cmac_ctx_new(WOLFPROV_CTX* provCtx, const char* propQuery) { - return wp_mac_ctx_new(provCtx, propQuery, WP_NAMES_CMAC, WP_MAC_TYPE_CMAC); + (void)propQuery; + return wp_mac_ctx_new(provCtx, WP_NAMES_CMAC, WP_MAC_TYPE_CMAC, + wp_cmac_functions); } /** Dspatch table for HMAC signing. */ diff --git a/src/wp_tls1_prf.c b/src/wp_tls1_prf.c index 6d99db77..ebe388c2 100644 --- a/src/wp_tls1_prf.c +++ b/src/wp_tls1_prf.c @@ -131,6 +131,44 @@ static void wp_kdf_tls1_prf_reset(wp_Tls1Prf_Ctx* ctx) } } +/** + * Duplicate the TLS1 PRF context object. + * + * Deep copies the configured secret so the copy derives identically. + * + * @param [in] src TLS1 PRF context object. + * @return New TLS1 PRF context object on success. + * @return NULL on failure. + */ +static wp_Tls1Prf_Ctx* wp_kdf_tls1_prf_dup(wp_Tls1Prf_Ctx* src) +{ + wp_Tls1Prf_Ctx* dst = NULL; + + if (wolfssl_prov_is_running()) { + dst = wp_kdf_tls1_prf_new(src->provCtx); + } + if (dst != NULL) { + int ok = 1; + + dst->mdType = src->mdType; + dst->secretSz = src->secretSz; + dst->seedSz = src->seedSz; + XMEMCPY(dst->seed, src->seed, src->seedSz); + + if (ok && (src->secret != NULL)) { + dst->secret = wp_octet_dup(src->secret, src->secretSz); + ok = dst->secret != NULL; + } + + if (!ok) { + wp_kdf_tls1_prf_free(dst); + dst = NULL; + } + } + + return dst; +} + /** * Derive key using TLS1 PRF algorithm. * @@ -348,6 +386,7 @@ static const OSSL_PARAM* wp_kdf_tls1_prf_gettable_ctx_params( /** Dispatch table for TLS1 PRF functions implemented using wolfSSL. */ const OSSL_DISPATCH wp_kdf_tls1_prf_functions[] = { { OSSL_FUNC_KDF_NEWCTX, (DFUNC)wp_kdf_tls1_prf_new }, + { OSSL_FUNC_KDF_DUPCTX, (DFUNC)wp_kdf_tls1_prf_dup }, { OSSL_FUNC_KDF_FREECTX, (DFUNC)wp_kdf_tls1_prf_free }, { OSSL_FUNC_KDF_RESET, (DFUNC)wp_kdf_tls1_prf_reset }, { OSSL_FUNC_KDF_DERIVE, (DFUNC)wp_kdf_tls1_prf_derive }, diff --git a/src/wp_wolfprov.c b/src/wp_wolfprov.c index d92618ef..36bca573 100644 --- a/src/wp_wolfprov.c +++ b/src/wp_wolfprov.c @@ -711,8 +711,10 @@ static const OSSL_ALGORITHM wolfprov_keyexch[] = { { WP_NAMES_HKDF, WOLFPROV_PROPERTIES, wp_hkdf_keyexch_functions, "" }, +#ifdef WP_HAVE_TLS1_PRF { WP_NAMES_TLS1_PRF, WOLFPROV_PROPERTIES, wp_tls1_prf_keyexch_functions, "" }, +#endif { NULL, NULL, NULL, NULL } }; diff --git a/test/test_hkdf.c b/test/test_hkdf.c index d8d940ff..8ff14872 100644 --- a/test/test_hkdf.c +++ b/test/test_hkdf.c @@ -613,6 +613,184 @@ static int test_hkdf_extract_only_bad_len(OSSL_LIB_CTX *libCtx) #define NUM_MODES 3 +#ifdef WP_HAVE_SHA256 +/* Configure an HKDF keyexch (saltLen-byte salt), dup it, and confirm both + * derive the same key so the dup preserved config. saltLen == 0 exercises + * the empty-salt boundary. */ +static int test_hkdf_dup_calc(int saltLen) +{ + int err = 0; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY_CTX *dupCtx = NULL; + unsigned char inKey[32]; + unsigned char salt[16]; + unsigned char info[8]; + unsigned char keyA[42]; + unsigned char keyB[42]; + size_t lenA = sizeof(keyA); + size_t lenB = sizeof(keyB); + + memset(inKey, 0x11, sizeof(inKey)); + memset(salt, 0x22, sizeof(salt)); + memset(info, 0x33, sizeof(info)); + + ctx = EVP_PKEY_CTX_new_from_name(wpLibCtx, "HKDF", NULL); + if (ctx == NULL) { + err = 1; + } + if (err == 0 && EVP_PKEY_derive_init(ctx) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_hkdf_mode(ctx, + EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha256()) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_set1_hkdf_key(ctx, inKey, sizeof(inKey)) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, saltLen) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_add1_hkdf_info(ctx, info, sizeof(info)) != 1) { + err = 1; + } + + /* Duplicate after configuring - the path that must deep-copy state. */ + if (err == 0) { + dupCtx = EVP_PKEY_CTX_dup(ctx); + if (dupCtx == NULL) { + PRINT_MSG("Failed to duplicate HKDF context"); + err = 1; + } + } + + if (err == 0 && EVP_PKEY_derive(ctx, keyA, &lenA) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_derive(dupCtx, keyB, &lenB) != 1) { + PRINT_MSG("Duplicated HKDF context failed to derive"); + err = 1; + } + if (err == 0 && ((lenA != lenB) || (memcmp(keyA, keyB, lenA) != 0))) { + PRINT_MSG("Duplicated HKDF context derived a different key"); + err = 1; + } + + EVP_PKEY_CTX_free(dupCtx); + EVP_PKEY_CTX_free(ctx); + return err; +} + +/* Configure a TLS 1.3 KDF context (extract uses salt, expand uses data), dup + * it, and confirm both derive the same key - exercises the salt/prefix/label/ + * data copy branches and the TLS13-KDF DUPCTX entry. */ +static int test_tls13_kdf_dup_calc(const char* mode, int extract) +{ + int err = 0; + EVP_KDF *kdf = NULL; + EVP_KDF_CTX *ctx = NULL; + EVP_KDF_CTX *dupCtx = NULL; + unsigned char secret[32]; + unsigned char octet[32]; + unsigned char keyA[32]; + unsigned char keyB[32]; + unsigned char prefix[] = "tls13 "; + unsigned char label[] = "derived"; + char digest[] = "SHA256"; + OSSL_PARAM params[7]; + OSSL_PARAM *p = params; + + memset(secret, 0x11, sizeof(secret)); + memset(octet, 0x33, sizeof(octet)); + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MODE, (char*)mode, 0); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, digest, 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, secret, + sizeof(secret)); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PREFIX, prefix, + sizeof(prefix) - 1); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_LABEL, label, + sizeof(label) - 1); + if (extract) { + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, octet, + sizeof(octet)); + } + else { + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_DATA, octet, + sizeof(octet)); + } + *p = OSSL_PARAM_construct_end(); + + kdf = EVP_KDF_fetch(wpLibCtx, "TLS13-KDF", NULL); + if (kdf == NULL) { + err = 1; + } + if (err == 0) { + ctx = EVP_KDF_CTX_new(kdf); + if (ctx == NULL) { + err = 1; + } + } + /* Set params before dup so the state is copied, not applied post-dup. */ + if (err == 0 && EVP_KDF_CTX_set_params(ctx, params) != 1) { + err = 1; + } + if (err == 0) { + dupCtx = EVP_KDF_CTX_dup(ctx); + if (dupCtx == NULL) { + PRINT_MSG("Failed to duplicate TLS13-KDF context"); + err = 1; + } + } + + if (err == 0 && EVP_KDF_derive(ctx, keyA, sizeof(keyA), NULL) != 1) { + err = 1; + } + if (err == 0 && EVP_KDF_derive(dupCtx, keyB, sizeof(keyB), NULL) != 1) { + PRINT_MSG("Duplicated TLS13-KDF context failed to derive"); + err = 1; + } + if (err == 0 && (memcmp(keyA, keyB, sizeof(keyA)) != 0)) { + PRINT_MSG("Duplicated TLS13-KDF context derived a different key"); + err = 1; + } + + EVP_KDF_CTX_free(dupCtx); + EVP_KDF_CTX_free(ctx); + EVP_KDF_free(kdf); + return err; +} +#endif /* WP_HAVE_SHA256 */ + +int test_hkdf_dup(void *data) +{ + int err = 0; + + (void)data; + + PRINT_MSG("Testing HKDF context dup preserves configuration"); +#ifdef WP_HAVE_SHA256 + err = test_hkdf_dup_calc(16); + if (err == 0) { + PRINT_MSG("Testing HKDF context dup with empty salt"); + err = test_hkdf_dup_calc(0); + } + if (err == 0) { + PRINT_MSG("Testing TLS13-KDF dup (expand) preserves prefix/label/data"); + err = test_tls13_kdf_dup_calc("EXPAND_ONLY", 0); + } + if (err == 0) { + PRINT_MSG("Testing TLS13-KDF dup (extract) preserves salt/prefix/label"); + err = test_tls13_kdf_dup_calc("EXTRACT_ONLY", 1); + } +#endif + + return err; +} + int test_hkdf(void *data) { int err = 0; diff --git a/test/test_tls1_prf.c b/test/test_tls1_prf.c index 3bdc2896..22e7afef 100644 --- a/test/test_tls1_prf.c +++ b/test/test_tls1_prf.c @@ -352,6 +352,89 @@ static int test_tls1_prf_fail(void) return err; } +#ifdef WP_HAVE_SHA256 +/* Configure a TLS1-PRF keyexch (secretLen-byte secret), dup it, and confirm + * both derive the same key so the dup preserved config. secretLen == 0 + * exercises the empty-secret boundary. */ +static int test_tls1_prf_dup_calc(int secretLen) +{ + int err = 0; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY_CTX *dupCtx = NULL; + unsigned char secret[32]; + unsigned char seed[16]; + unsigned char keyA[48]; + unsigned char keyB[48]; + size_t lenA = sizeof(keyA); + size_t lenB = sizeof(keyB); + + memset(secret, 0x11, sizeof(secret)); + memset(seed, 0x22, sizeof(seed)); + + ctx = EVP_PKEY_CTX_new_from_name(wpLibCtx, "TLS1-PRF", NULL); + if (ctx == NULL) { + err = 1; + } + if (err == 0 && EVP_PKEY_derive_init(ctx) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_set_tls1_prf_md(ctx, EVP_sha256()) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_set1_tls1_prf_secret(ctx, secret, + secretLen) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_CTX_add1_tls1_prf_seed(ctx, seed, + sizeof(seed)) != 1) { + err = 1; + } + + /* Duplicate after configuring - the path that must deep-copy state. */ + if (err == 0) { + dupCtx = EVP_PKEY_CTX_dup(ctx); + if (dupCtx == NULL) { + PRINT_MSG("Failed to duplicate TLS1-PRF context"); + err = 1; + } + } + + if (err == 0 && EVP_PKEY_derive(ctx, keyA, &lenA) != 1) { + err = 1; + } + if (err == 0 && EVP_PKEY_derive(dupCtx, keyB, &lenB) != 1) { + PRINT_MSG("Duplicated TLS1-PRF context failed to derive"); + err = 1; + } + if (err == 0 && ((lenA != lenB) || (memcmp(keyA, keyB, lenA) != 0))) { + PRINT_MSG("Duplicated TLS1-PRF context derived a different key"); + err = 1; + } + + EVP_PKEY_CTX_free(dupCtx); + EVP_PKEY_CTX_free(ctx); + return err; +} +#endif /* WP_HAVE_SHA256 */ + +int test_tls1_prf_dup(void *data) +{ + int err = 0; + + (void)data; + + PRINT_MSG("Testing TLS1-PRF context dup preserves configuration"); +#ifdef WP_HAVE_SHA256 + err = test_tls1_prf_dup_calc(32); + if (err == 0) { + PRINT_MSG("Testing TLS1-PRF context dup with empty secret"); + err = test_tls1_prf_dup_calc(0); + } +#endif + + return err; +} + int test_tls1_prf(void *data) { int err = 0; diff --git a/test/unit.c b/test/unit.c index a4035e10..dab16521 100644 --- a/test/unit.c +++ b/test/unit.c @@ -221,9 +221,11 @@ TEST_CASE test_case[] = { #endif #ifdef WP_HAVE_TLS1_PRF TEST_DECL(test_tls1_prf, NULL), + TEST_DECL(test_tls1_prf_dup, NULL), #endif #ifdef WP_HAVE_HKDF TEST_DECL(test_hkdf, NULL), + TEST_DECL(test_hkdf_dup, NULL), #endif #ifdef WP_HAVE_KBKDF TEST_DECL(test_kbkdf, NULL), diff --git a/test/unit.h b/test/unit.h index 6d6f780d..4dcd0e61 100644 --- a/test/unit.h +++ b/test/unit.h @@ -140,10 +140,12 @@ int test_gmac_dup(void *data); #ifdef WP_HAVE_TLS1_PRF int test_tls1_prf(void *data); +int test_tls1_prf_dup(void *data); #endif #ifdef WP_HAVE_HKDF int test_hkdf(void *data); +int test_hkdf_dup(void *data); #endif #ifdef WP_HAVE_KBKDF