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

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