Blame SOURCES/Use-OpenSSL-s-KBKDF-and-KRB5KDF-for-deriving-long-te.patch

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