aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobbie Harwood <rharwood@redhat.com>2019-09-20 17:20:59 -0400
committerGreg Hudson <ghudson@mit.edu>2021-11-15 11:16:08 -0500
commit1c87ce6c44a9de0824580a2d72a8a202237e01f4 (patch)
tree702930b142ba8be976dc78f4f96cd96b2c3678b2
parentf35077bfc570205092eca2a9d44e50ce265622f4 (diff)
downloadkrb5-1c87ce6c44a9de0824580a2d72a8a202237e01f4.zip
krb5-1c87ce6c44a9de0824580a2d72a8a202237e01f4.tar.gz
krb5-1c87ce6c44a9de0824580a2d72a8a202237e01f4.tar.bz2
Use OpenSSL 3 versions of remaining KDFs
In OpenSSL's terminology, id-pkinit-kdf is an instance of SSKDF, AES-SHA2 and Camellia use KBKDF, and the KDF for DES3 and AES-SHA1 has been named KRB5KDF. Support for these KDFs was added in OpenSSL 3; we already supported the existing PBKDF2. [ghudson@mit.edu: reorganize into builtin and openssl versions of the file; detect hash and encryption provider identity using pointer equality like we do in the OpenSSL PBKDF implementation; add helpers for this translation; simplify and better refactor the PKINIT code; fix some latent pkinit_kdf_test.c bugs]
-rw-r--r--src/lib/crypto/builtin/Makefile.in3
-rw-r--r--src/lib/crypto/builtin/deps11
-rw-r--r--src/lib/crypto/builtin/kdf.c190
-rw-r--r--src/lib/crypto/krb/crypto_int.h43
-rw-r--r--src/lib/crypto/krb/derive.c188
-rw-r--r--src/lib/crypto/krb/prf_aes2.c2
-rw-r--r--src/lib/crypto/openssl/Makefile.in3
-rw-r--r--src/lib/crypto/openssl/deps11
-rw-r--r--src/lib/crypto/openssl/kdf.c237
-rw-r--r--src/plugins/preauth/pkinit/pkinit_constants.c22
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto.h9
-rw-r--r--src/plugins/preauth/pkinit/pkinit_crypto_openssl.c330
-rw-r--r--src/plugins/preauth/pkinit/pkinit_kdf_test.c18
13 files changed, 685 insertions, 382 deletions
diff --git a/src/lib/crypto/builtin/Makefile.in b/src/lib/crypto/builtin/Makefile.in
index 62ce32c..e874fd2 100644
--- a/src/lib/crypto/builtin/Makefile.in
+++ b/src/lib/crypto/builtin/Makefile.in
@@ -9,14 +9,17 @@ LOCALINCLUDES=-I$(srcdir)/../krb $(CRYPTO_IMPL_CFLAGS)
STLIBOBJS=\
hmac.o \
+ kdf.o \
pbkdf2.o
OBJS=\
$(OUTPRE)hmac.$(OBJEXT) \
+ $(OUTPRE)kdf.$(OBJEXT) \
$(OUTPRE)pbkdf2.$(OBJEXT)
SRCS=\
$(srcdir)/hmac.c \
+ $(srcdir)/kdf.c \
$(srcdir)/pbkdf2.c
STOBJLISTS= des/OBJS.ST md4/OBJS.ST \
diff --git a/src/lib/crypto/builtin/deps b/src/lib/crypto/builtin/deps
index 7b83a55..0b18a19 100644
--- a/src/lib/crypto/builtin/deps
+++ b/src/lib/crypto/builtin/deps
@@ -12,6 +12,17 @@ hmac.so hmac.po $(OUTPRE)hmac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
hmac.c
+kdf.so kdf.po $(OUTPRE)kdf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kdf.c
pbkdf2.so pbkdf2.po $(OUTPRE)pbkdf2.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
diff --git a/src/lib/crypto/builtin/kdf.c b/src/lib/crypto/builtin/kdf.c
new file mode 100644
index 0000000..8a6658b
--- /dev/null
+++ b/src/lib/crypto/builtin/kdf.c
@@ -0,0 +1,190 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "crypto_int.h"
+
+#ifdef K5_BUILTIN_KDF
+
+krb5_error_code
+k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+ krb5_key key, const krb5_data *label,
+ const krb5_data *context, krb5_data *rnd_out)
+{
+ krb5_crypto_iov iov[5];
+ krb5_error_code ret;
+ krb5_data prf;
+ unsigned char ibuf[4], lbuf[4];
+
+ if (hash == NULL || rnd_out->length > hash->hashsize)
+ return KRB5_CRYPTO_INTERNAL;
+
+ /* 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(rnd_out->length * 8, lbuf);
+
+ ret = krb5int_hmac(hash, key, iov, 5, &prf);
+ if (!ret)
+ memcpy(rnd_out->data, prf.data, rnd_out->length);
+ zapfree(prf.data, prf.length);
+ return ret;
+}
+
+krb5_error_code
+k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc, krb5_key key,
+ const krb5_data *label, krb5_data *rnd_out)
+{
+ size_t blocksize, keybytes, n;
+ krb5_crypto_iov iov[6];
+ krb5_error_code ret;
+ krb5_data prf;
+ unsigned int i;
+ unsigned char ibuf[4], Lbuf[4];
+
+ blocksize = enc->block_size;
+ keybytes = enc->keybytes;
+
+ if (key->keyblock.length != enc->keylength || rnd_out->length != keybytes)
+ return KRB5_CRYPTO_INTERNAL;
+
+ /* Allocate encryption data buffer. */
+ ret = alloc_data(&prf, blocksize);
+ if (ret)
+ return ret;
+
+ /* K(i-1): the previous block of PRF output, initially all-zeros. */
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data = prf;
+ /* [i]2: four-byte big-endian binary string giving the block counter */
+ iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[1].data = make_data(ibuf, sizeof(ibuf));
+ /* Label: the fixed derived-key input */
+ iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[2].data = *label;
+ /* 0x00: separator byte */
+ iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[3].data = make_data("", 1);
+ /* Context: (unused) */
+ iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[4].data = empty_data();
+ /* [L]2: four-byte big-endian binary string giving the output length */
+ iov[5].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[5].data = make_data(Lbuf, sizeof(Lbuf));
+ store_32_be(rnd_out->length * 8, Lbuf);
+
+ for (i = 1, n = 0; n < keybytes; i++) {
+ /* Update the block counter. */
+ store_32_be(i, ibuf);
+
+ /* Compute a CMAC checksum, storing the result into K(i-1). */
+ ret = krb5int_cmac_checksum(enc, key, iov, 6, &prf);
+ if (ret)
+ goto cleanup;
+
+ /* Copy the result into the appropriate part of the output buffer. */
+ if (keybytes - n <= blocksize) {
+ memcpy(rnd_out->data + n, prf.data, keybytes - n);
+ break;
+ }
+ memcpy(rnd_out->data + n, prf.data, blocksize);
+ n += blocksize;
+ }
+
+cleanup:
+ zapfree(prf.data, blocksize);
+ return ret;
+}
+
+krb5_error_code
+k5_derive_random_rfc3961(const struct krb5_enc_provider *enc, krb5_key key,
+ const krb5_data *constant, krb5_data *rnd_out)
+{
+ 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 (key->keyblock.length != enc->keylength || rnd_out->length != keybytes)
+ return KRB5_CRYPTO_INTERNAL;
+
+ /* Allocate encryption data buffer. */
+ ret = alloc_data(&block, blocksize);
+ if (ret)
+ return ret;
+
+ /* Initialize the input block. */
+ if (constant->length == blocksize) {
+ memcpy(block.data, constant->data, blocksize);
+ } else {
+ krb5int_nfold(constant->length * 8, (uint8_t *)constant->data,
+ blocksize * 8, (uint8_t *)block.data);
+ }
+
+ /* Loop encrypting the blocks until enough key bytes are generated. */
+ n = 0;
+ while (n < keybytes) {
+ ret = encrypt_block(enc, key, &block);
+ if (ret)
+ goto cleanup;
+
+ if ((keybytes - n) <= blocksize) {
+ memcpy(rnd_out->data + n, block.data, (keybytes - n));
+ break;
+ }
+
+ memcpy(rnd_out->data + n, block.data, blocksize);
+ n += blocksize;
+ }
+
+cleanup:
+ zapfree(block.data, blocksize);
+ return ret;
+}
+
+#endif /* K5_BUILTIN_KDF */
diff --git a/src/lib/crypto/krb/crypto_int.h b/src/lib/crypto/krb/crypto_int.h
index 82474aa..f7980ef 100644
--- a/src/lib/crypto/krb/crypto_int.h
+++ b/src/lib/crypto/krb/crypto_int.h
@@ -42,14 +42,19 @@
* library carries complications, so use the built-in implementations of these
* primitives instead. OpenSSL 3.0 also deprecates DES_set_odd_parity() with
* no replacement.
+ *
+ * OpenSSL 3.0 adds KDF implementations matching the ones we use to derive
+ * encryption and authentication keys from protocol keys.
*/
#define K5_BUILTIN_DES_KEY_PARITY
#define K5_BUILTIN_MD4
#define K5_BUILTIN_RC4
+#define K5_OPENSSL_KDF
#else
#define K5_OPENSSL_DES_KEY_PARITY
#define K5_OPENSSL_MD4
#define K5_OPENSSL_RC4
+#define K5_BUILTIN_KDF
#endif
#define K5_OPENSSL_AES
@@ -68,6 +73,7 @@
#define K5_BUILTIN_DES
#define K5_BUILTIN_DES_KEY_PARITY
#define K5_BUILTIN_HMAC
+#define K5_BUILTIN_KDF
#define K5_BUILTIN_MD4
#define K5_BUILTIN_MD5
#define K5_BUILTIN_PBKDF2
@@ -387,10 +393,6 @@ krb5_error_code krb5int_derive_random(const struct krb5_enc_provider *enc,
krb5_key inkey, krb5_data *outrnd,
const krb5_data *in_constant,
enum deriv_alg alg);
-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);
/*** Miscellaneous prototypes ***/
@@ -510,6 +512,39 @@ krb5_error_code krb5int_pbkdf2_hmac(const struct krb5_hash_provider *hash,
const krb5_data *password,
const krb5_data *salt);
+/*
+ * Compute the NIST SP800-108 KDF in counter mode (section 5.1) with the
+ * following 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.
+ */
+krb5_error_code
+k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+ krb5_key key, const krb5_data *label,
+ const krb5_data *context, krb5_data *rnd_out);
+
+/*
+ * Compute the NIST SP800-108 KDF in feedback mode (section 5.2) with the
+ * following parameters:
+ * - CMAC (with enc as the enc provider) is the PRF.
+ * - A block counter of four bytes is used.
+ * - Label is the key derivation constant.
+ * - Context is empty.
+ * - Four bytes are used to encode the output length in the PRF input.
+ */
+krb5_error_code
+k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc, krb5_key key,
+ const krb5_data *label, krb5_data *rnd_out);
+
+/* Compute the RFC 3961 section 5.1 KDF (which uses n-fold) with enc as E,
+ * inkey as Key, and in_constant as Constant. */
+krb5_error_code
+k5_derive_random_rfc3961(const struct krb5_enc_provider *enc, krb5_key key,
+ const krb5_data *constant, krb5_data *rnd_out);
+
/* The following are used by test programs and are just handler functions from
* the AES and Camellia enc providers. */
krb5_error_code krb5int_aes_encrypt(krb5_key key, const krb5_data *ivec,
diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c
index 6707a73..3e51a00 100644
--- a/src/lib/crypto/krb/derive.c
+++ b/src/lib/crypto/krb/derive.c
@@ -77,185 +77,6 @@ cleanup:
return ENOMEM;
}
-static krb5_error_code
-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;
-}
-
-/*
- * NIST SP800-108 KDF in feedback mode (section 5.2).
- * Parameters:
- * - CMAC (with enc as the enc provider) is the PRF.
- * - A block counter of four bytes is used.
- * - Label is the key derivation constant.
- * - Context is empty.
- * - 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)
-{
- size_t blocksize, keybytes, n;
- krb5_crypto_iov iov[6];
- krb5_error_code ret;
- krb5_data prf;
- unsigned int i;
- unsigned char ibuf[4], Lbuf[4];
-
- blocksize = enc->block_size;
- keybytes = enc->keybytes;
-
- if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
- return KRB5_CRYPTO_INTERNAL;
-
- /* Allocate encryption data buffer. */
- ret = alloc_data(&prf, blocksize);
- if (ret)
- return ret;
-
- /* K(i-1): the previous block of PRF output, initially all-zeros. */
- iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[0].data = prf;
- /* [i]2: four-byte big-endian binary string giving the block counter */
- iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[1].data = make_data(ibuf, sizeof(ibuf));
- /* Label: the fixed derived-key input */
- iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[2].data = *in_constant;
- /* 0x00: separator byte */
- iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[3].data = make_data("", 1);
- /* Context: (unused) */
- iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[4].data = empty_data();
- /* [L]2: four-byte big-endian binary string giving the output length */
- iov[5].flags = KRB5_CRYPTO_TYPE_DATA;
- iov[5].data = make_data(Lbuf, sizeof(Lbuf));
- store_32_be(outrnd->length * 8, Lbuf);
-
- for (i = 1, n = 0; n < keybytes; i++) {
- /* Update the block counter. */
- store_32_be(i, ibuf);
-
- /* Compute a CMAC checksum, storing the result into K(i-1). */
- ret = krb5int_cmac_checksum(enc, inkey, iov, 6, &prf);
- if (ret)
- goto cleanup;
-
- /* Copy the result into the appropriate part of the output buffer. */
- if (keybytes - n <= blocksize) {
- memcpy(outrnd->data + n, prf.data, keybytes - n);
- break;
- }
- memcpy(outrnd->data + n, prf.data, blocksize);
- n += blocksize;
- }
-
-cleanup:
- zapfree(prf.data, blocksize);
- 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.
- */
-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];
-
- if (hash == NULL || outrnd->length > hash->hashsize)
- return KRB5_CRYPTO_INTERNAL;
-
- /* 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;
-}
-
krb5_error_code
krb5int_derive_random(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
@@ -266,13 +87,12 @@ 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, in_constant, outrnd);
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, in_constant, outrnd);
case DERIVE_SP800_108_HMAC:
- return k5_sp800_108_counter_hmac(hash, inkey, outrnd, in_constant,
- &empty);
+ return k5_sp800_108_counter_hmac(hash, inkey, in_constant, &empty,
+ outrnd);
default:
return EINVAL;
}
diff --git a/src/lib/crypto/krb/prf_aes2.c b/src/lib/crypto/krb/prf_aes2.c
index 9a5cffc..4361228 100644
--- a/src/lib/crypto/krb/prf_aes2.c
+++ b/src/lib/crypto/krb/prf_aes2.c
@@ -38,5 +38,5 @@ krb5int_aes2_prf(const struct krb5_keytypes *ktp, krb5_key key,
{
krb5_data label = string2data("prf");
- return k5_sp800_108_counter_hmac(ktp->hash, key, out, &label, in);
+ return k5_sp800_108_counter_hmac(ktp->hash, key, &label, in, out);
}
diff --git a/src/lib/crypto/openssl/Makefile.in b/src/lib/crypto/openssl/Makefile.in
index 1a24d77..d82049f 100644
--- a/src/lib/crypto/openssl/Makefile.in
+++ b/src/lib/crypto/openssl/Makefile.in
@@ -5,16 +5,19 @@ LOCALINCLUDES=-I$(srcdir)/../krb $(CRYPTO_IMPL_CFLAGS)
STLIBOBJS=\
hmac.o \
+ kdf.o \
pbkdf2.o \
sha256.o
OBJS=\
$(OUTPRE)hmac.$(OBJEXT) \
+ $(OUTPRE)kdf.$(OBJEXT) \
$(OUTPRE)pbkdf2.$(OBJEXT) \
$(OUTPRE)sha256.$(OBJEXT)
SRCS=\
$(srcdir)/hmac.c \
+ $(srcdir)/kdf.c \
$(srcdir)/pbkdf2.c \
$(srcdir)/sha256.c
diff --git a/src/lib/crypto/openssl/deps b/src/lib/crypto/openssl/deps
index 8f5efde..7009823 100644
--- a/src/lib/crypto/openssl/deps
+++ b/src/lib/crypto/openssl/deps
@@ -12,6 +12,17 @@ hmac.so hmac.po $(OUTPRE)hmac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
hmac.c
+kdf.so kdf.po $(OUTPRE)kdf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kdf.c
pbkdf2.so pbkdf2.po $(OUTPRE)pbkdf2.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../krb/crypto_int.h \
diff --git a/src/lib/crypto/openssl/kdf.c b/src/lib/crypto/openssl/kdf.c
new file mode 100644
index 0000000..41e845e
--- /dev/null
+++ b/src/lib/crypto/openssl/kdf.c
@@ -0,0 +1,237 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2021 by Red Hat, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "crypto_int.h"
+
+#ifdef K5_OPENSSL_KDF
+
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+
+static char *
+hash_name(const struct krb5_hash_provider *hash)
+{
+ if (hash == &krb5int_hash_sha1)
+ return "SHA1";
+ if (hash == &krb5int_hash_sha256)
+ return "SHA256";
+ if (hash == &krb5int_hash_sha384)
+ return "SHA384";
+ return NULL;
+}
+
+static char *
+enc_name(const struct krb5_enc_provider *enc)
+{
+ if (enc == &krb5int_enc_camellia128)
+ return "CAMELLIA-128-CBC";
+ if (enc == &krb5int_enc_camellia256)
+ return "CAMELLIA-256-CBC";
+ if (enc == &krb5int_enc_aes128)
+ return "AES-128-CBC";
+ if (enc == &krb5int_enc_aes256)
+ return "AES-256-CBC";
+ if (enc == &krb5int_enc_des3)
+ return "DES-EDE3-CBC";
+ return NULL;
+}
+
+krb5_error_code
+k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
+ krb5_key key, const krb5_data *label,
+ const krb5_data *context, krb5_data *rnd_out)
+{
+ krb5_error_code ret;
+ EVP_KDF *kdf = NULL;
+ EVP_KDF_CTX *kctx = NULL;
+ OSSL_PARAM params[6], *p = params;
+ char *digest;
+
+ digest = hash_name(hash);
+ if (digest == NULL) {
+ 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;
+ }
+
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, digest, 0);
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC,
+ OSSL_MAC_NAME_HMAC, 0);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+ key->keyblock.contents,
+ key->keyblock.length);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+ context->data, context->length);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
+ label->data, label->length);
+ *p = OSSL_PARAM_construct_end();
+ if (EVP_KDF_derive(kctx, (uint8_t *)rnd_out->data, rnd_out->length,
+ params) <= 0) {
+ ret = KRB5_CRYPTO_INTERNAL;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ if (ret)
+ zap(rnd_out->data, rnd_out->length);
+ EVP_KDF_free(kdf);
+ EVP_KDF_CTX_free(kctx);
+ return ret;
+}
+
+krb5_error_code
+k5_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc, krb5_key key,
+ const krb5_data *label, krb5_data *rnd_out)
+{
+ krb5_error_code ret;
+ EVP_KDF *kdf = NULL;
+ EVP_KDF_CTX *kctx = NULL;
+ OSSL_PARAM params[7], *p = params;
+ char *cipher;
+ static uint8_t zeroes[16];
+
+ memset(zeroes, 0, sizeof(zeroes));
+
+ cipher = enc_name(enc);
+ if (cipher == NULL) {
+ 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;
+ }
+
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MODE,
+ "FEEDBACK", 0);
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC,
+ OSSL_MAC_NAME_CMAC, 0);
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER, cipher, 0);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+ key->keyblock.contents,
+ key->keyblock.length);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
+ label->data, label->length);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED,
+ zeroes, sizeof(zeroes));
+ *p = OSSL_PARAM_construct_end();
+ if (EVP_KDF_derive(kctx, (uint8_t *)rnd_out->data, rnd_out->length,
+ params) <= 0) {
+ ret = KRB5_CRYPTO_INTERNAL;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ if (ret)
+ zap(rnd_out->data, rnd_out->length);
+ EVP_KDF_free(kdf);
+ EVP_KDF_CTX_free(kctx);
+ return ret;
+}
+
+krb5_error_code
+k5_derive_random_rfc3961(const struct krb5_enc_provider *enc, krb5_key key,
+ const krb5_data *constant, krb5_data *rnd_out)
+{
+ krb5_error_code ret;
+ EVP_KDF *kdf = NULL;
+ EVP_KDF_CTX *kctx = NULL;
+ OSSL_PARAM params[4], *p = params;
+ char *cipher;
+
+ if (key->keyblock.length != enc->keylength ||
+ rnd_out->length != enc->keybytes) {
+ return KRB5_CRYPTO_INTERNAL;
+ }
+
+ cipher = enc_name(enc);
+ if (cipher == NULL) {
+ ret = KRB5_CRYPTO_INTERNAL;
+ goto done;
+ }
+
+ kdf = EVP_KDF_fetch(NULL, "KRB5KDF", NULL);
+ if (kdf == NULL) {
+ ret = KRB5_CRYPTO_INTERNAL;
+ goto done;
+ }
+
+ kctx = EVP_KDF_CTX_new(kdf);
+ if (kctx == NULL) {
+ ret = KRB5_CRYPTO_INTERNAL;
+ goto done;
+ }
+
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CIPHER, cipher, 0);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+ key->keyblock.contents,
+ key->keyblock.length);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_CONSTANT,
+ constant->data, constant->length);
+ *p = OSSL_PARAM_construct_end();
+ if (EVP_KDF_derive(kctx, (uint8_t *)rnd_out->data, rnd_out->length,
+ params) <= 0) {
+ ret = KRB5_CRYPTO_INTERNAL;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ if (ret)
+ zap(rnd_out->data, rnd_out->length);
+ EVP_KDF_free(kdf);
+ EVP_KDF_CTX_free(kctx);
+ return ret;
+}
+
+#endif /* K5_OPENSSL_KDF */
diff --git a/src/plugins/preauth/pkinit/pkinit_constants.c b/src/plugins/preauth/pkinit/pkinit_constants.c
index 4d29efc..652897f 100644
--- a/src/plugins/preauth/pkinit/pkinit_constants.c
+++ b/src/plugins/preauth/pkinit/pkinit_constants.c
@@ -33,23 +33,13 @@
#include "pkinit.h"
/* statically declare OID constants for all three algorithms */
-const krb5_octet krb5_pkinit_sha1_oid[8] =
-{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x01};
-const size_t krb5_pkinit_sha1_oid_len = 8;
-const krb5_octet krb5_pkinit_sha256_oid[8] =
-{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x02};
-const size_t krb5_pkinit_sha256_oid_len = 8;
-const krb5_octet krb5_pkinit_sha512_oid [8] =
-{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x03};
-const size_t krb5_pkinit_sha512_oid_len = 8;
+static char sha1_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x01};
+static char sha256_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x02 };
+static char sha512_oid[8] = { 0x2B, 0x06, 0x01, 0x05, 0x02, 0x03, 0x06, 0x03 };
-#define oid_as_data(var, oid_base) \
- const krb5_data var = \
- {0, sizeof oid_base, (char *)oid_base}
-oid_as_data(sha1_id, krb5_pkinit_sha1_oid);
-oid_as_data(sha256_id, krb5_pkinit_sha256_oid);
-oid_as_data(sha512_id, krb5_pkinit_sha512_oid);
-#undef oid_as_data
+const krb5_data sha1_id = { KV5M_DATA, sizeof(sha1_oid), sha1_oid };
+const krb5_data sha256_id = { KV5M_DATA, sizeof(sha256_oid), sha256_oid };
+const krb5_data sha512_id = { KV5M_DATA, sizeof(sha512_oid), sha512_oid };
krb5_data const * const supported_kdf_alg_ids[] = {
&sha256_id,
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto.h b/src/plugins/preauth/pkinit/pkinit_crypto.h
index ec097bf..94a1b22 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto.h
+++ b/src/plugins/preauth/pkinit/pkinit_crypto.h
@@ -606,12 +606,9 @@ pkinit_alg_agility_kdf(krb5_context context,
krb5_data *pk_as_rep,
krb5_keyblock *key_block);
-extern const krb5_octet krb5_pkinit_sha1_oid[];
-extern const size_t krb5_pkinit_sha1_oid_len;
-extern const krb5_octet krb5_pkinit_sha256_oid[];
-extern const size_t krb5_pkinit_sha256_oid_len;
-extern const krb5_octet krb5_pkinit_sha512_oid[];
-extern const size_t krb5_pkinit_sha512_oid_len;
+extern const krb5_data sha1_id;
+extern const krb5_data sha256_id;
+extern const krb5_data sha512_id;
extern const krb5_data oakley_1024;
extern const krb5_data oakley_2048;
extern const krb5_data oakley_4096;
diff --git a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
index 72f9530..f91f6f9 100644
--- a/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
+++ b/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
@@ -38,6 +38,12 @@
#include <dirent.h>
#include <arpa/inet.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/core_names.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#endif
+
static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );
static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context );
@@ -2608,216 +2614,218 @@ cleanup:
}
-/**
- * Given an algorithm_identifier, this function returns the hash length
- * and EVP function associated with that algorithm.
- */
-static krb5_error_code
-pkinit_alg_values(krb5_context context,
- const krb5_data *alg_id,
- size_t *hash_bytes,
- const EVP_MD *(**func)(void))
+/* Return the OpenSSL descriptor for the given RFC 5652 OID specified in RFC
+ * 8636. RFC 8636 defines a SHA384 variant, but we don't use it. */
+static const EVP_MD *
+algid_to_md(const krb5_data *alg_id)
{
- *hash_bytes = 0;
- *func = NULL;
- if ((alg_id->length == krb5_pkinit_sha1_oid_len) &&
- (0 == memcmp(alg_id->data, &krb5_pkinit_sha1_oid,
- krb5_pkinit_sha1_oid_len))) {
- *hash_bytes = 20;
- *func = &EVP_sha1;
- return 0;
- } else if ((alg_id->length == krb5_pkinit_sha256_oid_len) &&
- (0 == memcmp(alg_id->data, krb5_pkinit_sha256_oid,
- krb5_pkinit_sha256_oid_len))) {
- *hash_bytes = 32;
- *func = &EVP_sha256;
- return 0;
- } else if ((alg_id->length == krb5_pkinit_sha512_oid_len) &&
- (0 == memcmp(alg_id->data, krb5_pkinit_sha512_oid,
- krb5_pkinit_sha512_oid_len))) {
- *hash_bytes = 64;
- *func = &EVP_sha512;
- return 0;
- } else {
- krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS,
- "Bad algorithm ID passed to PK-INIT KDF.");
- return KRB5_ERR_BAD_S2K_PARAMS;
- }
-} /* pkinit_alg_values() */
+ if (data_eq(*alg_id, sha1_id))
+ return EVP_sha1();
+ if (data_eq(*alg_id, sha256_id))
+ return EVP_sha256();
+ if (data_eq(*alg_id, sha512_id))
+ return EVP_sha512();
+ return NULL;
+}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-/* 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:
- *
- * 1. reps = keydatalen (K) / hash length (H)
- *
- * 2. Initialize a 32-bit, big-endian bit string counter as 1.
- *
- * 3. For i = 1 to reps by 1, do the following:
- *
- * - Compute Hashi = H(counter || Z || OtherInfo).
- *
- * - Increment counter (modulo 2^32)
- *
- * 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)
+#define sskdf openssl_sskdf
+static krb5_error_code
+openssl_sskdf(krb5_context context, const EVP_MD *md, krb5_data *key,
+ krb5_data *info, size_t len, krb5_data *out)
{
- krb5_error_code retval = 0;
-
- unsigned int reps = 0;
- uint32_t counter = 1; /* Does this type work on Windows? */
- 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);
+ krb5_error_code ret;
+ EVP_KDF *kdf = NULL;
+ EVP_KDF_CTX *kctx = NULL;
+ OSSL_PARAM params[4], *p = params;
- /* initialize random_data here to make clean-up safe */
- random_data.length = 0;
- random_data.data = NULL;
+ ret = alloc_data(out, len);
+ if (ret)
+ goto cleanup;
- /* 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)))
+ kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL);
+ if (kdf == NULL) {
+ ret = oerr(context, KRB5_CRYPTO_INTERNAL, _("Failed to fetch SSKDF"));
goto cleanup;
+ }
- random_data.length = rand_len;
- key_block->length = key_len;
+ kctx = EVP_KDF_CTX_new(kdf);
+ if (!kctx) {
+ ret = oerr(context, KRB5_CRYPTO_INTERNAL,
+ _("Failed to instantiate SSKDF"));
+ goto cleanup;
+ }
- if (NULL == (key_block->contents = malloc(key_block->length))) {
- retval = ENOMEM;
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)EVP_MD_get0_name(md), 0);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+ key->data, key->length);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+ info->data, info->length);
+ *p = OSSL_PARAM_construct_end();
+ if (EVP_KDF_derive(kctx, (uint8_t *)out->data, len, params) <= 0) {
+ ret = oerr(context, KRB5_CRYPTO_INTERNAL,
+ _("Failed to derive key using SSKDF"));
goto cleanup;
}
- memset (key_block->contents, 0, key_block->length);
+ ret = 0;
- /* 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();
+cleanup:
+ EVP_KDF_free(kdf);
+ EVP_KDF_CTX_free(kctx);
+ return ret;
+}
- if (0 != (retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func)))
- goto cleanup;
+#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+#define sskdf builtin_sskdf
+static krb5_error_code
+builtin_sskdf(krb5_context context, const EVP_MD *md, krb5_data *key,
+ krb5_data *info, size_t len, krb5_data *out)
+{
+ krb5_error_code ret;
+ uint32_t counter = 1, reps;
+ uint8_t be_counter[4], *outptr;
+ EVP_MD_CTX *ctx = NULL;
+ unsigned int s, hash_len;
- /* 1. reps = keydatalen (K) / hash length (H) */
- reps = key_block->length/hash_len;
+ hash_len = EVP_MD_size(md);
- /* ... and round up, if necessary */
- if (key_block->length > (reps * hash_len))
- reps++;
+ /* 1. reps = keydatalen (K) / hash length (H) rounded up. */
+ reps = (len + hash_len - 1) / hash_len;
/* 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)))
+ ret = alloc_data(out, reps * hash_len);
+ if (ret)
goto cleanup;
+ out->length = len;
- /* 2. Initialize a 32-bit, big-endian bit string counter as 1.
+ /*
+ * 2. Initialize a 32-bit, big-endian bit string counter as 1.
* 3. For i = 1 to reps by 1, do the following:
* - Compute Hashi = H(counter || Z || OtherInfo).
* - Increment counter (modulo 2^32)
+ * 4. Set key = Hash1 || Hash2 || ... so that length of key is K
+ * bytes.
*/
+ outptr = (uint8_t *)out->data;
for (counter = 1; counter <= reps; counter++) {
- uint s = 0;
- uint32_t be_counter = htonl(counter);
+ store_32_be(counter, be_counter);
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
- retval = KRB5_CRYPTO_INTERNAL;
+ ret = KRB5_CRYPTO_INTERNAL;
goto cleanup;
}
/* - Compute Hashi = H(counter || Z || OtherInfo). */
- if (!EVP_DigestInit(ctx, EVP_func())) {
- krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
- "Call to OpenSSL EVP_DigestInit() returned an error.");
- retval = KRB5_CRYPTO_INTERNAL;
- goto cleanup;
- }
-
- if (!EVP_DigestUpdate(ctx, &be_counter, 4) ||
- !EVP_DigestUpdate(ctx, secret->data, secret->length) ||
- !EVP_DigestUpdate(ctx, other_info->data, other_info->length)) {
- krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
- "Call to OpenSSL EVP_DigestUpdate() returned an error.");
- retval = KRB5_CRYPTO_INTERNAL;
+ if (!EVP_DigestInit(ctx, md) ||
+ !EVP_DigestUpdate(ctx, be_counter, 4) ||
+ !EVP_DigestUpdate(ctx, key->data, key->length) ||
+ !EVP_DigestUpdate(ctx, info->data, info->length) ||
+ !EVP_DigestFinal(ctx, outptr, &s)) {
+ ret = oerr(context, KRB5_CRYPTO_INTERNAL,
+ _("Failed to compute digest"));
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)) {
- krb5_set_error_message(context, KRB5_CRYPTO_INTERNAL,
- "Call to OpenSSL EVP_DigestUpdate() returned an error.");
- retval = KRB5_CRYPTO_INTERNAL;
- goto cleanup;
- }
- offset += s;
assert(s == hash_len);
+ outptr += s;
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 ret;
+}
- /* If this has been an error, free the allocated key_block, if any */
- if (retval) {
- krb5_free_keyblock_contents(context, key_block);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+/* 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 ret;
+ size_t rand_len = 0, key_len = 0;
+ const EVP_MD *md;
+ 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;
+ char *hash_name = NULL;
+
+ ret = krb5_c_keylengths(context, enctype, &rand_len, &key_len);
+ if (ret)
+ goto cleanup;
+
+ /* Allocate and initialize the key block. */
+ key_block->magic = 0;
+ key_block->enctype = enctype;
+ key_block->length = key_len;
+ key_block->contents = k5calloc(key_block->length, 1, &ret);
+ 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();
+ }
+
+ md = algid_to_md(alg_oid);
+ if (md == NULL) {
+ krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS,
+ "Bad algorithm ID passed to PK-INIT KDF.");
+ return KRB5_ERR_BAD_S2K_PARAMS;
}
- /* free other allocated resources, either way */
- if (random_data.data)
- free(random_data.data);
+ /* 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;
+ ret = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,
+ &supp_pub_info);
+ if (ret)
+ 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;
+ ret = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info);
+ if (ret)
+ goto cleanup;
+
+ ret = sskdf(context, md, secret, other_info, rand_len, &random_data);
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_c_random_to_key(context, enctype, &random_data, key_block);
+
+cleanup:
+ if (ret)
+ krb5_free_keyblock_contents(context, key_block);
+ free(hash_name);
+ 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() */
+ return ret;
+}
krb5_error_code
client_create_dh(krb5_context context,
diff --git a/src/plugins/preauth/pkinit/pkinit_kdf_test.c b/src/plugins/preauth/pkinit/pkinit_kdf_test.c
index 5f60de9..7f38e84 100644
--- a/src/plugins/preauth/pkinit/pkinit_kdf_test.c
+++ b/src/plugins/preauth/pkinit/pkinit_kdf_test.c
@@ -127,8 +127,7 @@ main(int argc, char **argv)
/* TEST 1: SHA-1/AES */
/* set up algorithm id */
- alg_id.algorithm.data = (char *)krb5_pkinit_sha1_oid;
- alg_id.algorithm.length = krb5_pkinit_sha1_oid_len;
+ alg_id.algorithm = sha1_id;
enctype = enctype_aes;
@@ -138,7 +137,7 @@ main(int argc, char **argv)
u_principal, v_principal,
enctype, &as_req, &pk_as_rep,
&key_block))) {
- printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d",
+ printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n",
retval);
goto cleanup;
}
@@ -159,8 +158,7 @@ main(int argc, char **argv)
/* TEST 2: SHA-256/AES */
/* set up algorithm id */
- alg_id.algorithm.data = (char *)krb5_pkinit_sha256_oid;
- alg_id.algorithm.length = krb5_pkinit_sha256_oid_len;
+ alg_id.algorithm = sha256_id;
enctype = enctype_aes;
@@ -170,7 +168,7 @@ main(int argc, char **argv)
u_principal, v_principal,
enctype, &as_req, &pk_as_rep,
&key_block))) {
- printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d",
+ printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n",
retval);
goto cleanup;
}
@@ -191,8 +189,7 @@ main(int argc, char **argv)
/* TEST 3: SHA-512/DES3 */
/* set up algorithm id */
- alg_id.algorithm.data = (char *)krb5_pkinit_sha512_oid;
- alg_id.algorithm.length = krb5_pkinit_sha512_oid_len;
+ alg_id.algorithm = sha512_id;
enctype = enctype_des3;
@@ -202,7 +199,7 @@ main(int argc, char **argv)
u_principal, v_principal,
enctype, &as_req, &pk_as_rep,
&key_block))) {
- printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d",
+ printf("ERROR in pkinit_kdf_test: kdf call failed, retval = %d\n",
retval);
goto cleanup;
}
@@ -225,5 +222,6 @@ cleanup:
krb5_free_principal(context, u_principal);
krb5_free_principal(context, v_principal);
krb5_free_keyblock_contents(context, &key_block);
- exit(retval);
+ krb5_free_context(context);
+ return retval ? 1 : 0;
}