krb5 commit: Use OpenSSL 3 versions of remaining KDFs

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/1c87ce6c44a9de0824580a2d72a8a202237e01f4
commit 1c87ce6c44a9de0824580a2d72a8a202237e01f4
Author: Robbie Harwood <[email protected]>
Date:   Fri Sep 20 17:20:59 2019 -0400

    Use OpenSSL 3 versions of remaining KDFs
    
    In OpenSSL's terminology, id-pkinit-kdf is an instance of SSKDF,
    AES-SHA2 and Camellia use KBKDF, and the KDF for DES3 and AES-SHA1 has
    been named KRB5KDF.  Support for these KDFs was added in OpenSSL 3; we
    already supported the existing PBKDF2.
    
    [[email protected]: reorganize into builtin and openssl versions of the
    file; detect hash and encryption provider identity using pointer
    equality like we do in the OpenSSL PBKDF implementation; add helpers
    for this translation; simplify and better refactor the PKINIT code;
    fix some latent pkinit_kdf_test.c bugs]

 src/lib/crypto/builtin/Makefile.in                 |    3 +
 src/lib/crypto/builtin/deps                        |   11 +
 src/lib/crypto/builtin/kdf.c                       |  190 +++++++++++
 src/lib/crypto/krb/crypto_int.h                    |   43 +++-
 src/lib/crypto/krb/derive.c                        |  188 +-----------
 src/lib/crypto/krb/prf_aes2.c                      |    2 +-
 src/lib/crypto/openssl/Makefile.in                 |    3 +
 src/lib/crypto/openssl/deps                        |   11 +
 src/lib/crypto/openssl/kdf.c                       |  237 ++++++++++++++
 src/plugins/preauth/pkinit/pkinit_constants.c      |   22 +-
 src/plugins/preauth/pkinit/pkinit_crypto.h         |    9 +-
 src/plugins/preauth/pkinit/pkinit_crypto_openssl.c |  330 ++++++++++----------
 src/plugins/preauth/pkinit/pkinit_kdf_test.c       |   18 +-
 13 files changed, 685 insertions(+), 382 deletions(-)

diff --git a/src/lib/crypto/builtin/Makefile.in b/src/lib/crypto/builtin/Makefile.in
index 62ce32c..e874fd2 100644
--- a/src/lib/crypto/builtin/Makefile.in
+++ b/src/lib/crypto/builtin/Makefile.in
@@ -9,14 +9,17 @@ LOCALINCLUDES=-I$(srcdir)/../krb $(CRYPTO_IMPL_CFLAGS)
 
 STLIBOBJS=\
 	hmac.o	\
+	kdf.o \
 	pbkdf2.o
 
 OBJS=\
 	$(OUTPRE)hmac.$(OBJEXT)	\
+	$(OUTPRE)kdf.$(OBJEXT) \
 	$(OUTPRE)pbkdf2.$(OBJEXT)
 
 SRCS=\
 	$(srcdir)/hmac.c	\
+	$(srcdir)/kdf.c		\
 	$(srcdir)/pbkdf2.c	
 
 STOBJLISTS= des/OBJS.ST md4/OBJS.ST 	\
diff --git a/src/lib/crypto/builtin/deps b/src/lib/crypto/builtin/deps
index 7b83a55..0b18a19 100644
--- a/src/lib/crypto/builtin/deps
+++ b/src/lib/crypto/builtin/deps
@@ -12,6 +12,17 @@ hmac.so hmac.po $(OUTPRE)hmac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   hmac.c
+kdf.so kdf.po $(OUTPRE)kdf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
+  $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+  $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+  $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+  $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+  kdf.c
 pbkdf2.so pbkdf2.po $(OUTPRE)pbkdf2.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
   $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
diff --git a/src/lib/crypto/builtin/kdf.c b/src/lib/crypto/builtin/kdf.c
new file mode 100644
index 0000000..8a6658b
--- /dev/null
+++ b/src/lib/crypto/builtin/kdf.c
@@ -0,0 +1,190 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "crypto_int.h"
+
+#ifdef K5_BUILTIN_KDF
+
+krb5_error_code
+k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+                          krb5_key key, const krb5_data *label,
+                          const krb5_data *context, krb5_data *rnd_out)
+{
+    krb5_crypto_iov iov[5];
+    krb5_error_code ret;
+    krb5_data prf;
+    unsigned char ibuf[4], lbuf[4];
+
+    if (hash == NULL || rnd_out->length > hash->hashsize)
+        return KRB5_CRYPTO_INTERNAL;
+
+    /* Allocate encryption data buffer. */
+    ret = alloc_data(&prf, hash->hashsize);
+    if (ret)
+        return ret;
+
+    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
+    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[0].data = make_data(ibuf, sizeof(ibuf));
+    store_32_be(1, ibuf);
+    /* Label */
+    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[1].data = *label;
+    /* 0x00: separator byte */
+    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[2].data = make_data("", 1);
+    /* Context */
+    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[3].data = *context;
+    /* [L]2: four-byte big-endian binary string giving the output length */
+    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[4].data = make_data(lbuf, sizeof(lbuf));
+    store_32_be(rnd_out->length * 8, lbuf);
+
+    ret = krb5int_hmac(hash, key, iov, 5, &prf);
+    if (!ret)
+        memcpy(rnd_out->data, prf.data, rnd_out->length);
+    zapfree(prf.data, prf.length);
+    return ret;
+}
+
+krb5_error_code
+k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc, krb5_key key,
+                           const krb5_data *label, krb5_data *rnd_out)
+{
+    size_t blocksize, keybytes, n;
+    krb5_crypto_iov iov[6];
+    krb5_error_code ret;
+    krb5_data prf;
+    unsigned int i;
+    unsigned char ibuf[4], Lbuf[4];
+
+    blocksize = enc->block_size;
+    keybytes = enc->keybytes;
+
+    if (key->keyblock.length != enc->keylength || rnd_out->length != keybytes)
+        return KRB5_CRYPTO_INTERNAL;
+
+    /* Allocate encryption data buffer. */
+    ret = alloc_data(&prf, blocksize);
+    if (ret)
+        return ret;
+
+    /* K(i-1): the previous block of PRF output, initially all-zeros. */
+    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[0].data = prf;
+    /* [i]2: four-byte big-endian binary string giving the block counter */
+    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[1].data = make_data(ibuf, sizeof(ibuf));
+    /* Label: the fixed derived-key input */
+    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[2].data = *label;
+    /* 0x00: separator byte */
+    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[3].data = make_data("", 1);
+    /* Context: (unused) */
+    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[4].data = empty_data();
+    /* [L]2: four-byte big-endian binary string giving the output length */
+    iov[5].flags = KRB5_CRYPTO_TYPE_DATA;
+    iov[5].data = make_data(Lbuf, sizeof(Lbuf));
+    store_32_be(rnd_out->length * 8, Lbuf);
+
+    for (i = 1, n = 0; n < keybytes; i++) {
+        /* Update the block counter. */
+        store_32_be(i, ibuf);
+
+        /* Compute a CMAC checksum, storing the result into K(i-1). */
+        ret = krb5int_cmac_checksum(enc, key, iov, 6, &prf);
+        if (ret)
+            goto cleanup;
+
+        /* Copy the result into the appropriate part of the output buffer. */
+        if (keybytes - n <= blocksize) {
+            memcpy(rnd_out->data + n, prf.data, keybytes - n);
+            break;
+        }
+        memcpy(rnd_out->data + n, prf.data, blocksize);
+        n += blocksize;
+    }
+
+cleanup:
+    zapfree(prf.data, blocksize);
+    return ret;
+}
+
+krb5_error_code
+k5_derive_random_rfc3961(const struct krb5_enc_provider *enc, krb5_key key,
+                         const krb5_data *constant, krb5_data *rnd_out)
+{
+    size_t blocksize, keybytes, n;
+    krb5_error_code ret;
+    krb5_data block = empty_data();
+
+    blocksize = enc->block_size;
+    keybytes = enc->keybytes;
+
+    if (blocksize == 1)
+        return KRB5_BAD_ENCTYPE;
+    if (key->keyblock.length != enc->keylength || rnd_out->length != keybytes)
+        return KRB5_CRYPTO_INTERNAL;
+
+    /* Allocate encryption data buffer. */
+    ret = alloc_data(&block, blocksize);
+    if (ret)
+        return ret;
+
+    /* Initialize the input block. */
+    if (constant->length == blocksize) {
+        memcpy(block.data, constant->data, blocksize);
+    } else {
+        krb5int_nfold(constant->length * 8, (uint8_t *)constant->data,
+                      blocksize * 8, (uint8_t *)block.data);
+    }
+
+    /* Loop encrypting the blocks until enough key bytes are generated. */
+    n = 0;
+    while (n < keybytes) {
+        ret = encrypt_block(enc, key, &block);
+        if (ret)
+            goto cleanup;
+
+        if ((keybytes - n) <= blocksize) {
+            memcpy(rnd_out->data + n, block.data, (keybytes - n));
+            break;
+        }
+
+        memcpy(rnd_out->data + n, block.data, blocksize);
+        n += blocksize;
+    }
+
+cleanup:
+    zapfree(block.data, blocksize);
+    return ret;
+}
+
+#endif /* K5_BUILTIN_KDF */
diff --git a/src/lib/crypto/krb/crypto_int.h b/src/lib/crypto/krb/crypto_int.h
index 82474aa..f7980ef 100644
--- a/src/lib/crypto/krb/crypto_int.h
+++ b/src/lib/crypto/krb/crypto_int.h
@@ -42,14 +42,19 @@
  * library carries complications, so use the built-in implementations of these
  * primitives instead.  OpenSSL 3.0 also deprecates DES_set_odd_parity() with
  * no replacement.
+ *
+ * OpenSSL 3.0 adds KDF implementations matching the ones we use to derive
+ * encryption and authentication keys from protocol keys.
  */
 #define K5_BUILTIN_DES_KEY_PARITY
 #define K5_BUILTIN_MD4
 #define K5_BUILTIN_RC4
+#define K5_OPENSSL_KDF
 #else
 #define K5_OPENSSL_DES_KEY_PARITY
 #define K5_OPENSSL_MD4
 #define K5_OPENSSL_RC4
+#define K5_BUILTIN_KDF
 #endif
 
 #define K5_OPENSSL_AES
@@ -68,6 +73,7 @@
 #define K5_BUILTIN_DES
 #define K5_BUILTIN_DES_KEY_PARITY
 #define K5_BUILTIN_HMAC
+#define K5_BUILTIN_KDF
 #define K5_BUILTIN_MD4
 #define K5_BUILTIN_MD5
 #define K5_BUILTIN_PBKDF2
@@ -387,10 +393,6 @@ krb5_error_code krb5int_derive_random(const struct krb5_enc_provider *enc,
                                       krb5_key inkey, krb5_data *outrnd,
                                       const krb5_data *in_constant,
                                       enum deriv_alg alg);
-krb5_error_code
-k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
-                          krb5_key inkey, krb5_data *outrnd,
-                          const krb5_data *label, const krb5_data *context);
 
 /*** Miscellaneous prototypes ***/
 
@@ -510,6 +512,39 @@ krb5_error_code krb5int_pbkdf2_hmac(const struct krb5_hash_provider *hash,
                                     const krb5_data *password,
                                     const krb5_data *salt);
 
+/*
+ * Compute the NIST SP800-108 KDF in counter mode (section 5.1) with the
+ * following parameters:
+ *   - HMAC (with hash as the hash provider) is the PRF.
+ *   - A block counter of four bytes is used.
+ *   - Four bytes are used to encode the output length in the PRF input.
+ *
+ * There are no uses requiring more than a single PRF invocation.
+ */
+krb5_error_code
+k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+                          krb5_key key, const krb5_data *label,
+                          const krb5_data *context, krb5_data *rnd_out);
+
+/*
+ * Compute the NIST SP800-108 KDF in feedback mode (section 5.2) with the
+ * following parameters:
+ *   - CMAC (with enc as the enc provider) is the PRF.
+ *   - A block counter of four bytes is used.
+ *   - Label is the key derivation constant.
+ *   - Context is empty.
+ *   - Four bytes are used to encode the output length in the PRF input.
+ */
+krb5_error_code
+k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc, krb5_key key,
+                           const krb5_data *label, krb5_data *rnd_out);
+
+/* Compute the RFC 3961 section 5.1 KDF (which uses n-fold) with enc as E,
+ * inkey as Key, and in_constant as Constant. */
+krb5_error_code
+k5_derive_random_rfc3961(const struct krb5_enc_provider *enc, krb5_key key,
+                         const krb5_data *constant, krb5_data *rnd_out);
+
 /* The following are used by test programs and are just handler functions from
  * the AES and Camellia enc providers. */
 krb5_error_code krb5int_aes_encrypt(krb5_key key, const krb5_data *ivec,
diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c
index 6707a73..3e51a00 100644
--- a/src/lib/crypto/krb/derive.c
+++ b/src/lib/crypto/krb/derive.c
@@ -77,185 +77,6 @@ cleanup:
     return ENOMEM;
 }
 
-static krb5_error_code
-derive_random_rfc3961(const struct krb5_enc_provider *enc,
-                      krb5_key inkey, krb5_data *outrnd,
-                      const krb5_data *in_constant)
-{
-    size_t blocksize, keybytes, n;
-    krb5_error_code ret;
-    krb5_data block = empty_data();
-
-    blocksize = enc->block_size;
-    keybytes = enc->keybytes;
-
-    if (blocksize == 1)
-        return KRB5_BAD_ENCTYPE;
-    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
-        return KRB5_CRYPTO_INTERNAL;
-
-    /* Allocate encryption data buffer. */
-    ret = alloc_data(&block, blocksize);
-    if (ret)
-        return ret;
-
-    /* Initialize the input block. */
-    if (in_constant->length == blocksize) {
-        memcpy(block.data, in_constant->data, blocksize);
-    } else {
-        krb5int_nfold(in_constant->length * 8,
-                      (unsigned char *) in_constant->data,
-                      blocksize * 8, (unsigned char *) block.data);
-    }
-
-    /* Loop encrypting the blocks until enough key bytes are generated. */
-    n = 0;
-    while (n < keybytes) {
-        ret = encrypt_block(enc, inkey, &block);
-        if (ret)
-            goto cleanup;
-
-        if ((keybytes - n) <= blocksize) {
-            memcpy(outrnd->data + n, block.data, (keybytes - n));
-            break;
-        }
-
-        memcpy(outrnd->data + n, block.data, blocksize);
-        n += blocksize;
-    }
-
-cleanup:
-    zapfree(block.data, blocksize);
-    return ret;
-}
-
-/*
- * NIST SP800-108 KDF in feedback mode (section 5.2).
- * Parameters:
- *   - CMAC (with enc as the enc provider) is the PRF.
- *   - A block counter of four bytes is used.
- *   - Label is the key derivation constant.
- *   - Context is empty.
- *   - Four bytes are used to encode the output length in the PRF input.
- */
-static krb5_error_code
-derive_random_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
-                                      krb5_key inkey, krb5_data *outrnd,
-                                      const krb5_data *in_constant)
-{
-    size_t blocksize, keybytes, n;
-    krb5_crypto_iov iov[6];
-    krb5_error_code ret;
-    krb5_data prf;
-    unsigned int i;
-    unsigned char ibuf[4], Lbuf[4];
-
-    blocksize = enc->block_size;
-    keybytes = enc->keybytes;
-
-    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
-        return KRB5_CRYPTO_INTERNAL;
-
-    /* Allocate encryption data buffer. */
-    ret = alloc_data(&prf, blocksize);
-    if (ret)
-        return ret;
-
-    /* K(i-1): the previous block of PRF output, initially all-zeros. */
-    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[0].data = prf;
-    /* [i]2: four-byte big-endian binary string giving the block counter */
-    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[1].data = make_data(ibuf, sizeof(ibuf));
-    /* Label: the fixed derived-key input */
-    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[2].data = *in_constant;
-    /* 0x00: separator byte */
-    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[3].data = make_data("", 1);
-    /* Context: (unused) */
-    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[4].data = empty_data();
-    /* [L]2: four-byte big-endian binary string giving the output length */
-    iov[5].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[5].data = make_data(Lbuf, sizeof(Lbuf));
-    store_32_be(outrnd->length * 8, Lbuf);
-
-    for (i = 1, n = 0; n < keybytes; i++) {
-        /* Update the block counter. */
-        store_32_be(i, ibuf);
-
-        /* Compute a CMAC checksum, storing the result into K(i-1). */
-        ret = krb5int_cmac_checksum(enc, inkey, iov, 6, &prf);
-        if (ret)
-            goto cleanup;
-
-        /* Copy the result into the appropriate part of the output buffer. */
-        if (keybytes - n <= blocksize) {
-            memcpy(outrnd->data + n, prf.data, keybytes - n);
-            break;
-        }
-        memcpy(outrnd->data + n, prf.data, blocksize);
-        n += blocksize;
-    }
-
-cleanup:
-    zapfree(prf.data, blocksize);
-    return ret;
-}
-
-/*
- * NIST SP800-108 KDF in counter mode (section 5.1).
- * Parameters:
- *   - HMAC (with hash as the hash provider) is the PRF.
- *   - A block counter of four bytes is used.
- *   - Four bytes are used to encode the output length in the PRF input.
- *
- * There are no uses requiring more than a single PRF invocation.
- */
-krb5_error_code
-k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
-                          krb5_key inkey, krb5_data *outrnd,
-                          const krb5_data *label, const krb5_data *context)
-{
-    krb5_crypto_iov iov[5];
-    krb5_error_code ret;
-    krb5_data prf;
-    unsigned char ibuf[4], lbuf[4];
-
-    if (hash == NULL || outrnd->length > hash->hashsize)
-        return KRB5_CRYPTO_INTERNAL;
-
-    /* Allocate encryption data buffer. */
-    ret = alloc_data(&prf, hash->hashsize);
-    if (ret)
-        return ret;
-
-    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
-    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[0].data = make_data(ibuf, sizeof(ibuf));
-    store_32_be(1, ibuf);
-    /* Label */
-    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[1].data = *label;
-    /* 0x00: separator byte */
-    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[2].data = make_data("", 1);
-    /* Context */
-    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[3].data = *context;
-    /* [L]2: four-byte big-endian binary string giving the output length */
-    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
-    iov[4].data = make_data(lbuf, sizeof(lbuf));
-    store_32_be(outrnd->length * 8, lbuf);
-
-    ret = krb5int_hmac(hash, inkey, iov, 5, &prf);
-    if (!ret)
-        memcpy(outrnd->data, prf.data, outrnd->length);
-    zapfree(prf.data, prf.length);
-    return ret;
-}
-
 krb5_error_code
 krb5int_derive_random(const struct krb5_enc_provider *enc,
                       const struct krb5_hash_provider *hash,
@@ -266,13 +87,12 @@ krb5int_derive_random(const struct krb5_enc_provider *enc,
 
     switch (alg) {
     case DERIVE_RFC3961:
-        return derive_random_rfc3961(enc, inkey, outrnd, in_constant);
+        return k5_derive_random_rfc3961(enc, inkey, in_constant, outrnd);
     case DERIVE_SP800_108_CMAC:
-        return derive_random_sp800_108_feedback_cmac(enc, inkey, outrnd,
-                                                     in_constant);
+        return k5_sp800_108_feedback_cmac(enc, inkey, in_constant, outrnd);
     case DERIVE_SP800_108_HMAC:
-        return k5_sp800_108_counter_hmac(hash, inkey, outrnd, in_constant,
-                                         &empty);
+        return k5_sp800_108_counter_hmac(hash, inkey, in_constant, &empty,
+                                         outrnd);
     default:
         return EINVAL;
     }
diff --git a/src/lib/crypto/krb/prf_aes2.c b/src/lib/crypto/krb/prf_aes2.c
index 9a5cffc..4361228 100644
--- a/src/lib/crypto/krb/prf_aes2.c
+++ b/src/lib/crypto/krb/prf_aes2.c
@@ -38,5 +38,5 @@ krb5int_aes2_prf(const struct krb5_keytypes *ktp, krb5_key key,
 {
     krb5_data label = string2data("prf");
 
-    return k5_sp800_108_counter_hmac(ktp->hash, key, out, &label, in);
+    return k5_sp800_108_counter_hmac(ktp->hash, key, &label, in, out);
 }
diff --git a/src/lib/crypto/openssl/Makefile.in b/src/lib/crypto/openssl/Makefile.in
index 1a24d77..d82049f 100644
--- a/src/lib/crypto/openssl/Makefile.in
+++ b/src/lib/crypto/openssl/Makefile.in
@@ -5,16 +5,19 @@ LOCALINCLUDES=-I$(srcdir)/../krb $(CRYPTO_IMPL_CFLAGS)
 
 STLIBOBJS=\
 	hmac.o	\
+	kdf.o	\
 	pbkdf2.o \
 	sha256.o
 
 OBJS=\
 	$(OUTPRE)hmac.$(OBJEXT)	\
+	$(OUTPRE)kdf.$(OBJEXT)	\
 	$(OUTPRE)pbkdf2.$(OBJEXT) \
 	$(OUTPRE)sha256.$(OBJEXT)
 
 SRCS=\
 	$(srcdir)/hmac.c	\
+	$(srcdir)/kdf.c		\
 	$(srcdir)/pbkdf2.c	\
 	$(srcdir)/sha256.c
 
diff --git a/src/lib/crypto/openssl/deps b/src/lib/crypto/openssl/deps
index 8f5efde..7009823 100644
--- a/src/lib/crypto/openssl/deps
+++ b/src/lib/crypto/openssl/deps
@@ -12,6 +12,17 @@ hmac.so hmac.po $(OUTPRE)hmac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   hmac.c
+kdf.so kdf.po $(OUTPRE)kdf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
+  $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+  $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+  $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+  $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+  $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+  kdf.c
 pbkdf2.so pbkdf2.po $(OUTPRE)pbkdf2.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
   $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
diff --git a/src/lib/crypto/openssl/kdf.c b/src/lib/crypto/openssl/kdf.c
new file mode 100644
index 0000000..41e845e
--- /dev/null
+++ b/src/lib/crypto/openssl/kdf.c
@@ -0,0 +1,237 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2021 by Red Hat, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "crypto_int.h"
+
+#ifdef K5_OPENSSL_KDF
+
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+
+static char *
+hash_name(const struct krb5_hash_provider *hash)
+{
+    if (hash == &krb5int_hash_sha1)
+        return "SHA1";
+    if (hash == &krb5int_hash_sha256)
+        return "SHA256";
+    if (hash == &krb5int_hash_sha384)
+        return "SHA384";
+    return NULL;
+}
+
+static char *
+enc_name(const struct krb5_enc_provider *enc)
+{
+    if (enc == &krb5int_enc_camellia128)
+        return "CAMELLIA-128-CBC";
+    if (enc == &krb5int_enc_camellia256)
+        return "CAMELLIA-256-CBC";
+    if (enc == &krb5int_enc_aes128)
+        return "AES-128-CBC";
+    if (enc == &krb5int_enc_aes256)
+        return "AES-256-CBC";
+    if (enc == &krb5int_enc_des3)
+        return "DES-EDE3-CBC";
+    return NULL;
+}
+
+krb5_error_code
+k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+                          krb5_key key, const krb5_data *label,
+                          const krb5_data *context, krb5_data *rnd_out)
+{
+    krb5_error_code ret;
+    EVP_KDF *kdf = NULL;
+    EVP_KDF_CTX *kctx = NULL;
+    OSSL_PARAM params[6], *p = params;
+    char *digest;
+
+    digest = hash_name(hash);
+    if (digest == NULL) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL);
+    if (!kdf) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    kctx = EVP_KDF_CTX_new(kdf);
+    if (!kctx) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, digest, 0);
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC,
+                                            OSSL_MAC_NAME_HMAC, 0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                             key->keyblock.contents,
+                                             key->keyblock.length);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+                                             context->data, context->length);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
+                                             label->data, label->length);
+    *p = OSSL_PARAM_construct_end();
+    if (EVP_KDF_derive(kctx, (uint8_t *)rnd_out->data, rnd_out->length,
+                       params) <= 0) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    ret = 0;
+done:
+    if (ret)
+        zap(rnd_out->data, rnd_out->length);
+    EVP_KDF_free(kdf);
+    EVP_KDF_CTX_free(kctx);
+    return ret;
+}
+
+krb5_error_code
+k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc, krb5_key key,
+                           const krb5_data *label, krb5_data *rnd_out)
+{
+    krb5_error_code ret;
+    EVP_KDF *kdf = NULL;
+    EVP_KDF_CTX *kctx = NULL;
+    OSSL_PARAM params[7], *p = params;
+    char *cipher;
+    static uint8_t zeroes[16];
+
+    memset(zeroes, 0, sizeof(zeroes));
+
+    cipher = enc_name(enc);
+    if (cipher == NULL) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL);
+    if (!kdf) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    kctx = EVP_KDF_CTX_new(kdf);
+    if (!kctx) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MODE,
+                                            "FEEDBACK", 0);
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC,
+                                            OSSL_MAC_NAME_CMAC, 0);
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER, cipher, 0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                             key->keyblock.contents,
+                                             key->keyblock.length);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
+                                             label->data, label->length);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED,
+                                             zeroes, sizeof(zeroes));
+    *p = OSSL_PARAM_construct_end();
+    if (EVP_KDF_derive(kctx, (uint8_t *)rnd_out->data, rnd_out->length,
+                       params) <= 0) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    ret = 0;
+done:
+    if (ret)
+        zap(rnd_out->data, rnd_out->length);
+    EVP_KDF_free(kdf);
+    EVP_KDF_CTX_free(kctx);
+    return ret;
+}
+
+krb5_error_code
+k5_derive_random_rfc3961(const struct krb5_enc_provider *enc, krb5_key key,
+                         const krb5_data *constant, krb5_data *rnd_out)
+{
+    krb5_error_code ret;
+    EVP_KDF *kdf = NULL;
+    EVP_KDF_CTX *kctx = NULL;
+    OSSL_PARAM params[4], *p = params;
+    char *cipher;
+
+    if (key->keyblock.length != enc->keylength ||
+        rnd_out->length != enc->keybytes) {
+        return KRB5_CRYPTO_INTERNAL;
+    }
+
+    cipher = enc_name(enc);
+    if (cipher == NULL) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    kdf = EVP_KDF_fetch(NULL, "KRB5KDF", NULL);
+    if (kdf == NULL) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    kctx = EVP_KDF_CTX_new(kdf);
+    if (kctx == NULL) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER, cipher, 0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                             key->keyblock.contents,
+                                             key->keyblock.length);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_CONSTANT,
+                                             constant->data, constant->length);
+    *p = OSSL_PARAM_construct_end();
+    if (EVP_KDF_derive(kctx, (uint8_t *)rnd_out->data, rnd_out->length,
+                       params) <= 0) {
+        ret = KRB5_CRYPTO_INTERNAL;
+        goto done;
+    }
+
+    ret = 0;
+done:
+    if (ret)
+        zap(rnd_out->data, rnd_out->length);
+    EVP_KDF_free(kdf);
+    EVP_KDF_CTX_free(kctx);
+    return ret;
+}
+
+#endif /* K5_OPENSSL_KDF */
diff --git a/src/plugins/preauth/pkinit/pkinit_constants.c b/src/plugins/preauth/pkinit/pkinit_constants.c
index 4d29efc..652897f 100644
--- a/src/plugins/preauth/pkinit/pkinit_constants.c
+++ b/src/plugins/preauth/pkinit/pkinit_constants.c
@@ -33,23 +33,13 @@
 #include "pkinit.h"
 
 /* statically declare OID constants for all three algorithms */
-const krb5_octet krb5_pkinit_sha1_oid[8] =
-{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x01};
-const size_t krb5_pkinit_sha1_oid_len = 8;
-const krb5_octet krb5_pkinit_sha256_oid[8] =
-{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x02};
-const size_t krb5_pkinit_sha256_oid_len = 8;
-const krb5_octet krb5_pkinit_sha512_oid [8] =
-{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x03};
-const size_t krb5_pkinit_sha512_oid_len = 8;
+static char sha1_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x01};
+static char sha256_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x02 };
+static char sha512_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x03 };
 
-#define oid_as_data(var, oid_base)                      \
-    const krb5_data var =                               \
-    {0, sizeof oid_base, (char *)oid_base}
-oid_as_data(sha1_id, krb5_pkinit_sha1_oid);
-oid_as_data(sha256_id, krb5_pkinit_sha256_oid);
-oid_as_data(sha512_id, krb5_pkinit_sha512_oid);
-#undef oid_as_data
+const krb5_data sha1_id = { KV5M_DATA, sizeof(sha1_oid), sha1_oid };
+const krb5_data sha256_id = { KV5M_DATA, sizeof(sha256_oid), sha256_oid };
+const krb5_data sha512_id = { KV5M_DATA, sizeof(sha512_oid), sha512_oid };
 
 krb5_data const * const supported_kdf_alg_ids[] = {
     &sha256_id,
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
index ec097bf..94a1b22 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -606,12 +606,9 @@ pkinit_alg_agility_kdf(krb5_context context,
                        krb5_data *pk_as_rep,
                        krb5_keyblock *key_block);
 
-extern const krb5_octet krb5_pkinit_sha1_oid[];
-extern const size_t krb5_pkinit_sha1_oid_len;
-extern const krb5_octet krb5_pkinit_sha256_oid[];
-extern const size_t krb5_pkinit_sha256_oid_len;
-extern const krb5_octet krb5_pkinit_sha512_oid[];
-extern const size_t  krb5_pkinit_sha512_oid_len;
+extern const krb5_data sha1_id;
+extern const krb5_data sha256_id;
+extern const krb5_data sha512_id;
 extern const krb5_data oakley_1024;
 extern const krb5_data oakley_2048;
 extern const krb5_data oakley_4096;
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 72f9530..f91f6f9 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -38,6 +38,12 @@
 #include <dirent.h>
 #include <arpa/inet.h>
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/core_names.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#endif
+
 static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );
 static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context );
 
@@ -2608,216 +2614,218 @@ cleanup:
 }
 
 
-/**
- * Given an algorithm_identifier, this function returns the hash length
- * and EVP function associated with that algorithm.
- */
-static krb5_error_code
-pkinit_alg_values(krb5_context context,
-                  const krb5_data *alg_id,
-                  size_t *hash_bytes,
-                  const EVP_MD *(**func)(void))
+/* Return the OpenSSL descriptor for the given RFC 5652 OID specified in RFC
+ * 8636.  RFC 8636 defines a SHA384 variant, but we don't use it. */
+static const EVP_MD *
+algid_to_md(const krb5_data *alg_id)
 {
-    *hash_bytes = 0;
-    *func = NULL;
-    if ((alg_id->length == krb5_pkinit_sha1_oid_len) &&
-        (0 == memcmp(alg_id->data, &krb5_pkinit_sha1_oid,
-                     krb5_pkinit_sha1_oid_len))) {
-        *hash_bytes = 20;
-        *func = &EVP_sha1;
-        return 0;
-    } else if ((alg_id->length == krb5_pkinit_sha256_oid_len) &&
-               (0 == memcmp(alg_id->data, krb5_pkinit_sha256_oid,
-                            krb5_pkinit_sha256_oid_len))) {
-        *hash_bytes = 32;
-        *func = &EVP_sha256;
-        return 0;
-    } else if ((alg_id->length == krb5_pkinit_sha512_oid_len) &&
-               (0 == memcmp(alg_id->data, krb5_pkinit_sha512_oid,
-                            krb5_pkinit_sha512_oid_len))) {
-        *hash_bytes = 64;
-        *func = &EVP_sha512;
-        return 0;
-    } else {
-        krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS,
-                               "Bad algorithm ID passed to PK-INIT KDF.");
-        return KRB5_ERR_BAD_S2K_PARAMS;
-    }
-} /* pkinit_alg_values() */
+    if (data_eq(*alg_id, sha1_id))
+        return EVP_sha1();
+    if (data_eq(*alg_id, sha256_id))
+        return EVP_sha256();
+    if (data_eq(*alg_id, sha512_id))
+        return EVP_sha512();
+    return NULL;
+}
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
 
-/* pkinit_alg_agility_kdf() --
- * This function generates a key using the KDF described in
- * draft_ietf_krb_wg_pkinit_alg_agility-04.txt.  The algorithm is
- * described as follows:
- *
- *     1.  reps = keydatalen (K) / hash length (H)
- *
- *     2.  Initialize a 32-bit, big-endian bit string counter as 1.
- *
- *     3.  For i = 1 to reps by 1, do the following:
- *
- *         -  Compute Hashi = H(counter || Z || OtherInfo).
- *
- *         -  Increment counter (modulo 2^32)
- *
- *     4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes.
- */
-krb5_error_code
-pkinit_alg_agility_kdf(krb5_context context,
-                       krb5_data *secret,
-                       krb5_data *alg_oid,
-                       krb5_const_principal party_u_info,
-                       krb5_const_principal party_v_info,
-                       krb5_enctype enctype,
-                       krb5_data *as_req,
-                       krb5_data *pk_as_rep,
-                       krb5_keyblock *key_block)
+#define sskdf openssl_sskdf
+static krb5_error_code
+openssl_sskdf(krb5_context context, const EVP_MD *md, krb5_data *key,
+              krb5_data *info, size_t len, krb5_data *out)
 {
-    krb5_error_code retval = 0;
-
-    unsigned int reps = 0;
-    uint32_t counter = 1;       /* Does this type work on Windows? */
-    size_t offset = 0;
-    size_t hash_len = 0;
-    size_t rand_len = 0;
-    size_t key_len = 0;
-    krb5_data random_data;
-    krb5_sp80056a_other_info other_info_fields;
-    krb5_pkinit_supp_pub_info supp_pub_info_fields;
-    krb5_data *other_info = NULL;
-    krb5_data *supp_pub_info = NULL;
-    krb5_algorithm_identifier alg_id;
-    EVP_MD_CTX *ctx = NULL;
-    const EVP_MD *(*EVP_func)(void);
+    krb5_error_code ret;
+    EVP_KDF *kdf = NULL;
+    EVP_KDF_CTX *kctx = NULL;
+    OSSL_PARAM params[4], *p = params;
 
-    /* initialize random_data here to make clean-up safe */
-    random_data.length = 0;
-    random_data.data = NULL;
+    ret = alloc_data(out, len);
+    if (ret)
+        goto cleanup;
 
-    /* allocate and initialize the key block */
-    key_block->magic = 0;
-    key_block->enctype = enctype;
-    if (0 != (retval = krb5_c_keylengths(context, enctype, &rand_len,
-                                         &key_len)))
+    kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL);
+    if (kdf == NULL) {
+        ret = oerr(context, KRB5_CRYPTO_INTERNAL, _("Failed to fetch SSKDF"));
         goto cleanup;
+    }
 
-    random_data.length = rand_len;
-    key_block->length = key_len;
+    kctx = EVP_KDF_CTX_new(kdf);
+    if (!kctx) {
+        ret = oerr(context, KRB5_CRYPTO_INTERNAL,
+                   _("Failed to instantiate SSKDF"));
+        goto cleanup;
+    }
 
-    if (NULL == (key_block->contents = malloc(key_block->length))) {
-        retval = ENOMEM;
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+                                            (char *)EVP_MD_get0_name(md), 0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                             key->data, key->length);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+                                             info->data, info->length);
+    *p = OSSL_PARAM_construct_end();
+    if (EVP_KDF_derive(kctx, (uint8_t *)out->data, len, params) <= 0) {
+        ret = oerr(context, KRB5_CRYPTO_INTERNAL,
+                   _("Failed to derive key using SSKDF"));
         goto cleanup;
     }
 
-    memset (key_block->contents, 0, key_block->length);
+    ret = 0;
 
-    /* If this is anonymous pkinit, use the anonymous principle for party_u_info */
-    if (party_u_info && krb5_principal_compare_any_realm(context, party_u_info,
-                                                         krb5_anonymous_principal()))
-        party_u_info = (krb5_principal)krb5_anonymous_principal();
+cleanup:
+    EVP_KDF_free(kdf);
+    EVP_KDF_CTX_free(kctx);
+    return ret;
+}
 
-    if (0 != (retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func)))
-        goto cleanup;
+#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+#define sskdf builtin_sskdf
+static krb5_error_code
+builtin_sskdf(krb5_context context, const EVP_MD *md, krb5_data *key,
+              krb5_data *info, size_t len, krb5_data *out)
+{
+    krb5_error_code ret;
+    uint32_t counter = 1, reps;
+    uint8_t be_counter[4], *outptr;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned int s, hash_len;
 
-    /* 1.  reps = keydatalen (K) / hash length (H) */
-    reps = key_block->length/hash_len;
+    hash_len = EVP_MD_size(md);
 
-    /* ... and round up, if necessary */
-    if (key_block->length > (reps * hash_len))
-        reps++;
+    /* 1.  reps = keydatalen (K) / hash length (H) rounded up. */
+    reps = (len + hash_len - 1) / hash_len;
 
     /* Allocate enough space in the random data buffer to hash directly into
      * it, even if the last hash will make it bigger than the key length. */
-    if (NULL == (random_data.data = malloc(reps * hash_len))) {
-        retval = ENOMEM;
-        goto cleanup;
-    }
-
-    /* Encode the ASN.1 octet string for "SuppPubInfo" */
-    supp_pub_info_fields.enctype = enctype;
-    supp_pub_info_fields.as_req = *as_req;
-    supp_pub_info_fields.pk_as_rep = *pk_as_rep;
-    if (0 != ((retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
-                                                         &supp_pub_info))))
-        goto cleanup;
-
-    /* Now encode the ASN.1 octet string for "OtherInfo" */
-    memset(&alg_id, 0, sizeof alg_id);
-    alg_id.algorithm = *alg_oid; /*alias*/
-
-    other_info_fields.algorithm_identifier = alg_id;
-    other_info_fields.party_u_info = (krb5_principal) party_u_info;
-    other_info_fields.party_v_info = (krb5_principal) party_v_info;
-    other_info_fields.supp_pub_info = *supp_pub_info;
-    if (0 != (retval = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info)))
+    ret = alloc_data(out, reps * hash_len);
+    if (ret)
         goto cleanup;
+    out->length = len;
 
-    /* 2.  Initialize a 32-bit, big-endian bit string counter as 1.
+    /*
+     * 2.  Initialize a 32-bit, big-endian bit string counter as 1.
      * 3.  For i = 1 to reps by 1, do the following:
      *     -   Compute Hashi = H(counter || Z || OtherInfo).
      *     -   Increment counter (modulo 2^32)
+     * 4.  Set key = Hash1 || Hash2 || ... so that length of key is K
+     *     bytes.
      */
+    outptr = (uint8_t *)out->data;
     for (counter = 1; counter <= reps; counter++) {
-        uint s = 0;
-        uint32_t be_counter = htonl(counter);
+        store_32_be(counter, be_counter);
 
         ctx = EVP_MD_CTX_new();
         if (ctx == NULL) {
-            retval = KRB5_CRYPTO_INTERNAL;
+            ret = KRB5_CRYPTO_INTERNAL;
             goto cleanup;
         }
 
         /* -   Compute Hashi = H(counter || Z || OtherInfo). */
-        if (!EVP_DigestInit(ctx, EVP_func())) {
-            krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
-                                   "Call to OpenSSL EVP_DigestInit() returned an error.");
-            retval = KRB5_CRYPTO_INTERNAL;
-            goto cleanup;
-        }
-
-        if (!EVP_DigestUpdate(ctx, &be_counter, 4) ||
-            !EVP_DigestUpdate(ctx, secret->data, secret->length) ||
-            !EVP_DigestUpdate(ctx, other_info->data, other_info->length)) {
-            krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
-                                   "Call to OpenSSL EVP_DigestUpdate() returned an error.");
-            retval = KRB5_CRYPTO_INTERNAL;
+        if (!EVP_DigestInit(ctx, md) ||
+            !EVP_DigestUpdate(ctx, be_counter, 4) ||
+            !EVP_DigestUpdate(ctx, key->data, key->length) ||
+            !EVP_DigestUpdate(ctx, info->data, info->length) ||
+            !EVP_DigestFinal(ctx, outptr, &s)) {
+            ret = oerr(context, KRB5_CRYPTO_INTERNAL,
+                       _("Failed to compute digest"));
             goto cleanup;
         }
 
-        /* 4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes. */
-        if (!EVP_DigestFinal(ctx, (uint8_t *)random_data.data + offset, &s)) {
-            krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
-                                   "Call to OpenSSL EVP_DigestUpdate() returned an error.");
-            retval = KRB5_CRYPTO_INTERNAL;
-            goto cleanup;
-        }
-        offset += s;
         assert(s == hash_len);
+        outptr += s;
 
         EVP_MD_CTX_free(ctx);
         ctx = NULL;
     }
 
-    retval = krb5_c_random_to_key(context, enctype, &random_data,
-                                  key_block);
-
 cleanup:
     EVP_MD_CTX_free(ctx);
+    return ret;
+}
 
-    /* If this has been an error, free the allocated key_block, if any */
-    if (retval) {
-        krb5_free_keyblock_contents(context, key_block);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+/* id-pkinit-kdf family, as specified by RFC 8636. */
+krb5_error_code
+pkinit_alg_agility_kdf(krb5_context context, krb5_data *secret,
+                       krb5_data *alg_oid, krb5_const_principal party_u_info,
+                       krb5_const_principal party_v_info,
+                       krb5_enctype enctype, krb5_data *as_req,
+                       krb5_data *pk_as_rep, krb5_keyblock *key_block)
+{
+    krb5_error_code ret;
+    size_t rand_len = 0, key_len = 0;
+    const EVP_MD *md;
+    krb5_sp80056a_other_info other_info_fields;
+    krb5_pkinit_supp_pub_info supp_pub_info_fields;
+    krb5_data *other_info = NULL, *supp_pub_info = NULL;
+    krb5_data random_data = empty_data();
+    krb5_algorithm_identifier alg_id;
+    char *hash_name = NULL;
+
+    ret = krb5_c_keylengths(context, enctype, &rand_len, &key_len);
+    if (ret)
+        goto cleanup;
+
+    /* Allocate and initialize the key block. */
+    key_block->magic = 0;
+    key_block->enctype = enctype;
+    key_block->length = key_len;
+    key_block->contents = k5calloc(key_block->length, 1, &ret);
+    if (key_block->contents == NULL)
+        goto cleanup;
+
+    /* If this is anonymous pkinit, use the anonymous principle for
+     * party_u_info. */
+    if (party_u_info &&
+        krb5_principal_compare_any_realm(context, party_u_info,
+                                         krb5_anonymous_principal())) {
+        party_u_info = (krb5_principal)krb5_anonymous_principal();
+    }
+
+    md = algid_to_md(alg_oid);
+    if (md == NULL) {
+        krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS,
+                               "Bad algorithm ID passed to PK-INIT KDF.");
+        return KRB5_ERR_BAD_S2K_PARAMS;
     }
 
-    /* free other allocated resources, either way */
-    if (random_data.data)
-        free(random_data.data);
+    /* Encode the ASN.1 octet string for "SuppPubInfo". */
+    supp_pub_info_fields.enctype = enctype;
+    supp_pub_info_fields.as_req = *as_req;
+    supp_pub_info_fields.pk_as_rep = *pk_as_rep;
+    ret = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
+                                           &supp_pub_info);
+    if (ret)
+        goto cleanup;
+
+    /* Now encode the ASN.1 octet string for "OtherInfo". */
+    memset(&alg_id, 0, sizeof(alg_id));
+    alg_id.algorithm = *alg_oid;
+    other_info_fields.algorithm_identifier = alg_id;
+    other_info_fields.party_u_info = (krb5_principal)party_u_info;
+    other_info_fields.party_v_info = (krb5_principal)party_v_info;
+    other_info_fields.supp_pub_info = *supp_pub_info;
+    ret = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info);
+    if (ret)
+        goto cleanup;
+
+    ret = sskdf(context, md, secret, other_info, rand_len, &random_data);
+    if (ret)
+        goto cleanup;
+
+    ret = krb5_c_random_to_key(context, enctype, &random_data, key_block);
+
+cleanup:
+    if (ret)
+        krb5_free_keyblock_contents(context, key_block);
+    free(hash_name);
+    zapfree(random_data.data, random_data.length);
     krb5_free_data(context, other_info);
     krb5_free_data(context, supp_pub_info);
-
-    return retval;
-} /*pkinit_alg_agility_kdf() */
+    return ret;
+}
 
 krb5_error_code
 client_create_dh(krb5_context context,
diff --git a/src/plugins/preauth/pkinit/pkinit_kdf_test.c b/src/plugins/preauth/pkinit/pkinit_kdf_test.c
index 5f60de9..7f38e84 100644
--- a/src/plugins/preauth/pkinit/pkinit_kdf_test.c
+++ b/src/plugins/preauth/pkinit/pkinit_kdf_test.c
@@ -127,8 +127,7 @@ main(int argc, char **argv)
 
     /* TEST 1:  SHA-1/AES */
     /* set up algorithm id */
-    alg_id.algorithm.data = (char *)krb5_pkinit_sha1_oid;
-    alg_id.algorithm.length = krb5_pkinit_sha1_oid_len;
+    alg_id.algorithm = sha1_id;
 
     enctype = enctype_aes;
 
@@ -138,7 +137,7 @@ main(int argc, char **argv)
                                               u_principal, v_principal,
                                               enctype, &as_req, &pk_as_rep,
                                               &key_block))) {
-        printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d",
+        printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n",
                retval);
         goto cleanup;
     }
@@ -159,8 +158,7 @@ main(int argc, char **argv)
 
     /* TEST 2: SHA-256/AES */
     /* set up algorithm id */
-    alg_id.algorithm.data = (char *)krb5_pkinit_sha256_oid;
-    alg_id.algorithm.length = krb5_pkinit_sha256_oid_len;
+    alg_id.algorithm = sha256_id;
 
     enctype = enctype_aes;
 
@@ -170,7 +168,7 @@ main(int argc, char **argv)
                                               u_principal, v_principal,
                                               enctype, &as_req, &pk_as_rep,
                                               &key_block))) {
-        printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d",
+        printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n",
                retval);
         goto cleanup;
     }
@@ -191,8 +189,7 @@ main(int argc, char **argv)
 
     /* TEST 3: SHA-512/DES3 */
     /* set up algorithm id */
-    alg_id.algorithm.data = (char *)krb5_pkinit_sha512_oid;
-    alg_id.algorithm.length = krb5_pkinit_sha512_oid_len;
+    alg_id.algorithm = sha512_id;
 
     enctype = enctype_des3;
 
@@ -202,7 +199,7 @@ main(int argc, char **argv)
                                               u_principal, v_principal,
                                               enctype, &as_req, &pk_as_rep,
                                               &key_block))) {
-        printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d",
+        printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n",
                retval);
         goto cleanup;
     }
@@ -225,5 +222,6 @@ cleanup:
     krb5_free_principal(context, u_principal);
     krb5_free_principal(context, v_principal);
     krb5_free_keyblock_contents(context, &key_block);
-    exit(retval);
+    krb5_free_context(context);
+    return retval ? 1 : 0;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.