krb5/Use-OpenSSL-s-KBKDF-and-KRB5KDF-for-deriving-long-te.patch
Julien Rische d5c38cc27b Do not block KRB5KDF and MD4/5 in FIPS mode
Bypass OpenSSL's restrictions to use KRB5KDF in FIPS mode in case at
least one of AES SHA-1 HMAC encryption types are used.

Use OpenSSL 3.0 library context to access MD4 and MD5 lazily from
legacy provider if RADIUS is being used or RC4 encryption type is
enabled, without affecting global context.

Remove EVP_MD_CTX_FLAG_NON_FIPS_ALLOW flag since does not have any
effect anymore.

Such exceptions should not be allowed by the default FIPS crypto
policy.

Resolves: rhbz#2039684
Resolves: rhbz#2053135

Signed-off-by: Julien Rische <jrische@redhat.com>
2022-02-28 14:19:37 +01:00

486 lines
16 KiB
Diff

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