Blame SOURCES/Use-backported-version-of-OpenSSL-3-KDF-interface.patch

92593c
From 5e147f7f2924edfd278940dea8b1d8ed09d6872c Mon Sep 17 00:00:00 2001
92593c
From: Robbie Harwood <rharwood@redhat.com>
92593c
Date: Fri, 15 Nov 2019 20:05:16 +0000
92593c
Subject: [PATCH] Use backported version of OpenSSL-3 KDF interface
92593c
92593c
(cherry picked from commit 0e20daf7ccfe50518c89735c3dae2fde08d92325)
92593c
---
92593c
 src/configure.in                              |   4 +
92593c
 src/lib/crypto/krb/derive.c                   | 346 +++++++++++++-----
92593c
 .../preauth/pkinit/pkinit_crypto_openssl.c    | 257 +++++++------
92593c
 3 files changed, 423 insertions(+), 184 deletions(-)
92593c
92593c
diff --git a/src/configure.in b/src/configure.in
92593c
index 9f6b67b44..cf4b1139a 100644
92593c
--- a/src/configure.in
92593c
+++ b/src/configure.in
92593c
@@ -269,6 +269,10 @@ AC_SUBST(CRYPTO_IMPL)
92593c
 AC_SUBST(CRYPTO_IMPL_CFLAGS)
92593c
 AC_SUBST(CRYPTO_IMPL_LIBS)
92593c
 
92593c
+AC_CHECK_FUNCS(EVP_KDF_CTX_new_id EVP_KDF_ctrl EVP_KDF_derive,
92593c
+               AC_DEFINE(OSSL_KDFS, 1, [Define if using OpenSSL KDFs]),
92593c
+               AC_MSG_ERROR([backported OpenSSL KDFs not found]))
92593c
+
92593c
 AC_ARG_WITH([prng-alg],
92593c
 AC_HELP_STRING([--with-prng-alg=ALG], [use specified PRNG algorithm. @<:@fortuna@:>@]),
92593c
 [PRNG_ALG=$withval
92593c
diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c
92593c
index 6707a7308..915a173dd 100644
92593c
--- a/src/lib/crypto/krb/derive.c
92593c
+++ b/src/lib/crypto/krb/derive.c
92593c
@@ -27,6 +27,13 @@
92593c
 
92593c
 #include "crypto_int.h"
92593c
 
92593c
+#ifdef OSSL_KDFS
92593c
+#include <openssl/evp.h>
92593c
+#include <openssl/kdf.h>
92593c
+#else
92593c
+#error "Refusing to build without OpenSSL KDFs!"
92593c
+#endif
92593c
+
92593c
 static krb5_key
92593c
 find_cached_dkey(struct derived_key *list, const krb5_data *constant)
92593c
 {
92593c
@@ -77,55 +84,193 @@ cleanup:
92593c
     return ENOMEM;
92593c
 }
92593c
 
92593c
+#ifdef OSSL_KDFS
92593c
 static krb5_error_code
92593c
-derive_random_rfc3961(const struct krb5_enc_provider *enc,
92593c
-                      krb5_key inkey, krb5_data *outrnd,
92593c
-                      const krb5_data *in_constant)
92593c
+openssl_kbdkf_counter_hmac(const struct krb5_hash_provider *hash,
92593c
+                           krb5_key inkey, krb5_data *outrnd,
92593c
+                           const krb5_data *label, const krb5_data *context)
92593c
 {
92593c
-    size_t blocksize, keybytes, n;
92593c
-    krb5_error_code ret;
92593c
-    krb5_data block = empty_data();
92593c
+    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
92593c
+    EVP_KDF_CTX *ctx = NULL;
92593c
+    const EVP_MD *digest;
92593c
+
92593c
+    if (!strcmp(hash->hash_name, "SHA1"))
92593c
+        digest = EVP_sha1();
92593c
+    else if (!strcmp(hash->hash_name, "SHA-256"))
92593c
+        digest = EVP_sha256();
92593c
+    else if (!strcmp(hash->hash_name, "SHA-384"))
92593c
+        digest = EVP_sha384();
92593c
+    else
92593c
+        goto done;
92593c
+
92593c
+    ctx = EVP_KDF_CTX_new_id(EVP_KDF_KB);
92593c
+    if (!ctx)
92593c
+        goto done;
92593c
+
92593c
+    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, digest) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_MAC_TYPE,
92593c
+                     EVP_KDF_KB_MAC_TYPE_HMAC) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, inkey->keyblock.contents,
92593c
+                     inkey->keyblock.length) != 1 ||
92593c
+        (context->length > 0 &&
92593c
+         EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_INFO, context->data,
92593c
+                      context->length) != 1) ||
92593c
+        (label->length > 0 &&
92593c
+         EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SALT, label->data,
92593c
+                      label->length) != 1) ||
92593c
+        EVP_KDF_derive(ctx, (unsigned char *)outrnd->data,
92593c
+                       outrnd->length) != 1)
92593c
+        goto done;
92593c
+
92593c
+    ret = 0;
92593c
+done:
92593c
+    if (ret)
92593c
+        zap(outrnd->data, outrnd->length);
92593c
+    EVP_KDF_CTX_free(ctx);
92593c
+    return ret;
92593c
+}
92593c
 
92593c
-    blocksize = enc->block_size;
92593c
-    keybytes = enc->keybytes;
92593c
+static krb5_error_code
92593c
+openssl_kbkdf_feedback_cmac(const struct krb5_enc_provider *enc,
92593c
+                            krb5_key inkey, krb5_data *outrnd,
92593c
+                            const krb5_data *in_constant)
92593c
+{
92593c
+    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
92593c
+    EVP_KDF_CTX *ctx = NULL;
92593c
+    const EVP_CIPHER *cipher;
92593c
+    static unsigned char zeroes[16];
92593c
+
92593c
+    memset(zeroes, 0, sizeof(zeroes));
92593c
+
92593c
+    if (enc->keylength == 16)
92593c
+        cipher = EVP_camellia_128_cbc();
92593c
+    else if (enc->keylength == 32)
92593c
+        cipher = EVP_camellia_256_cbc();
92593c
+    else
92593c
+        goto done;
92593c
+
92593c
+    ctx = EVP_KDF_CTX_new_id(EVP_KDF_KB);
92593c
+    if (!ctx)
92593c
+        goto done;
92593c
+
92593c
+    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_MODE,
92593c
+                     EVP_KDF_KB_MODE_FEEDBACK) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_MAC_TYPE,
92593c
+                     EVP_KDF_KB_MAC_TYPE_CMAC) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_CIPHER, cipher) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, inkey->keyblock.contents,
92593c
+                     inkey->keyblock.length) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SALT, in_constant->data,
92593c
+                     in_constant->length) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KB_SEED, zeroes,
92593c
+                     sizeof(zeroes)) != 1 ||
92593c
+        EVP_KDF_derive(ctx, (unsigned char *)outrnd->data,
92593c
+                       outrnd->length) != 1)
92593c
+        goto done;
92593c
+
92593c
+    ret = 0;
92593c
+done:
92593c
+    if (ret)
92593c
+        zap(outrnd->data, outrnd->length);
92593c
+    EVP_KDF_CTX_free(ctx);
92593c
+    return ret;
92593c
+}
92593c
 
92593c
-    if (blocksize == 1)
92593c
-        return KRB5_BAD_ENCTYPE;
92593c
-    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
92593c
+static krb5_error_code
92593c
+openssl_krb5kdf(const struct krb5_enc_provider *enc, krb5_key inkey,
92593c
+                krb5_data *outrnd, const krb5_data *in_constant)
92593c
+{
92593c
+    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
92593c
+    EVP_KDF_CTX *ctx = NULL;
92593c
+    const EVP_CIPHER *cipher;
92593c
+
92593c
+    if (inkey->keyblock.length != enc->keylength ||
92593c
+        outrnd->length != enc->keybytes) {
92593c
         return KRB5_CRYPTO_INTERNAL;
92593c
+    }
92593c
 
92593c
-    /* Allocate encryption data buffer. */
92593c
-    ret = alloc_data(&block, blocksize);
92593c
+    if (enc->encrypt == krb5int_aes_encrypt && enc->keylength == 16)
92593c
+        cipher = EVP_aes_128_cbc();
92593c
+    else if (enc->encrypt == krb5int_aes_encrypt && enc->keylength == 32)
92593c
+        cipher = EVP_aes_256_cbc();
92593c
+    else if (enc->keylength == 24)
92593c
+        cipher = EVP_des_ede3_cbc();
92593c
+    else
92593c
+        goto done;
92593c
+
92593c
+    ctx = EVP_KDF_CTX_new_id(EVP_KDF_KRB5KDF);
92593c
+    if (ctx == NULL)
92593c
+        goto done;
92593c
+
92593c
+    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_CIPHER, cipher) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, inkey->keyblock.contents,
92593c
+                     inkey->keyblock.length) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KRB5KDF_CONSTANT,
92593c
+                     in_constant->data, in_constant->length) != 1 ||
92593c
+        EVP_KDF_derive(ctx, (unsigned char *)outrnd->data,
92593c
+                       outrnd->length) != 1)
92593c
+        goto done;
92593c
+
92593c
+    ret = 0;
92593c
+done:
92593c
     if (ret)
92593c
-        return ret;
92593c
+        zap(outrnd->data, outrnd->length);
92593c
+    EVP_KDF_CTX_free(ctx);
92593c
+    return ret;
92593c
+}
92593c
 
92593c
-    /* Initialize the input block. */
92593c
-    if (in_constant->length == blocksize) {
92593c
-        memcpy(block.data, in_constant->data, blocksize);
92593c
-    } else {
92593c
-        krb5int_nfold(in_constant->length * 8,
92593c
-                      (unsigned char *) in_constant->data,
92593c
-                      blocksize * 8, (unsigned char *) block.data);
92593c
-    }
92593c
+#else /* OSSL_KDFS */
92593c
 
92593c
-    /* Loop encrypting the blocks until enough key bytes are generated. */
92593c
-    n = 0;
92593c
-    while (n < keybytes) {
92593c
-        ret = encrypt_block(enc, inkey, &block);
92593c
-        if (ret)
92593c
-            goto cleanup;
92593c
+/*
92593c
+ * NIST SP800-108 KDF in counter mode (section 5.1).
92593c
+ * Parameters:
92593c
+ *   - HMAC (with hash as the hash provider) is the PRF.
92593c
+ *   - A block counter of four bytes is used.
92593c
+ *   - Four bytes are used to encode the output length in the PRF input.
92593c
+ *
92593c
+ * There are no uses requiring more than a single PRF invocation.
92593c
+ */
92593c
+static krb5_error_code
92593c
+builtin_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
92593c
+                               krb5_key inkey, krb5_data *outrnd,
92593c
+                               const krb5_data *label,
92593c
+                               const krb5_data *context)
92593c
+{
92593c
+    krb5_crypto_iov iov[5];
92593c
+    krb5_error_code ret;
92593c
+    krb5_data prf;
92593c
+    unsigned char ibuf[4], lbuf[4];
92593c
 
92593c
-        if ((keybytes - n) <= blocksize) {
92593c
-            memcpy(outrnd->data + n, block.data, (keybytes - n));
92593c
-            break;
92593c
-        }
92593c
+    if (hash == NULL || outrnd->length > hash->hashsize)
92593c
+        return KRB5_CRYPTO_INTERNAL;
92593c
 
92593c
-        memcpy(outrnd->data + n, block.data, blocksize);
92593c
-        n += blocksize;
92593c
-    }
92593c
+    /* Allocate encryption data buffer. */
92593c
+    ret = alloc_data(&prf, hash->hashsize);
92593c
+    if (ret)
92593c
+        return ret;
92593c
 
92593c
-cleanup:
92593c
-    zapfree(block.data, blocksize);
92593c
+    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
92593c
+    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
+    iov[0].data = make_data(ibuf, sizeof(ibuf));
92593c
+    store_32_be(1, ibuf);
92593c
+    /* Label */
92593c
+    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
+    iov[1].data = *label;
92593c
+    /* 0x00: separator byte */
92593c
+    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
+    iov[2].data = make_data("", 1);
92593c
+    /* Context */
92593c
+    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
+    iov[3].data = *context;
92593c
+    /* [L]2: four-byte big-endian binary string giving the output length */
92593c
+    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
+    iov[4].data = make_data(lbuf, sizeof(lbuf));
92593c
+    store_32_be(outrnd->length * 8, lbuf);
92593c
+
92593c
+    ret = krb5int_hmac(hash, inkey, iov, 5, &prf;;
92593c
+    if (!ret)
92593c
+        memcpy(outrnd->data, prf.data, outrnd->length);
92593c
+    zapfree(prf.data, prf.length);
92593c
     return ret;
92593c
 }
92593c
 
92593c
@@ -139,9 +284,9 @@ cleanup:
92593c
  *   - Four bytes are used to encode the output length in the PRF input.
92593c
  */
92593c
 static krb5_error_code
92593c
-derive_random_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
92593c
-                                      krb5_key inkey, krb5_data *outrnd,
92593c
-                                      const krb5_data *in_constant)
92593c
+builtin_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
92593c
+                                krb5_key inkey, krb5_data *outrnd,
92593c
+                                const krb5_data *in_constant)
92593c
 {
92593c
     size_t blocksize, keybytes, n;
92593c
     krb5_crypto_iov iov[6];
92593c
@@ -204,57 +349,95 @@ cleanup:
92593c
     return ret;
92593c
 }
92593c
 
92593c
-/*
92593c
- * NIST SP800-108 KDF in counter mode (section 5.1).
92593c
- * Parameters:
92593c
- *   - HMAC (with hash as the hash provider) is the PRF.
92593c
- *   - A block counter of four bytes is used.
92593c
- *   - Four bytes are used to encode the output length in the PRF input.
92593c
- *
92593c
- * There are no uses requiring more than a single PRF invocation.
92593c
- */
92593c
-krb5_error_code
92593c
-k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
92593c
-                          krb5_key inkey, krb5_data *outrnd,
92593c
-                          const krb5_data *label, const krb5_data *context)
92593c
+static krb5_error_code
92593c
+builtin_derive_random_rfc3961(const struct krb5_enc_provider *enc,
92593c
+                              krb5_key inkey, krb5_data *outrnd,
92593c
+                              const krb5_data *in_constant)
92593c
 {
92593c
-    krb5_crypto_iov iov[5];
92593c
+    size_t blocksize, keybytes, n;
92593c
     krb5_error_code ret;
92593c
-    krb5_data prf;
92593c
-    unsigned char ibuf[4], lbuf[4];
92593c
+    krb5_data block = empty_data();
92593c
 
92593c
-    if (hash == NULL || outrnd->length > hash->hashsize)
92593c
+    blocksize = enc->block_size;
92593c
+    keybytes = enc->keybytes;
92593c
+
92593c
+    if (blocksize == 1)
92593c
+        return KRB5_BAD_ENCTYPE;
92593c
+    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
92593c
         return KRB5_CRYPTO_INTERNAL;
92593c
 
92593c
     /* Allocate encryption data buffer. */
92593c
-    ret = alloc_data(&prf, hash->hashsize);
92593c
+    ret = alloc_data(&block, blocksize);
92593c
     if (ret)
92593c
         return ret;
92593c
 
92593c
-    /* [i]2: four-byte big-endian binary string giving the block counter (1) */
92593c
-    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
-    iov[0].data = make_data(ibuf, sizeof(ibuf));
92593c
-    store_32_be(1, ibuf);
92593c
-    /* Label */
92593c
-    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
-    iov[1].data = *label;
92593c
-    /* 0x00: separator byte */
92593c
-    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
-    iov[2].data = make_data("", 1);
92593c
-    /* Context */
92593c
-    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
-    iov[3].data = *context;
92593c
-    /* [L]2: four-byte big-endian binary string giving the output length */
92593c
-    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
92593c
-    iov[4].data = make_data(lbuf, sizeof(lbuf));
92593c
-    store_32_be(outrnd->length * 8, lbuf);
92593c
+    /* Initialize the input block. */
92593c
+    if (in_constant->length == blocksize) {
92593c
+        memcpy(block.data, in_constant->data, blocksize);
92593c
+    } else {
92593c
+        krb5int_nfold(in_constant->length * 8,
92593c
+                      (unsigned char *) in_constant->data,
92593c
+                      blocksize * 8, (unsigned char *) block.data);
92593c
+    }
92593c
 
92593c
-    ret = krb5int_hmac(hash, inkey, iov, 5, &prf;;
92593c
-    if (!ret)
92593c
-        memcpy(outrnd->data, prf.data, outrnd->length);
92593c
-    zapfree(prf.data, prf.length);
92593c
+    /* Loop encrypting the blocks until enough key bytes are generated. */
92593c
+    n = 0;
92593c
+    while (n < keybytes) {
92593c
+        ret = encrypt_block(enc, inkey, &block);
92593c
+        if (ret)
92593c
+            goto cleanup;
92593c
+
92593c
+        if ((keybytes - n) <= blocksize) {
92593c
+            memcpy(outrnd->data + n, block.data, (keybytes - n));
92593c
+            break;
92593c
+        }
92593c
+
92593c
+        memcpy(outrnd->data + n, block.data, blocksize);
92593c
+        n += blocksize;
92593c
+    }
92593c
+
92593c
+cleanup:
92593c
+    zapfree(block.data, blocksize);
92593c
     return ret;
92593c
 }
92593c
+#endif /* OSSL_KDFS */
92593c
+
92593c
+krb5_error_code
92593c
+k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
92593c
+                          krb5_key inkey, krb5_data *outrnd,
92593c
+                          const krb5_data *label, const krb5_data *context)
92593c
+{
92593c
+#ifdef OSSL_KDFS
92593c
+    return openssl_kbdkf_counter_hmac(hash, inkey, outrnd, label, context);
92593c
+#else
92593c
+    return builtin_sp800_108_counter_hmac(hash, inkey, outrnd, label,
92593c
+                                          context);
92593c
+#endif
92593c
+}
92593c
+
92593c
+static krb5_error_code
92593c
+k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
92593c
+                           krb5_key inkey, krb5_data *outrnd,
92593c
+                           const krb5_data *in_constant)
92593c
+{
92593c
+#ifdef OSSL_KDFS
92593c
+    return openssl_kbkdf_feedback_cmac(enc, inkey, outrnd, in_constant);
92593c
+#else
92593c
+    return builtin_sp800_108_feedback_cmac(enc, inkey, outrnd, in_constant);
92593c
+#endif
92593c
+}
92593c
+
92593c
+static krb5_error_code
92593c
+k5_derive_random_rfc3961(const struct krb5_enc_provider *enc,
92593c
+                         krb5_key inkey, krb5_data *outrnd,
92593c
+                         const krb5_data *in_constant)
92593c
+{
92593c
+#ifdef OSSL_KDFS
92593c
+    return openssl_krb5kdf(enc, inkey, outrnd, in_constant);
92593c
+#else
92593c
+    return builtin_derive_random_rfc3961(enc, inkey, outrnd, in_constant);
92593c
+#endif
92593c
+}
92593c
 
92593c
 krb5_error_code
92593c
 krb5int_derive_random(const struct krb5_enc_provider *enc,
92593c
@@ -266,10 +449,9 @@ krb5int_derive_random(const struct krb5_enc_provider *enc,
92593c
 
92593c
     switch (alg) {
92593c
     case DERIVE_RFC3961:
92593c
-        return derive_random_rfc3961(enc, inkey, outrnd, in_constant);
92593c
+        return k5_derive_random_rfc3961(enc, inkey, outrnd, in_constant);
92593c
     case DERIVE_SP800_108_CMAC:
92593c
-        return derive_random_sp800_108_feedback_cmac(enc, inkey, outrnd,
92593c
-                                                     in_constant);
92593c
+        return k5_sp800_108_feedback_cmac(enc, inkey, outrnd, in_constant);
92593c
     case DERIVE_SP800_108_HMAC:
92593c
         return k5_sp800_108_counter_hmac(hash, inkey, outrnd, in_constant,
92593c
                                          &empty);
92593c
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
92593c
index 5ff81d8cf..8d2c230c8 100644
92593c
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
92593c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
92593c
@@ -38,6 +38,13 @@
92593c
 #include <dirent.h>
92593c
 #include <arpa/inet.h>
92593c
 
92593c
+#ifdef OSSL_KDFS
92593c
+#include <openssl/evp.h>
92593c
+#include <openssl/kdf.h>
92593c
+#else
92593c
+#error "Refusing to build without OpenSSL KDFs!"
92593c
+#endif
92593c
+
92593c
 static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );
92593c
 static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context );
92593c
 
92593c
@@ -2460,11 +2467,51 @@ pkinit_alg_values(krb5_context context,
92593c
     }
92593c
 } /* pkinit_alg_values() */
92593c
 
92593c
+#ifdef OSSL_KDFS
92593c
+static krb5_error_code
92593c
+openssl_sskdf(krb5_context context, size_t hash_bytes, krb5_data *key,
92593c
+              krb5_data *info, char *out, size_t out_len)
92593c
+{
92593c
+    krb5_error_code ret = KRB5_CRYPTO_INTERNAL;
92593c
+    EVP_KDF_CTX *ctx = NULL;
92593c
+    const EVP_MD *digest;
92593c
+
92593c
+    /* RFC 8636 defines a SHA384 variant, but we don't use it. */
92593c
+    if (hash_bytes == 20) {
92593c
+        digest = EVP_sha1();
92593c
+    } else if (hash_bytes == 32) {
92593c
+        digest = EVP_sha256();
92593c
+    } else if (hash_bytes == 64) {
92593c
+        digest = EVP_sha512();
92593c
+    } else {
92593c
+        krb5_set_error_message(context, ret, "Bad hash type for SSKDF");
92593c
+        goto done;
92593c
+    }
92593c
 
92593c
-/* pkinit_alg_agility_kdf() --
92593c
- * This function generates a key using the KDF described in
92593c
- * draft_ietf_krb_wg_pkinit_alg_agility-04.txt.  The algorithm is
92593c
- * described as follows:
92593c
+    ctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
92593c
+    if (!ctx) {
92593c
+        oerr(context, ret, _("Failed to instantiate SSKDF"));
92593c
+        goto done;
92593c
+    }
92593c
+
92593c
+    if (EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, digest) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, key->data,
92593c
+                     key->length) != 1 ||
92593c
+        EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSKDF_INFO, info->data,
92593c
+                     info->length) != 1 ||
92593c
+        EVP_KDF_derive(ctx, (unsigned char *)out, out_len) != 1)
92593c
+        goto done;
92593c
+
92593c
+    ret = 0;
92593c
+done:
92593c
+    EVP_KDF_CTX_free(ctx);
92593c
+    return ret;
92593c
+}
92593c
+#else
92593c
+/*
92593c
+ * Generate a key using the KDF described in RFC 8636, also known as SSKDF
92593c
+ * (single-step kdf).  Our caller precomputes `reps`, but otherwise the
92593c
+ * algorithm is as follows:
92593c
  *
92593c
  *     1.  reps = keydatalen (K) / hash length (H)
92593c
  *
92593c
@@ -2478,95 +2525,16 @@ pkinit_alg_values(krb5_context context,
92593c
  *
92593c
  *     4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes.
92593c
  */
92593c
-krb5_error_code
92593c
-pkinit_alg_agility_kdf(krb5_context context,
92593c
-                       krb5_data *secret,
92593c
-                       krb5_data *alg_oid,
92593c
-                       krb5_const_principal party_u_info,
92593c
-                       krb5_const_principal party_v_info,
92593c
-                       krb5_enctype enctype,
92593c
-                       krb5_data *as_req,
92593c
-                       krb5_data *pk_as_rep,
92593c
-                       krb5_keyblock *key_block)
92593c
+static krb5_error_code
92593c
+builtin_sskdf(krb5_context context, unsigned int reps, size_t hash_len,
92593c
+              const EVP_MD *(*EVP_func)(void), krb5_data *secret,
92593c
+              krb5_data *other_info, char *out, size_t out_len)
92593c
 {
92593c
     krb5_error_code retval = 0;
92593c
 
92593c
-    unsigned int reps = 0;
92593c
-    uint32_t counter = 1;       /* Does this type work on Windows? */
92593c
+    uint32_t counter = 1;
92593c
     size_t offset = 0;
92593c
-    size_t hash_len = 0;
92593c
-    size_t rand_len = 0;
92593c
-    size_t key_len = 0;
92593c
-    krb5_data random_data;
92593c
-    krb5_sp80056a_other_info other_info_fields;
92593c
-    krb5_pkinit_supp_pub_info supp_pub_info_fields;
92593c
-    krb5_data *other_info = NULL;
92593c
-    krb5_data *supp_pub_info = NULL;
92593c
-    krb5_algorithm_identifier alg_id;
92593c
     EVP_MD_CTX *ctx = NULL;
92593c
-    const EVP_MD *(*EVP_func)(void);
92593c
-
92593c
-    /* initialize random_data here to make clean-up safe */
92593c
-    random_data.length = 0;
92593c
-    random_data.data = NULL;
92593c
-
92593c
-    /* allocate and initialize the key block */
92593c
-    key_block->magic = 0;
92593c
-    key_block->enctype = enctype;
92593c
-    if (0 != (retval = krb5_c_keylengths(context, enctype, &rand_len,
92593c
-                                         &key_len)))
92593c
-        goto cleanup;
92593c
-
92593c
-    random_data.length = rand_len;
92593c
-    key_block->length = key_len;
92593c
-
92593c
-    if (NULL == (key_block->contents = malloc(key_block->length))) {
92593c
-        retval = ENOMEM;
92593c
-        goto cleanup;
92593c
-    }
92593c
-
92593c
-    memset (key_block->contents, 0, key_block->length);
92593c
-
92593c
-    /* If this is anonymous pkinit, use the anonymous principle for party_u_info */
92593c
-    if (party_u_info && krb5_principal_compare_any_realm(context, party_u_info,
92593c
-                                                         krb5_anonymous_principal()))
92593c
-        party_u_info = (krb5_principal)krb5_anonymous_principal();
92593c
-
92593c
-    if (0 != (retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func)))
92593c
-        goto cleanup;
92593c
-
92593c
-    /* 1.  reps = keydatalen (K) / hash length (H) */
92593c
-    reps = key_block->length/hash_len;
92593c
-
92593c
-    /* ... and round up, if necessary */
92593c
-    if (key_block->length > (reps * hash_len))
92593c
-        reps++;
92593c
-
92593c
-    /* Allocate enough space in the random data buffer to hash directly into
92593c
-     * it, even if the last hash will make it bigger than the key length. */
92593c
-    if (NULL == (random_data.data = malloc(reps * hash_len))) {
92593c
-        retval = ENOMEM;
92593c
-        goto cleanup;
92593c
-    }
92593c
-
92593c
-    /* Encode the ASN.1 octet string for "SuppPubInfo" */
92593c
-    supp_pub_info_fields.enctype = enctype;
92593c
-    supp_pub_info_fields.as_req = *as_req;
92593c
-    supp_pub_info_fields.pk_as_rep = *pk_as_rep;
92593c
-    if (0 != ((retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
92593c
-                                                         &supp_pub_info))))
92593c
-        goto cleanup;
92593c
-
92593c
-    /* Now encode the ASN.1 octet string for "OtherInfo" */
92593c
-    memset(&alg_id, 0, sizeof alg_id);
92593c
-    alg_id.algorithm = *alg_oid; /*alias*/
92593c
-
92593c
-    other_info_fields.algorithm_identifier = alg_id;
92593c
-    other_info_fields.party_u_info = (krb5_principal) party_u_info;
92593c
-    other_info_fields.party_v_info = (krb5_principal) party_v_info;
92593c
-    other_info_fields.supp_pub_info = *supp_pub_info;
92593c
-    if (0 != (retval = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info)))
92593c
-        goto cleanup;
92593c
 
92593c
     /* 2.  Initialize a 32-bit, big-endian bit string counter as 1.
92593c
      * 3.  For i = 1 to reps by 1, do the following:
92593c
@@ -2600,8 +2568,9 @@ pkinit_alg_agility_kdf(krb5_context context,
92593c
             goto cleanup;
92593c
         }
92593c
 
92593c
-        /* 4.  Set key = Hash1 || Hash2 || ... so that length of key is K bytes. */
92593c
-        if (!EVP_DigestFinal(ctx, (uint8_t *)random_data.data + offset, &s)) {
92593c
+        /* 4.  Set key = Hash1 || Hash2 || ... so that length of key is K
92593c
+         * bytes. */
92593c
+        if (!EVP_DigestFinal(ctx, (unsigned char *)out + offset, &s)) {
92593c
             krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
92593c
                                    "Call to OpenSSL EVP_DigestUpdate() returned an error.");
92593c
             retval = KRB5_CRYPTO_INTERNAL;
92593c
@@ -2613,26 +2582,110 @@ pkinit_alg_agility_kdf(krb5_context context,
92593c
         EVP_MD_CTX_free(ctx);
92593c
         ctx = NULL;
92593c
     }
92593c
-
92593c
-    retval = krb5_c_random_to_key(context, enctype, &random_data,
92593c
-                                  key_block);
92593c
-
92593c
 cleanup:
92593c
     EVP_MD_CTX_free(ctx);
92593c
+    return retval;
92593c
+} /* builtin_sskdf() */
92593c
+#endif /* OSSL_KDFS */
92593c
 
92593c
-    /* If this has been an error, free the allocated key_block, if any */
92593c
-    if (retval) {
92593c
-        krb5_free_keyblock_contents(context, key_block);
92593c
+/* id-pkinit-kdf family, as specified by RFC 8636. */
92593c
+krb5_error_code
92593c
+pkinit_alg_agility_kdf(krb5_context context, krb5_data *secret,
92593c
+                       krb5_data *alg_oid, krb5_const_principal party_u_info,
92593c
+                       krb5_const_principal party_v_info,
92593c
+                       krb5_enctype enctype, krb5_data *as_req,
92593c
+                       krb5_data *pk_as_rep, krb5_keyblock *key_block)
92593c
+{
92593c
+    krb5_error_code retval;
92593c
+    size_t hash_len = 0, rand_len = 0, key_len = 0;
92593c
+    const EVP_MD *(*EVP_func)(void);
92593c
+    krb5_sp80056a_other_info other_info_fields;
92593c
+    krb5_pkinit_supp_pub_info supp_pub_info_fields;
92593c
+    krb5_data *other_info = NULL, *supp_pub_info = NULL;
92593c
+    krb5_data random_data = empty_data();
92593c
+    krb5_algorithm_identifier alg_id;
92593c
+    unsigned int reps;
92593c
+
92593c
+    /* Allocate and initialize the key block. */
92593c
+    key_block->magic = 0;
92593c
+    key_block->enctype = enctype;
92593c
+
92593c
+    /* Use separate variables to avoid alignment restriction problems. */
92593c
+    retval = krb5_c_keylengths(context, enctype, &rand_len, &key_len);
92593c
+    if (retval)
92593c
+        goto cleanup;
92593c
+    random_data.length = rand_len;
92593c
+    key_block->length = key_len;
92593c
+
92593c
+    key_block->contents = k5calloc(key_block->length, 1, &retval);
92593c
+    if (key_block->contents == NULL)
92593c
+        goto cleanup;
92593c
+
92593c
+    /* If this is anonymous pkinit, use the anonymous principle for
92593c
+     * party_u_info. */
92593c
+    if (party_u_info &&
92593c
+        krb5_principal_compare_any_realm(context, party_u_info,
92593c
+                                         krb5_anonymous_principal())) {
92593c
+        party_u_info = (krb5_principal)krb5_anonymous_principal();
92593c
     }
92593c
 
92593c
-    /* free other allocated resources, either way */
92593c
-    if (random_data.data)
92593c
-        free(random_data.data);
92593c
+    retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func);
92593c
+    if (retval)
92593c
+        goto cleanup;
92593c
+
92593c
+    /* 1.  reps = keydatalen (K) / hash length (H) */
92593c
+    reps = key_block->length / hash_len;
92593c
+
92593c
+    /* ... and round up, if necessary. */
92593c
+    if (key_block->length > (reps * hash_len))
92593c
+        reps++;
92593c
+
92593c
+    /* Allocate enough space in the random data buffer to hash directly into
92593c
+     * it, even if the last hash will make it bigger than the key length. */
92593c
+    random_data.data = k5alloc(reps * hash_len, &retval);
92593c
+    if (random_data.data == NULL)
92593c
+        goto cleanup;
92593c
+
92593c
+    /* Encode the ASN.1 octet string for "SuppPubInfo". */
92593c
+    supp_pub_info_fields.enctype = enctype;
92593c
+    supp_pub_info_fields.as_req = *as_req;
92593c
+    supp_pub_info_fields.pk_as_rep = *pk_as_rep;
92593c
+    retval = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
92593c
+                                              &supp_pub_info);
92593c
+    if (retval)
92593c
+        goto cleanup;
92593c
+
92593c
+    /* Now encode the ASN.1 octet string for "OtherInfo". */
92593c
+    memset(&alg_id, 0, sizeof(alg_id));
92593c
+    alg_id.algorithm = *alg_oid;
92593c
+    other_info_fields.algorithm_identifier = alg_id;
92593c
+    other_info_fields.party_u_info = (krb5_principal)party_u_info;
92593c
+    other_info_fields.party_v_info = (krb5_principal)party_v_info;
92593c
+    other_info_fields.supp_pub_info = *supp_pub_info;
92593c
+    retval = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info);
92593c
+    if (retval)
92593c
+        goto cleanup;
92593c
+
92593c
+#ifdef OSSL_KDFS
92593c
+    retval = openssl_sskdf(context, hash_len, secret, other_info,
92593c
+                           random_data.data, key_block->length);
92593c
+#else
92593c
+    retval = builtin_sskdf(context, reps, hash_len, EVP_func, secret,
92593c
+                           other_info, random_data.data, key_block->length);
92593c
+#endif
92593c
+    if (retval)
92593c
+        goto cleanup;
92593c
+
92593c
+    retval = krb5_c_random_to_key(context, enctype, &random_data, key_block);
92593c
+cleanup:
92593c
+    if (retval)
92593c
+        krb5_free_keyblock_contents(context, key_block);
92593c
+
92593c
+    zapfree(random_data.data, random_data.length);
92593c
     krb5_free_data(context, other_info);
92593c
     krb5_free_data(context, supp_pub_info);
92593c
-
92593c
     return retval;
92593c
-} /*pkinit_alg_agility_kdf() */
92593c
+}
92593c
 
92593c
 /* Call DH_compute_key() and ensure that we left-pad short results instead of
92593c
  * leaving junk bytes at the end of the buffer. */