krb5 commit: Use OpenSSL 3 versions of remaining KDFs

Greg Hudson ghudson at mit.edu
Mon Nov 15 15:36:11 EST 2021


https://github.com/krb5/krb5/commit/1c87ce6c44a9de0824580a2d72a8a202237e01f4
commit 1c87ce6c44a9de0824580a2d72a8a202237e01f4
Author: Robbie Harwood <rharwood at redhat.com>
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.
    
    [ghudson at mit.edu: 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;
 }


More information about the cvs-krb5 mailing list