diff options
author | Robbie Harwood <rharwood@redhat.com> | 2019-09-20 17:20:59 -0400 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2021-11-15 11:16:08 -0500 |
commit | 1c87ce6c44a9de0824580a2d72a8a202237e01f4 (patch) | |
tree | 702930b142ba8be976dc78f4f96cd96b2c3678b2 | |
parent | f35077bfc570205092eca2a9d44e50ce265622f4 (diff) | |
download | krb5-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.in | 3 | ||||
-rw-r--r-- | src/lib/crypto/builtin/deps | 11 | ||||
-rw-r--r-- | src/lib/crypto/builtin/kdf.c | 190 | ||||
-rw-r--r-- | src/lib/crypto/krb/crypto_int.h | 43 | ||||
-rw-r--r-- | src/lib/crypto/krb/derive.c | 188 | ||||
-rw-r--r-- | src/lib/crypto/krb/prf_aes2.c | 2 | ||||
-rw-r--r-- | src/lib/crypto/openssl/Makefile.in | 3 | ||||
-rw-r--r-- | src/lib/crypto/openssl/deps | 11 | ||||
-rw-r--r-- | src/lib/crypto/openssl/kdf.c | 237 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_constants.c | 22 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto.h | 9 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_crypto_openssl.c | 330 | ||||
-rw-r--r-- | src/plugins/preauth/pkinit/pkinit_kdf_test.c | 18 |
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; } |