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