diff --git a/include/wolfprovider/alg_funcs.h b/include/wolfprovider/alg_funcs.h
index 3133044a..c709ebe1 100644
--- a/include/wolfprovider/alg_funcs.h
+++ b/include/wolfprovider/alg_funcs.h
@@ -146,6 +146,7 @@ typedef void (*DFUNC)(void);
#define WP_NAMES_PKCS12KDF "PKCS12KDF"
#define WP_NAMES_TLS1_3_KDF "TLS13-KDF"
#define WP_NAMES_TLS1_PRF "TLS1-PRF"
+#define WP_NAMES_KRB5KDF "KRB5KDF"
/* Signature names. */
#define WP_NAMES_RSA "RSA:rsaEncryption:1.2.840.113549.1.1.1"
@@ -309,6 +310,7 @@ extern const OSSL_DISPATCH wp_kdf_pbkdf2_functions[];
extern const OSSL_DISPATCH wp_kdf_pkcs12_functions[];
extern const OSSL_DISPATCH wp_kdf_tls1_3_kdf_functions[];
extern const OSSL_DISPATCH wp_kdf_tls1_prf_functions[];
+extern const OSSL_DISPATCH wp_kdf_krb5kdf_functions[];
/* Signature implementations. */
extern const OSSL_DISPATCH wp_rsa_signature_functions[];
diff --git a/include/wolfprovider/settings.h b/include/wolfprovider/settings.h
index b5ab88c7..9b5d3894 100644
--- a/include/wolfprovider/settings.h
+++ b/include/wolfprovider/settings.h
@@ -82,6 +82,7 @@
#ifndef NO_AES_CBC
#define WP_HAVE_AESCBC
#define WP_HAVE_AESCTS
+ #define WP_HAVE_KRB5KDF
#endif
#ifndef NO_DES3
#define WP_HAVE_DES3CBC
diff --git a/src/include.am b/src/include.am
index e4729015..49d1dd99 100644
--- a/src/include.am
+++ b/src/include.am
@@ -21,6 +21,7 @@ libwolfprov_la_SOURCES += src/wp_tls1_prf.c
libwolfprov_la_SOURCES += src/wp_kdf_kmgmt.c
libwolfprov_la_SOURCES += src/wp_kdf_exch.c
libwolfprov_la_SOURCES += src/wp_pbkdf2.c
+libwolfprov_la_SOURCES += src/wp_krb5kdf.c
libwolfprov_la_SOURCES += src/wp_rsa_kmgmt.c
libwolfprov_la_SOURCES += src/wp_rsa_sig.c
libwolfprov_la_SOURCES += src/wp_rsa_asym.c
diff --git a/src/wp_krb5kdf.c b/src/wp_krb5kdf.c
new file mode 100644
index 00000000..68ab8a75
--- /dev/null
+++ b/src/wp_krb5kdf.c
@@ -0,0 +1,473 @@
+/* wp_krb5kdf.c
+ *
+ * Copyright (C) 2006-2024 wolfSSL Inc.
+ *
+ * This file is part of wolfProvider.
+ *
+ * wolfProvider 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.
+ *
+ * wolfProvider 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 wolfProvider. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+/** Base set of parameters settable against context for KRB5KDF. */
+#define WP_KRB5KDF_BASE_SETTABLES \
+ 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_KEY, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_CONSTANT, NULL, 0)
+
+/**
+ * The KRB5KDF context structure.
+ */
+typedef struct wp_Krb5kdfCtx {
+ /** wolfSSL provider context. */
+ WOLFPROV_CTX* provCtx;
+
+ /** Cipher type for KRB5KDF. */
+ int cipherType;
+
+ /** Key for KDF. */
+ unsigned char* key;
+ /** Size of key in bytes. */
+ size_t keySz;
+
+ /** Constant for KRB5KDF. */
+ unsigned char* constant;
+ /** Size of constant in bytes. */
+ size_t constantSz;
+} wp_Krb5kdfCtx;
+
+#define WP_KRB5KDF_CIPHER_NONE 0
+#define WP_KRB5KDF_CIPHER_AES_128_CBC 1
+#define WP_KRB5KDF_CIPHER_AES_256_CBC 2
+
+/**
+ * Create a new KRB5KDF context object.
+ *
+ * @param [in] provCtx wolfProvider context object.
+ * @return NULL on failure.
+ * @return KRB5KDF context object on success.
+ */
+static wp_Krb5kdfCtx* wp_kdf_krb5kdf_new(WOLFPROV_CTX* provCtx)
+{
+ wp_Krb5kdfCtx* ctx = NULL;
+
+ if (wolfssl_prov_is_running()) {
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ }
+ if (ctx != NULL) {
+ ctx->provCtx = provCtx;
+ ctx->cipherType = WP_KRB5KDF_CIPHER_NONE;
+ }
+
+ return ctx;
+}
+
+/**
+ * Clear KRB5KDF context object.
+ *
+ * @param [in, out] ctx KRB5KDF context object.
+ */
+static void wp_kdf_krb5kdf_clear(wp_Krb5kdfCtx* ctx)
+{
+ if (ctx != NULL) {
+ OPENSSL_clear_free(ctx->key, ctx->keySz);
+ if (ctx->constant != NULL) {
+ OPENSSL_free(ctx->constant);
+ }
+ }
+}
+
+/**
+ * Free the KRB5KDF context object.
+ *
+ * @param [in, out] ctx KRB5KDF context object.
+ */
+static void wp_kdf_krb5kdf_free(wp_Krb5kdfCtx* ctx)
+{
+ if (ctx != NULL) {
+ wp_kdf_krb5kdf_clear(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+/**
+ * Reset KRB5KDF context object.
+ *
+ * Disposes of allocated data.
+ *
+ * @param [in, out] ctx KRB5KDF context object.
+ */
+static void wp_kdf_krb5kdf_reset(wp_Krb5kdfCtx* ctx)
+{
+ if (ctx != NULL) {
+ WOLFPROV_CTX* provCtx = ctx->provCtx;
+ wp_kdf_krb5kdf_clear(ctx);
+ XMEMSET(ctx, 0, sizeof(*ctx));
+ ctx->provCtx = provCtx;
+ }
+}
+
+#define WP_MAX_CIPHER_NAME_LEN 12
+
+/**
+ * Set the KRB5KDF context parameters.
+ *
+ * @param [in, out] ctx KRB5KDF context object.
+ * @param [in] params Array of parameters with values.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+static int wp_kdf_krb5kdf_set_ctx_params(wp_Krb5kdfCtx* ctx,
+ const OSSL_PARAM params[])
+{
+ int ok = 1;
+ OSSL_PARAM* p;
+
+ if (params != NULL) {
+ if (ok) {
+ p = OSSL_PARAM_locate((OSSL_PARAM*)params, OSSL_KDF_PARAM_CIPHER);
+ if ((p != NULL) && (p->data != NULL)) {
+ char cipher[WP_MAX_CIPHER_NAME_LEN];
+ char* pCipher = cipher;
+ XMEMSET(cipher, 0, sizeof(cipher));
+
+ if (!OSSL_PARAM_get_utf8_string(p, &pCipher, sizeof(cipher))) {
+ ok = 0;
+ }
+ if (ok) {
+ /* Only allow AES-128-CBC or AES-256-CBC. */
+ if (XSTRCMP(cipher, "AES-128-CBC") == 0) {
+ ctx->cipherType = WP_KRB5KDF_CIPHER_AES_128_CBC;
+ }
+ else if (XSTRCMP(cipher, "AES-256-CBC") == 0) {
+ ctx->cipherType = WP_KRB5KDF_CIPHER_AES_256_CBC;
+ }
+ else {
+ ok = 0;
+ }
+ }
+ }
+ }
+
+ if (ok) {
+ p = OSSL_PARAM_locate((OSSL_PARAM*)params, OSSL_KDF_PARAM_KEY);
+ if ((p != NULL) && (p->data != NULL)) {
+ OPENSSL_clear_free(ctx->key, ctx->keySz);
+ ctx->key = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void**)&ctx->key, 0,
+ &ctx->keySz)) {
+ ok = 0;
+ }
+ }
+ }
+
+ if (ok) {
+ p = OSSL_PARAM_locate((OSSL_PARAM*)params, OSSL_KDF_PARAM_CONSTANT);
+ if ((p != NULL) && (p->data != NULL)) {
+ OPENSSL_free(ctx->constant);
+ ctx->constant = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void**)&ctx->constant, 0,
+ &ctx->constantSz)) {
+ ok = 0;
+ }
+ }
+ }
+ }
+
+ return ok;
+}
+
+/**
+ * Get the KRB5KDF context parameters.
+ *
+ * @param [in] ctx KRB5KDF context object.
+ * @param [in, out] params Array of parameters with values.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+static int wp_kdf_krb5kdf_get_ctx_params(wp_Krb5kdfCtx* ctx,
+ OSSL_PARAM params[])
+{
+ int ok = 1;
+ OSSL_PARAM* p;
+
+ (void)ctx;
+
+ p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_size_t(p, ctx->keySz)) {
+ ok = 0;
+ }
+ }
+
+ return ok;
+}
+
+/**
+ * Returns the parameters that can be set in the KRB5KDF context.
+ *
+ * @param [in] ctx KRB5KDF context object. Unused.
+ * @param [in] provCtx Provider context object. Unused.
+ * @return Array of parameters.
+ */
+static const OSSL_PARAM* wp_kdf_krb5kdf_settable_ctx_params(wp_Krb5kdfCtx* ctx,
+ WOLFPROV_CTX* provCtx)
+{
+ static const OSSL_PARAM wp_krb5kdf_supported_settable_ctx_params[] = {
+ WP_KRB5KDF_BASE_SETTABLES,
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_END
+ };
+ (void)ctx;
+ (void)provCtx;
+ return wp_krb5kdf_supported_settable_ctx_params;
+}
+
+/**
+ * Returns the parameters that can be retrieved from the KRB5KDF context.
+ *
+ * @param [in] ctx KRB5KDF context object. Unused.
+ * @param [in] provCtx Provider context object. Unused.
+ * @return Array of parameters.
+ */
+static const OSSL_PARAM* wp_kdf_krb5kdf_gettable_ctx_params(wp_Krb5kdfCtx* ctx,
+ WOLFPROV_CTX* provCtx)
+{
+ static const OSSL_PARAM wp_krb5kdf_supported_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+ };
+ (void)ctx;
+ (void)provCtx;
+ return wp_krb5kdf_supported_gettable_ctx_params;
+}
+
+static int wp_kdf_krb5kdf_expected_key_size(wp_Krb5kdfCtx* ctx)
+{
+ switch (ctx->cipherType) {
+ case WP_KRB5KDF_CIPHER_AES_128_CBC:
+ return 16;
+ case WP_KRB5KDF_CIPHER_AES_256_CBC:
+ return 32;
+ default:
+ return 0;
+ }
+}
+
+/* N-fold(K) where blocksize is N, and constant_len is K
+ * Note: Here |= denotes concatenation
+ *
+ * L = lcm(N,K)
+ * R = L/K
+ *
+ * for r: 1 -> R
+ * s |= constant rot 13*(r-1))
+ *
+ * block = 0
+ * for k: 1 -> K
+ * block += s[N(k-1)..(N-1)k] (ones'-complement addition)
+ *
+ * Optimizing for space we compute:
+ * for each l in L-1 -> 0:
+ * s[l] = (constant rot 13*(l/K))[l%k]
+ * block[l % N] += s[l] (with carry)
+ * finally add carry if any
+ */
+static void n_fold(unsigned char *block, unsigned int blocksize,
+ const unsigned char *constant, size_t constant_len)
+{
+ unsigned int cnt;
+ unsigned int i;
+ unsigned int a;
+ unsigned int b;
+ unsigned int carry;
+ unsigned int rot;
+ unsigned int bi;
+ unsigned int const_len = (unsigned int)constant_len;
+
+ /* If equal size then only one unrotated copy of constant needed. */
+ if (blocksize == const_len) {
+ XMEMCPY(block, constant, constant_len);
+ return;
+ }
+
+ /* Compute GCD of constant_len and blocksize. */
+ a = blocksize;
+ b = const_len;
+ while (b != 0) {
+ unsigned int t = b;
+ b = a % b;
+ a = t;
+ }
+ /* Calculate LCM of constant_len and blocksize. */
+ cnt = (const_len * blocksize) / a;
+
+ /* Start with constant un-rotated and then add to zero for the rest. */
+ XMEMCPY(block, constant, constant_len);
+ XMEMSET(block + constant_len, 0, blocksize - constant_len);
+
+ /* No initial carry. */
+ carry = 0;
+ /* First rotation is 13 bits. */
+ rot = 13;
+ /* Starting block index - constant_len <= blocksize. */
+ bi = const_len;
+ /* Do one constant at a time - cnt is a multiple of constant_len. */
+ for (i = const_len; i < cnt; i += const_len) {
+ unsigned int j;
+ /* Calculate first index into constant to rotate. */
+ unsigned int ci = ((const_len - (rot >> 3)) - 1) % const_len;
+ /* Calculate amount to rotate right and left. */
+ unsigned char rr = rot & 0x7;
+ unsigned char rl = 8 - rr;
+
+ /* Add in constant buffer to block. */
+ for (j = 0; j < const_len; j++) {
+ /* Rotated constant value. */
+ unsigned char rcv;
+
+ /* Get rotated constant buffer value. */
+ rcv = (unsigned char)(constant[ci] << rl);
+ ci = (ci + 1) % const_len;
+ rcv |= (unsigned char)(constant[ci] >> rr);
+
+ /* Add block value and rotated constant value to previous carry. */
+ carry += block[bi] + rcv;
+ /* Store new block value. */
+ block[bi] = (unsigned char)(carry & 0xff);
+ /* Get carry. */
+ carry >>= 8;
+
+ /* Next block index. */
+ bi = (bi + 1) % blocksize;
+ }
+ rot += 13;
+ }
+
+ /* Final carry pass. */
+ for (i = 0; (i < blocksize) && (carry > 0); i++) {
+ carry += block[i];
+ block[i] = (unsigned char)(carry & 0xff);
+ carry >>= 8;
+ }
+}
+
+/**
+ * Derive a key using KRB5KDF.
+ *
+ * @param [in, out] ctx KRB5KDF context object.
+ * @param [out] key Buffer to hold derived key.
+ * @param [in] keyLen Size of buffer in bytes.
+ * @param [in] params Array of parameters to set before deriving.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+static int wp_kdf_krb5kdf_derive(wp_Krb5kdfCtx* ctx, unsigned char* key,
+ size_t keyLen, const OSSL_PARAM params[])
+{
+ int ok = 1;
+ size_t osize = 0;
+ size_t cipherLen = 0;
+ int rc;
+ Aes aes;
+ byte block[AES_BLOCK_SIZE];
+ byte cipherBlock[AES_BLOCK_SIZE];
+ byte *plain = NULL;
+ byte *cipher = NULL;
+
+ if (!wolfssl_prov_is_running()) {
+ ok = 0;
+ }
+ if (ok && (!wp_kdf_krb5kdf_set_ctx_params(ctx, params))) {
+ ok = 0;
+ }
+ if (ok && (ctx->key == NULL)) {
+ ok = 0;
+ }
+ if (ok && (ctx->keySz != keyLen)) {
+ ok = 0;
+ }
+ if (ok && (wp_kdf_krb5kdf_expected_key_size(ctx) != (int)ctx->keySz)) {
+ ok = 0;
+ }
+ if (ok) {
+ XMEMSET(key, 0, keyLen);
+
+ rc = wc_AesSetKey(&aes, ctx->key, (word32)ctx->keySz, NULL,
+ AES_ENCRYPTION);
+ if (rc != 0) {
+ ok = 0;
+ }
+ }
+ if (ok) {
+ n_fold(block, AES_BLOCK_SIZE, ctx->constant, ctx->constantSz);
+ plain = block;
+ cipher = cipherBlock;
+ for (osize = 0; ok && osize < keyLen; osize += cipherLen) {
+ rc = wc_AesCbcEncrypt(&aes, cipher, plain, AES_BLOCK_SIZE);
+ if (rc != 0) {
+ ok = 0;
+ }
+
+ cipherLen = AES_BLOCK_SIZE;
+ if (cipherLen > (keyLen - osize))
+ cipherLen = (keyLen - osize);
+
+ XMEMCPY(key + osize, cipher, cipherLen);
+ if (keyLen > (osize + cipherLen)) {
+ rc = wc_AesSetKey(&aes, ctx->key, (word32)ctx->keySz, NULL,
+ AES_ENCRYPTION);
+ if (rc != 0) {
+ ok = 0;
+ }
+
+ if (ok) {
+ /* swap blocks */
+ plain = cipher;
+ if (cipher == block) {
+ cipher = cipherBlock;
+ } else {
+ cipher = block;
+ }
+ }
+ }
+ }
+ }
+
+ wc_AesFree(&aes);
+
+ return ok;
+}
+
+/** Dispatch table for KRB5KDF functions implemented using wolfSSL. */
+const OSSL_DISPATCH wp_kdf_krb5kdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (DFUNC)wp_kdf_krb5kdf_new },
+ { OSSL_FUNC_KDF_FREECTX, (DFUNC)wp_kdf_krb5kdf_free },
+ { OSSL_FUNC_KDF_RESET, (DFUNC)wp_kdf_krb5kdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (DFUNC)wp_kdf_krb5kdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, (DFUNC)wp_kdf_krb5kdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (DFUNC)wp_kdf_krb5kdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, (DFUNC)wp_kdf_krb5kdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (DFUNC)wp_kdf_krb5kdf_get_ctx_params },
+ { 0, NULL }
+};
diff --git a/src/wp_wolfprov.c b/src/wp_wolfprov.c
index 444964e5..a0f789b3 100644
--- a/src/wp_wolfprov.c
+++ b/src/wp_wolfprov.c
@@ -546,6 +546,10 @@ static const OSSL_ALGORITHM wolfprov_kdfs[] = {
{ WP_NAMES_TLS1_PRF, WOLFPROV_PROPERTIES, wp_kdf_tls1_prf_functions,
"" },
#endif
+#ifdef WP_HAVE_KRB5KDF
+ { WP_NAMES_KRB5KDF, WOLFPROV_PROPERTIES, wp_kdf_krb5kdf_functions,
+ "" },
+#endif
{ NULL, NULL, NULL, NULL }
};
diff --git a/test/include.am b/test/include.am
index f4f55329..07f2fc6e 100644
--- a/test/include.am
+++ b/test/include.am
@@ -20,6 +20,7 @@ test_unit_test_SOURCES = \
test/test_ecc.c \
test/test_ecx.c \
test/test_gmac.c \
+ test/test_krb5kdf.c \
test/test_hkdf.c \
test/test_hmac.c \
test/test_logging.c \
diff --git a/test/test_krb5kdf.c b/test/test_krb5kdf.c
new file mode 100644
index 00000000..022b18d0
--- /dev/null
+++ b/test/test_krb5kdf.c
@@ -0,0 +1,214 @@
+/* test_krb5kdf.c
+ *
+ * Copyright (C) 2006-2024 wolfSSL Inc.
+ *
+ * This file is part of wolfProvider.
+ *
+ * wolfProvider 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.
+ *
+ * wolfProvider 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 wolfProvider. If not, see .
+ */
+
+#include "unit.h"
+
+#ifdef WP_HAVE_KRB5KDF
+
+/* Test KRB5KDF using OpenSSL implementation */
+static int test_krb5kdf_calc(OSSL_LIB_CTX* libCtx, unsigned char *key,
+ size_t keyLen, const char* cipher, const unsigned char* inKey,
+ size_t inKeyLen, const unsigned char* constant, size_t constantLen)
+{
+ int err = 0;
+ EVP_KDF *kdf = NULL;
+ EVP_KDF_CTX *kctx = NULL;
+ OSSL_PARAM params[4], *p = params;
+
+ /* Create KDF */
+ kdf = EVP_KDF_fetch(libCtx, "KRB5KDF", NULL);
+ if (kdf == NULL) {
+ PRINT_MSG("Failed to fetch KRB5KDF");
+ err = 1;
+ }
+
+ if (err == 0) {
+ /* Create KDF context */
+ kctx = EVP_KDF_CTX_new(kdf);
+ if (kctx == NULL) {
+ PRINT_MSG("Failed to create KDF context");
+ err = 1;
+ }
+ }
+
+ if (err == 0) {
+ /* Set parameters */
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER,
+ (char*)cipher, 0);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+ (unsigned char*)inKey, inKeyLen);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_CONSTANT,
+ (unsigned char*)constant, constantLen);
+ *p = OSSL_PARAM_construct_end();
+
+ /* Derive key */
+ if (EVP_KDF_derive(kctx, key, keyLen, params) <= 0) {
+ PRINT_MSG("Failed to derive key");
+ err = 1;
+ }
+ }
+
+ EVP_KDF_CTX_free(kctx);
+ EVP_KDF_free(kdf);
+ return err;
+}
+
+/* Test error cases */
+static int test_krb5kdf_error_cases(OSSL_LIB_CTX* libCtx)
+{
+ int err;
+ unsigned char key[32];
+ /* 32-byte key for AES-128 test (wrong size) */
+ unsigned char inKey32[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+ };
+ /* 16-byte key for AES-256 test (wrong size) */
+ unsigned char inKey16[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
+ };
+ unsigned char constant[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+ };
+
+ PRINT_MSG("Testing KRB5KDF error case - AES-128-CBC with 32-byte key");
+ /* This should fail since AES-128 key size is 16 bytes */
+ err = test_krb5kdf_calc(libCtx, key, sizeof(key), "AES-128-CBC",
+ inKey32, sizeof(inKey32), constant, sizeof(constant));
+ if (err == 0) {
+ /* If we get here, the test failed because it should have errored */
+ PRINT_MSG("FAILED: KRB5KDF accepted wrong key size for AES-128-CBC");
+ return 1;
+ }
+ PRINT_MSG("Negative test passed - KRB5KDF correctly rejected wrong key size for AES-128-CBC");
+
+ PRINT_MSG("Testing KRB5KDF error case - AES-256-CBC with 16-byte key");
+ /* This should fail since AES-256 key size is 32 bytes */
+ err = test_krb5kdf_calc(libCtx, key, sizeof(key), "AES-256-CBC",
+ inKey16, sizeof(inKey16), constant, sizeof(constant));
+ if (err == 0) {
+ /* If we get here, the test failed because it should have errored */
+ PRINT_MSG("FAILED: KRB5KDF accepted wrong key size for AES-256-CBC");
+ return 1;
+ }
+ PRINT_MSG("Negative test passed - KRB5KDF correctly rejected wrong key size for AES-256-CBC");
+
+ return 0;
+}
+
+/* Test vectors */
+static int test_krb5kdf_vector(void)
+{
+ int err = 0;
+ unsigned char oKey[32];
+ unsigned char wKey[32];
+ /* Test vector - AES-128-CBC */
+ unsigned char inKey128[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
+ };
+ /* Test vector - AES-256-CBC */
+ unsigned char inKey256[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+ };
+ unsigned char constant[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+ };
+
+ /* Test AES-128-CBC */
+ PRINT_MSG("Testing KRB5KDF with OpenSSL - AES-128-CBC");
+ err = test_krb5kdf_calc(osslLibCtx, oKey, 16, "AES-128-CBC",
+ inKey128, sizeof(inKey128), constant, sizeof(constant));
+ if (err == 1) {
+ PRINT_MSG("FAILED OpenSSL - AES-128-CBC");
+ return err;
+ }
+ PRINT_MSG("Testing KRB5KDF with wolfSSL - AES-128-CBC");
+ err = test_krb5kdf_calc(wpLibCtx, wKey, 16, "AES-128-CBC",
+ inKey128, sizeof(inKey128), constant, sizeof(constant));
+ if (err == 1) {
+ PRINT_MSG("FAILED wolfSSL - AES-128-CBC");
+ return err;
+ }
+ if (memcmp(oKey, wKey, 16) != 0) {
+ PRINT_MSG("FAILED, wolfSSL and OpenSSL derived different keys");
+ PRINT_BUFFER("OpenSSL key", oKey, 16);
+ PRINT_BUFFER("wolfSSL key", wKey, 16);
+ return 1;
+ }
+
+ /* Test AES-256-CBC */
+ PRINT_MSG("Testing KRB5KDF with OpenSSL - AES-256-CBC");
+ err = test_krb5kdf_calc(osslLibCtx, oKey, 32, "AES-256-CBC",
+ inKey256, sizeof(inKey256), constant, sizeof(constant));
+ if (err == 1) {
+ PRINT_MSG("FAILED OpenSSL - AES-256-CBC");
+ return err;
+ }
+ PRINT_MSG("Testing KRB5KDF with wolfSSL - AES-256-CBC");
+ err = test_krb5kdf_calc(wpLibCtx, wKey, 32, "AES-256-CBC",
+ inKey256, sizeof(inKey256), constant, sizeof(constant));
+ if (err == 1) {
+ PRINT_MSG("FAILED wolfSSL - AES-256-CBC");
+ return err;
+ }
+ if (memcmp(oKey, wKey, 32) != 0) {
+ PRINT_MSG("FAILED, wolfSSL and OpenSSL derived different keys");
+ PRINT_BUFFER("OpenSSL key", oKey, 32);
+ PRINT_BUFFER("wolfSSL key", wKey, 32);
+ return 1;
+ }
+
+ return err;
+}
+
+int test_krb5kdf(void *data)
+{
+ int err = 0;
+
+ (void)data;
+
+ err = test_krb5kdf_vector();
+ if (err != 0) {
+ return err;
+ }
+
+ /* Test error cases with OpenSSL first */
+ err = test_krb5kdf_error_cases(osslLibCtx);
+ if (err != 0) {
+ return err;
+ }
+
+ /* Test error cases with wolfSSL */
+ err = test_krb5kdf_error_cases(wpLibCtx);
+ if (err != 0) {
+ return err;
+ }
+
+ return err;
+}
+
+#endif /* WP_HAVE_KRB5KDF */
diff --git a/test/unit.c b/test/unit.c
index ad370310..dbfbf4ec 100644
--- a/test/unit.c
+++ b/test/unit.c
@@ -99,6 +99,9 @@ TEST_CASE test_case[] = {
#ifdef WP_HAVE_HKDF
TEST_DECL(test_hkdf, NULL),
#endif
+#ifdef WP_HAVE_KRB5KDF
+ TEST_DECL(test_krb5kdf, NULL),
+#endif
#ifdef WP_HAVE_DES3CBC
TEST_DECL(test_des3_cbc, NULL),
TEST_DECL(test_des3_cbc_stream, NULL),
diff --git a/test/unit.h b/test/unit.h
index 0499533b..918dfb72 100644
--- a/test/unit.h
+++ b/test/unit.h
@@ -132,6 +132,10 @@ int test_tls1_prf(void *data);
int test_hkdf(void *data);
#endif
+#ifdef WP_HAVE_KRB5KDF
+int test_krb5kdf(void *data);
+#endif
+
#ifdef WP_HAVE_DES3CBC
int test_des3_cbc(void *data);
int test_des3_cbc_stream(void *data);