ad88d4f
From 21e3b9a4463f1d1aeb71de8a27c298f1307d186b Mon Sep 17 00:00:00 2001
ad88d4f
From: Robbie Harwood <rharwood@redhat.com>
ad88d4f
Date: Fri, 4 Oct 2019 14:49:29 -0400
ad88d4f
Subject: [PATCH] Use OpenSSL's KBKDF and KRB5KDF for deriving long-term keys
ad88d4f
ad88d4f
If supported, use OpenSSL-provided KBKDF (aes-sha2 and camellia) and
ad88d4f
KRB5KDF (3des and aes-sha1).  We already use OpenSSL's PBKDF2 where
ad88d4f
appropriate.  OpenSSL added support for these KDFs in 3.0.
ad88d4f
ad88d4f
(cherry picked from commit ef8d11f6fb1232201c9efd2ae2ed567023fb85d2)
ad88d4f
[rharwood@redhat.com: 3des removal]
ad88d4f
---
ad88d4f
 src/lib/crypto/krb/derive.c | 409 ++++++++++++++++++++++++++++--------
ad88d4f
 1 file changed, 324 insertions(+), 85 deletions(-)
ad88d4f
ad88d4f
diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c
ad88d4f
index 6707a7308..8e474b38e 100644
ad88d4f
--- a/src/lib/crypto/krb/derive.c
ad88d4f
+++ b/src/lib/crypto/krb/derive.c
ad88d4f
@@ -27,6 +27,12 @@
ad88d4f
 
ad88d4f
 #include "crypto_int.h"
ad88d4f
 
ad88d4f
+#ifdef HAVE_EVP_KDF_FETCH
ad88d4f
+#include <openssl/core_names.h>
ad88d4f
+#include <openssl/evp.h>
ad88d4f
+#include <openssl/kdf.h>
ad88d4f
+#endif
ad88d4f
+
ad88d4f
 static krb5_key
ad88d4f
 find_cached_dkey(struct derived_key *list, const krb5_data *constant)
ad88d4f
 {
ad88d4f
@@ -77,55 +83,251 @@ cleanup:
ad88d4f
     return ENOMEM;
ad88d4f
 }
ad88d4f
 
ad88d4f
+#ifdef HAVE_EVP_KDF_FETCH
ad88d4f
 static krb5_error_code
ad88d4f
-derive_random_rfc3961(const struct krb5_enc_provider *enc,
ad88d4f
-                      krb5_key inkey, krb5_data *outrnd,
ad88d4f
-                      const krb5_data *in_constant)
ad88d4f
+openssl_kbdkf_counter_hmac(const struct krb5_hash_provider *hash,
ad88d4f
+                           krb5_key inkey, krb5_data *outrnd,
ad88d4f
+                           const krb5_data *label, const krb5_data *context)
ad88d4f
 {
ad88d4f
-    size_t blocksize, keybytes, n;
ad88d4f
     krb5_error_code ret;
ad88d4f
-    krb5_data block = empty_data();
ad88d4f
+    EVP_KDF *kdf = NULL;
ad88d4f
+    EVP_KDF_CTX *kctx = NULL;
ad88d4f
+    OSSL_PARAM params[6];
ad88d4f
+    size_t i = 0;
ad88d4f
+    char *digest;
ad88d4f
 
ad88d4f
-    blocksize = enc->block_size;
ad88d4f
-    keybytes = enc->keybytes;
ad88d4f
+    /* On NULL hash, preserve default behavior for pbkdf2_string_to_key(). */
ad88d4f
+    if (hash == NULL || !strcmp(hash->hash_name, "SHA1")) {
ad88d4f
+        digest = "SHA1";
ad88d4f
+    } else if (!strcmp(hash->hash_name, "SHA-256")) {
ad88d4f
+        digest = "SHA256";
ad88d4f
+    } else if (!strcmp(hash->hash_name, "SHA-384")) {
ad88d4f
+        digest = "SHA384";
ad88d4f
+    } else {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
 
ad88d4f
-    if (blocksize == 1)
ad88d4f
-        return KRB5_BAD_ENCTYPE;
ad88d4f
-    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
ad88d4f
+    kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL);
ad88d4f
+    if (!kdf) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    kctx = EVP_KDF_CTX_new(kdf);
ad88d4f
+    if (!kctx) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
ad88d4f
+                                                   digest, 0);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC,
ad88d4f
+                                                   "HMAC", 0);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
ad88d4f
+                                                    inkey->keyblock.contents,
ad88d4f
+                                                    inkey->keyblock.length);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
ad88d4f
+                                                    context->data,
ad88d4f
+                                                    context->length);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
ad88d4f
+                                                    label->data,
ad88d4f
+                                                    label->length);
ad88d4f
+    params[i] = OSSL_PARAM_construct_end();
ad88d4f
+    if (EVP_KDF_derive(kctx, (unsigned char *)outrnd->data, outrnd->length,
ad88d4f
+                       params) <= 0) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    ret = 0;
ad88d4f
+done:
ad88d4f
+    if (ret)
ad88d4f
+        zap(outrnd->data, outrnd->length);
ad88d4f
+    EVP_KDF_free(kdf);
ad88d4f
+    EVP_KDF_CTX_free(kctx);
ad88d4f
+    return ret;
ad88d4f
+}
ad88d4f
+
ad88d4f
+static krb5_error_code
ad88d4f
+openssl_kbkdf_feedback_cmac(const struct krb5_enc_provider *enc,
ad88d4f
+                            krb5_key inkey, krb5_data *outrnd,
ad88d4f
+                            const krb5_data *in_constant)
ad88d4f
+{
ad88d4f
+    krb5_error_code ret;
ad88d4f
+    EVP_KDF *kdf = NULL;
ad88d4f
+    EVP_KDF_CTX *kctx = NULL;
ad88d4f
+    OSSL_PARAM params[7];
ad88d4f
+    size_t i = 0;
ad88d4f
+    char *cipher;
ad88d4f
+    static unsigned char zeroes[16];
ad88d4f
+
ad88d4f
+    memset(zeroes, 0, sizeof(zeroes));
ad88d4f
+
ad88d4f
+    if (!memcmp(enc, &krb5int_enc_camellia128, sizeof(*enc))) {
ad88d4f
+        cipher = "CAMELLIA-128-CBC";
ad88d4f
+    } else if (!memcmp(enc, &krb5int_enc_camellia256, sizeof(*enc))) {
ad88d4f
+        cipher = "CAMELLIA-256-CBC";
ad88d4f
+    } else {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL);
ad88d4f
+    if (!kdf) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    kctx = EVP_KDF_CTX_new(kdf);
ad88d4f
+    if (!kctx) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MODE,
ad88d4f
+                                                   "FEEDBACK", 0);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC,
ad88d4f
+                                                   "CMAC", 0);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER,
ad88d4f
+                                                   cipher, 0);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
ad88d4f
+                                                    inkey->keyblock.contents,
ad88d4f
+                                                    inkey->keyblock.length);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
ad88d4f
+                                                    in_constant->data,
ad88d4f
+                                                    in_constant->length);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED,
ad88d4f
+                                                    zeroes, sizeof(zeroes));
ad88d4f
+    params[i] = OSSL_PARAM_construct_end();
ad88d4f
+    if (EVP_KDF_derive(kctx, (unsigned char *)outrnd->data, outrnd->length,
ad88d4f
+                       params) <= 0) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    ret = 0;
ad88d4f
+done:
ad88d4f
+    if (ret)
ad88d4f
+        zap(outrnd->data, outrnd->length);
ad88d4f
+    EVP_KDF_free(kdf);
ad88d4f
+    EVP_KDF_CTX_free(kctx);
ad88d4f
+    return ret;
ad88d4f
+}
ad88d4f
+
ad88d4f
+static krb5_error_code
ad88d4f
+openssl_krb5kdf(const struct krb5_enc_provider *enc, krb5_key inkey,
ad88d4f
+                krb5_data *outrnd, const krb5_data *in_constant)
ad88d4f
+{
ad88d4f
+    krb5_error_code ret;
ad88d4f
+    EVP_KDF *kdf = NULL;
ad88d4f
+    EVP_KDF_CTX *kctx = NULL;
ad88d4f
+    OSSL_PARAM params[4];
ad88d4f
+    size_t i = 0;
ad88d4f
+    char *cipher;
ad88d4f
+
ad88d4f
+    if (inkey->keyblock.length != enc->keylength ||
ad88d4f
+        outrnd->length != enc->keybytes) {
ad88d4f
+        return KRB5_CRYPTO_INTERNAL;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    if (!memcmp(enc, &krb5int_enc_aes128, sizeof(*enc))) {
ad88d4f
+        cipher = "AES-128-CBC";
ad88d4f
+    } else if (!memcmp(enc, &krb5int_enc_aes256, sizeof(*enc))) {
ad88d4f
+        cipher = "AES-256-CBC";
ad88d4f
+    } else {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    kdf = EVP_KDF_fetch(NULL, "KRB5KDF", NULL);
ad88d4f
+    if (kdf == NULL) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    kctx = EVP_KDF_CTX_new(kdf);
ad88d4f
+    if (kctx == NULL) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER,
ad88d4f
+                                                   cipher, 0);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
ad88d4f
+                                                    inkey->keyblock.contents,
ad88d4f
+                                                    inkey->keyblock.length);
ad88d4f
+    params[i++] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_CONSTANT,
ad88d4f
+                                                    in_constant->data,
ad88d4f
+                                                    in_constant->length);
ad88d4f
+    params[i] = OSSL_PARAM_construct_end();
ad88d4f
+    if (EVP_KDF_derive(kctx, (unsigned char *)outrnd->data, outrnd->length,
ad88d4f
+                       params) <= 0) {
ad88d4f
+        ret = KRB5_CRYPTO_INTERNAL;
ad88d4f
+        goto done;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    ret = 0;
ad88d4f
+done:
ad88d4f
+    if (ret)
ad88d4f
+        zap(outrnd->data, outrnd->length);
ad88d4f
+    EVP_KDF_free(kdf);
ad88d4f
+    EVP_KDF_CTX_free(kctx);
ad88d4f
+    return ret;
ad88d4f
+}
ad88d4f
+
ad88d4f
+#else /* HAVE_EVP_KDF_FETCH */
ad88d4f
+
ad88d4f
+/*
ad88d4f
+ * NIST SP800-108 KDF in counter mode (section 5.1).
ad88d4f
+ * Parameters:
ad88d4f
+ *   - HMAC (with hash as the hash provider) is the PRF.
ad88d4f
+ *   - A block counter of four bytes is used.
ad88d4f
+ *   - Four bytes are used to encode the output length in the PRF input.
ad88d4f
+ *
ad88d4f
+ * There are no uses requiring more than a single PRF invocation.
ad88d4f
+ */
ad88d4f
+static krb5_error_code
ad88d4f
+builtin_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
ad88d4f
+                               krb5_key inkey, krb5_data *outrnd,
ad88d4f
+                               const krb5_data *label,
ad88d4f
+                               const krb5_data *context)
ad88d4f
+{
ad88d4f
+    krb5_crypto_iov iov[5];
ad88d4f
+    krb5_error_code ret;
ad88d4f
+    krb5_data prf;
ad88d4f
+    unsigned char ibuf[4], lbuf[4];
ad88d4f
+
ad88d4f
+    if (hash == NULL || outrnd->length > hash->hashsize)
ad88d4f
         return KRB5_CRYPTO_INTERNAL;
ad88d4f
 
ad88d4f
     /* Allocate encryption data buffer. */
ad88d4f
-    ret = alloc_data(&block, blocksize);
ad88d4f
+    ret = alloc_data(&prf, hash->hashsize);
ad88d4f
     if (ret)
ad88d4f
         return ret;
ad88d4f
 
ad88d4f
-    /* Initialize the input block. */
ad88d4f
-    if (in_constant->length == blocksize) {
ad88d4f
-        memcpy(block.data, in_constant->data, blocksize);
ad88d4f
-    } else {
ad88d4f
-        krb5int_nfold(in_constant->length * 8,
ad88d4f
-                      (unsigned char *) in_constant->data,
ad88d4f
-                      blocksize * 8, (unsigned char *) block.data);
ad88d4f
-    }
ad88d4f
+    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
ad88d4f
+    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
+    iov[0].data = make_data(ibuf, sizeof(ibuf));
ad88d4f
+    store_32_be(1, ibuf);
ad88d4f
+    /* Label */
ad88d4f
+    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
+    iov[1].data = *label;
ad88d4f
+    /* 0x00: separator byte */
ad88d4f
+    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
+    iov[2].data = make_data("", 1);
ad88d4f
+    /* Context */
ad88d4f
+    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
+    iov[3].data = *context;
ad88d4f
+    /* [L]2: four-byte big-endian binary string giving the output length */
ad88d4f
+    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
+    iov[4].data = make_data(lbuf, sizeof(lbuf));
ad88d4f
+    store_32_be(outrnd->length * 8, lbuf);
ad88d4f
 
ad88d4f
-    /* Loop encrypting the blocks until enough key bytes are generated. */
ad88d4f
-    n = 0;
ad88d4f
-    while (n < keybytes) {
ad88d4f
-        ret = encrypt_block(enc, inkey, &block);
ad88d4f
-        if (ret)
ad88d4f
-            goto cleanup;
ad88d4f
-
ad88d4f
-        if ((keybytes - n) <= blocksize) {
ad88d4f
-            memcpy(outrnd->data + n, block.data, (keybytes - n));
ad88d4f
-            break;
ad88d4f
-        }
ad88d4f
-
ad88d4f
-        memcpy(outrnd->data + n, block.data, blocksize);
ad88d4f
-        n += blocksize;
ad88d4f
-    }
ad88d4f
-
ad88d4f
-cleanup:
ad88d4f
-    zapfree(block.data, blocksize);
ad88d4f
+    ret = krb5int_hmac(hash, inkey, iov, 5, &prf;;
ad88d4f
+    if (!ret)
ad88d4f
+        memcpy(outrnd->data, prf.data, outrnd->length);
ad88d4f
+    zapfree(prf.data, prf.length);
ad88d4f
     return ret;
ad88d4f
 }
ad88d4f
 
ad88d4f
@@ -139,9 +341,9 @@ cleanup:
ad88d4f
  *   - Four bytes are used to encode the output length in the PRF input.
ad88d4f
  */
ad88d4f
 static krb5_error_code
ad88d4f
-derive_random_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
ad88d4f
-                                      krb5_key inkey, krb5_data *outrnd,
ad88d4f
-                                      const krb5_data *in_constant)
ad88d4f
+builtin_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
ad88d4f
+                                krb5_key inkey, krb5_data *outrnd,
ad88d4f
+                                const krb5_data *in_constant)
ad88d4f
 {
ad88d4f
     size_t blocksize, keybytes, n;
ad88d4f
     krb5_crypto_iov iov[6];
ad88d4f
@@ -204,56 +406,94 @@ cleanup:
ad88d4f
     return ret;
ad88d4f
 }
ad88d4f
 
ad88d4f
-/*
ad88d4f
- * NIST SP800-108 KDF in counter mode (section 5.1).
ad88d4f
- * Parameters:
ad88d4f
- *   - HMAC (with hash as the hash provider) is the PRF.
ad88d4f
- *   - A block counter of four bytes is used.
ad88d4f
- *   - Four bytes are used to encode the output length in the PRF input.
ad88d4f
- *
ad88d4f
- * There are no uses requiring more than a single PRF invocation.
ad88d4f
- */
ad88d4f
+static krb5_error_code
ad88d4f
+builtin_derive_random_rfc3961(const struct krb5_enc_provider *enc,
ad88d4f
+                              krb5_key inkey, krb5_data *outrnd,
ad88d4f
+                              const krb5_data *in_constant)
ad88d4f
+{
ad88d4f
+    size_t blocksize, keybytes, n;
ad88d4f
+    krb5_error_code ret;
ad88d4f
+    krb5_data block = empty_data();
ad88d4f
+
ad88d4f
+    blocksize = enc->block_size;
ad88d4f
+    keybytes = enc->keybytes;
ad88d4f
+
ad88d4f
+    if (blocksize == 1)
ad88d4f
+        return KRB5_BAD_ENCTYPE;
ad88d4f
+    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
ad88d4f
+        return KRB5_CRYPTO_INTERNAL;
ad88d4f
+
ad88d4f
+    /* Allocate encryption data buffer. */
ad88d4f
+    ret = alloc_data(&block, blocksize);
ad88d4f
+    if (ret)
ad88d4f
+        return ret;
ad88d4f
+
ad88d4f
+    /* Initialize the input block. */
ad88d4f
+    if (in_constant->length == blocksize) {
ad88d4f
+        memcpy(block.data, in_constant->data, blocksize);
ad88d4f
+    } else {
ad88d4f
+        krb5int_nfold(in_constant->length * 8,
ad88d4f
+                      (unsigned char *) in_constant->data,
ad88d4f
+                      blocksize * 8, (unsigned char *) block.data);
ad88d4f
+    }
ad88d4f
+
ad88d4f
+    /* Loop encrypting the blocks until enough key bytes are generated. */
ad88d4f
+    n = 0;
ad88d4f
+    while (n < keybytes) {
ad88d4f
+        ret = encrypt_block(enc, inkey, &block);
ad88d4f
+        if (ret)
ad88d4f
+            goto cleanup;
ad88d4f
+
ad88d4f
+        if ((keybytes - n) <= blocksize) {
ad88d4f
+            memcpy(outrnd->data + n, block.data, (keybytes - n));
ad88d4f
+            break;
ad88d4f
+        }
ad88d4f
+
ad88d4f
+        memcpy(outrnd->data + n, block.data, blocksize);
ad88d4f
+        n += blocksize;
ad88d4f
+    }
ad88d4f
+
ad88d4f
+cleanup:
ad88d4f
+    zapfree(block.data, blocksize);
ad88d4f
+    return ret;
ad88d4f
+}
ad88d4f
+#endif /* HAVE_EVP_KDF_FETCH */
ad88d4f
+
ad88d4f
 krb5_error_code
ad88d4f
 k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
ad88d4f
                           krb5_key inkey, krb5_data *outrnd,
ad88d4f
                           const krb5_data *label, const krb5_data *context)
ad88d4f
 {
ad88d4f
-    krb5_crypto_iov iov[5];
ad88d4f
-    krb5_error_code ret;
ad88d4f
-    krb5_data prf;
ad88d4f
-    unsigned char ibuf[4], lbuf[4];
ad88d4f
+#ifdef HAVE_EVP_KDF_FETCH
ad88d4f
+    return openssl_kbdkf_counter_hmac(hash, inkey, outrnd, label, context);
ad88d4f
+#else
ad88d4f
+    return builtin_sp800_108_counter_hmac(hash, inkey, outrnd, label,
ad88d4f
+                                          context);
ad88d4f
+#endif
ad88d4f
+}
ad88d4f
 
ad88d4f
-    if (hash == NULL || outrnd->length > hash->hashsize)
ad88d4f
-        return KRB5_CRYPTO_INTERNAL;
ad88d4f
+static krb5_error_code
ad88d4f
+sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
ad88d4f
+                           krb5_key inkey, krb5_data *outrnd,
ad88d4f
+                           const krb5_data *in_constant)
ad88d4f
+{
ad88d4f
+#ifdef HAVE_EVP_KDF_FETCH
ad88d4f
+    return openssl_kbkdf_feedback_cmac(enc, inkey, outrnd, in_constant);
ad88d4f
+#else
ad88d4f
+    return builtin_sp800_108_feedback_cmac(enc, inkey, outrnd, in_constant);
ad88d4f
+#endif
ad88d4f
+}
ad88d4f
 
ad88d4f
-    /* Allocate encryption data buffer. */
ad88d4f
-    ret = alloc_data(&prf, hash->hashsize);
ad88d4f
-    if (ret)
ad88d4f
-        return ret;
ad88d4f
-
ad88d4f
-    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
ad88d4f
-    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
-    iov[0].data = make_data(ibuf, sizeof(ibuf));
ad88d4f
-    store_32_be(1, ibuf);
ad88d4f
-    /* Label */
ad88d4f
-    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
-    iov[1].data = *label;
ad88d4f
-    /* 0x00: separator byte */
ad88d4f
-    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
-    iov[2].data = make_data("", 1);
ad88d4f
-    /* Context */
ad88d4f
-    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
-    iov[3].data = *context;
ad88d4f
-    /* [L]2: four-byte big-endian binary string giving the output length */
ad88d4f
-    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
ad88d4f
-    iov[4].data = make_data(lbuf, sizeof(lbuf));
ad88d4f
-    store_32_be(outrnd->length * 8, lbuf);
ad88d4f
-
ad88d4f
-    ret = krb5int_hmac(hash, inkey, iov, 5, &prf;;
ad88d4f
-    if (!ret)
ad88d4f
-        memcpy(outrnd->data, prf.data, outrnd->length);
ad88d4f
-    zapfree(prf.data, prf.length);
ad88d4f
-    return ret;
ad88d4f
+static krb5_error_code
ad88d4f
+derive_random_rfc3961(const struct krb5_enc_provider *enc,
ad88d4f
+                         krb5_key inkey, krb5_data *outrnd,
ad88d4f
+                         const krb5_data *in_constant)
ad88d4f
+{
ad88d4f
+#ifdef HAVE_EVP_KDF_FETCH
ad88d4f
+    return openssl_krb5kdf(enc, inkey, outrnd, in_constant);
ad88d4f
+#else
ad88d4f
+    return builtin_derive_random_rfc3961(enc, inkey, outrnd, in_constant);
ad88d4f
+#endif
ad88d4f
 }
ad88d4f
 
ad88d4f
 krb5_error_code
ad88d4f
@@ -268,8 +508,7 @@ krb5int_derive_random(const struct krb5_enc_provider *enc,
ad88d4f
     case DERIVE_RFC3961:
ad88d4f
         return derive_random_rfc3961(enc, inkey, outrnd, in_constant);
ad88d4f
     case DERIVE_SP800_108_CMAC:
ad88d4f
-        return derive_random_sp800_108_feedback_cmac(enc, inkey, outrnd,
ad88d4f
-                                                     in_constant);
ad88d4f
+        return sp800_108_feedback_cmac(enc, inkey, outrnd, in_constant);
ad88d4f
     case DERIVE_SP800_108_HMAC:
ad88d4f
         return k5_sp800_108_counter_hmac(hash, inkey, outrnd, in_constant,
ad88d4f
                                          &empty);