From 56636c9972bdef29c2b957b67cf9fa55ae0ff192 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 11 Dec 2022 18:10:32 +0100 Subject: [PATCH] heimdal: backport openssl3 support patches --- heimdal/1041.patch | 2138 ++++++++++++++++++++++++++++++++++++++++++++ heimdal/PKGBUILD | 11 +- 2 files changed, 2146 insertions(+), 3 deletions(-) create mode 100644 heimdal/1041.patch diff --git a/heimdal/1041.patch b/heimdal/1041.patch new file mode 100644 index 00000000..5292735b --- /dev/null +++ b/heimdal/1041.patch @@ -0,0 +1,2138 @@ +From 96ae6bbd142190b1504a82499fb3b4772c16efd1 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Mon, 21 Nov 2022 12:06:59 -0600 +Subject: [PATCH 01/12] cf: Check for OpenSSL 3.0 + +This check is admittedly lame. But it's all I have time for at the +moment. A better check would be a program that includes the correct +headers and succeeds if the OpenSSL version macro indicates it's at +3.0 or higher. Or perhaps we could run the openssl(1) version command- +line and parse its output. But checking for functions that are in 3.0 +and not 1.1 will do for the time being. + +(cherry picked from commit ac8c1341fbb1d03b32d6feb0c18e1276c3d43c75) +--- + cf/crypto.m4 | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/cf/crypto.m4 b/cf/crypto.m4 +index b8b011dd15..059b0aa9e5 100644 +--- a/cf/crypto.m4 ++++ b/cf/crypto.m4 +@@ -134,6 +134,14 @@ if test "$with_openssl" != "no"; then + LDFLAGS="${saved_LDFLAGS}" + fi + ++if test "$openssl" = "yes"; then ++ AC_CHECK_LIB([crypto], ++ [OSSL_EC_curve_nid2name], ++ [AC_DEFINE_UNQUOTED([HAVE_OPENSSL_30], 1, ++ [whether OpenSSL is 3.0 or higher])] ++ ) ++fi ++ + LIB_hcrypto='$(top_builddir)/lib/hcrypto/libhcrypto.la' + LIB_hcrypto_a='$(top_builddir)/lib/hcrypto/.libs/libhcrypto.a' + LIB_hcrypto_so='$(top_builddir)/lib/hcrypto/.libs/libhcrypto.so' + +From 3d0f1cc3ff28829803420057af2669454ad11421 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Mon, 21 Nov 2022 12:09:07 -0600 +Subject: [PATCH 02/12] cf: Check for OpenSSL FIPS + +(cherry picked from commit 83cd1255f69dd5fff535fcc2c2147ca021c75d95) +--- + cf/crypto.m4 | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/cf/crypto.m4 b/cf/crypto.m4 +index 059b0aa9e5..a8ef519841 100644 +--- a/cf/crypto.m4 ++++ b/cf/crypto.m4 +@@ -140,6 +140,15 @@ if test "$openssl" = "yes"; then + [AC_DEFINE_UNQUOTED([HAVE_OPENSSL_30], 1, + [whether OpenSSL is 3.0 or higher])] + ) ++ AC_CHECK_HEADERS([openssl/fips.h], ++ [AC_DEFINE_UNQUOTED([HAVE_OPENSSL_FIPS_H], 1, ++ [whether openssl/fips.h is available])] ++ ) ++ AC_CHECK_LIB([crypto], ++ [FIPS_mode_set], ++ [AC_DEFINE_UNQUOTED([HAVE_OPENSSL_FIPS_MODE_SET_API], 1, ++ [whether FIPS_mode_set API is available])] ++ ) + fi + + LIB_hcrypto='$(top_builddir)/lib/hcrypto/libhcrypto.la' + +From 3f668b6e7a13bfc50ea28cc32b7ad4ed67c0f0e7 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Thu, 17 Nov 2022 18:57:35 -0600 +Subject: [PATCH 03/12] hcrypto: OpenSSL's EVP_Cipher() returns a length + +(cherry picked from commit 47e6c68de8594e03a7b73ecbc759dc0451cb3966) +--- + lib/hcrypto/evp-openssl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/hcrypto/evp-openssl.c b/lib/hcrypto/evp-openssl.c +index ae38e27c00..e7b119475e 100644 +--- a/lib/hcrypto/evp-openssl.c ++++ b/lib/hcrypto/evp-openssl.c +@@ -158,7 +158,7 @@ cipher_do_cipher(hc_EVP_CIPHER_CTX *ctx, unsigned char *out, + struct ossl_cipher_ctx *ossl_ctx = ctx->cipher_data; + + assert(ossl_ctx != NULL); +- return EVP_Cipher(ossl_ctx->ossl_cipher_ctx, out, in, len); ++ return EVP_Cipher(ossl_ctx->ossl_cipher_ctx, out, in, len) == 0 ? 0 : 1; + } + + static int + +From cc5897eae61da950d6bc8df9eb0fb0627e6fe4c9 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Mon, 21 Nov 2022 14:24:31 -0600 +Subject: [PATCH 04/12] hcrypto: Fix EVP_CipherUpdate() bugs + +(cherry picked from commit 11846fcabb8f98e12bd9b5b950e650de6eb42451) +--- + lib/hcrypto/evp.c | 33 +++++++++++++++++---------------- + 1 file changed, 17 insertions(+), 16 deletions(-) + +diff --git a/lib/hcrypto/evp.c b/lib/hcrypto/evp.c +index 06935449e4..2b7df3ddef 100644 +--- a/lib/hcrypto/evp.c ++++ b/lib/hcrypto/evp.c +@@ -857,37 +857,34 @@ EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, void *out, int *outlen, + + *outlen = 0; + +- /** +- * If there in no spare bytes in the left from last Update and the +- * input length is on the block boundery, the EVP_CipherUpdate() +- * function can take a shortcut (and preformance gain) and +- * directly encrypt the data, otherwise we hav to fix it up and +- * store extra it the EVP_CIPHER_CTX. ++ /* ++ * If there in no bytes left over from the last Update and the ++ * input length is on a block boundary, then we can take a ++ * shortcut (and preformance gain) and directly encrypt the ++ * data. + */ +- if (ctx->buf_len == 0 && (inlen & ctx->block_mask) == 0) { ++ if (ctx->buf_len == 0 && inlen && (inlen & ctx->block_mask) == 0) { + ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen); + if (ret == 1) + *outlen = inlen; +- else ++ else + *outlen = 0; + return ret; + } + +- + blocksize = EVP_CIPHER_CTX_block_size(ctx); + left = blocksize - ctx->buf_len; + assert(left > 0); + + if (ctx->buf_len) { +- +- /* if total buffer is smaller then input, store locally */ ++ /* If we can't fill one block in the buffer, save the input there */ + if (inlen < left) { + memcpy(ctx->buf + ctx->buf_len, in, inlen); + ctx->buf_len += inlen; + return 1; + } + +- /* fill in local buffer and encrypt */ ++ /* Fill the buffer and encrypt */ + memcpy(ctx->buf + ctx->buf_len, in, left); + ret = (*ctx->cipher->do_cipher)(ctx, out, ctx->buf, blocksize); + memset(ctx->buf, 0, blocksize); +@@ -905,12 +902,16 @@ EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, void *out, int *outlen, + ctx->buf_len = (inlen & ctx->block_mask); + inlen &= ~ctx->block_mask; + +- ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen); +- if (ret != 1) +- return ret; ++ if (inlen) { ++ /* Encrypt all the whole blocks of input that we have */ ++ ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen); ++ if (ret != 1) ++ return ret; ++ } + + *outlen += inlen; + ++ /* Save the tail of the input, if any */ + in = ((unsigned char *)in) + inlen; + memcpy(ctx->buf, in, ctx->buf_len); + } +@@ -969,7 +970,7 @@ EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, void *out, int *outlen) + * @param in in data to the operation. + * @param size length of data. + * +- * @return 1 on success. ++ * @return bytes encrypted on success, zero on failure. + */ + + int + +From 30d589528e8477a37711cc1d7a2b88c26f4bd324 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Thu, 17 Nov 2022 19:00:49 -0600 +Subject: [PATCH 05/12] hcrypto: Use builtin 1DES/RC2/RC4 (OpenSSL 3) + +At some point before we make an 8.0 release we'll probably just remove +all the legacy, weak ciphers and hashes (except MD5, most likely). + +To drop these we'll have to re-generate PKCS#12 test samples using +stronger PBEs, and possible add new PBE types. + +(cherry picked from commit 2ddea96ba231433dd74e707e14b42cd88b92d8b7) +--- + lib/hcrypto/evp-openssl.c | 24 +++++++++++++++++ + lib/hcrypto/evp.c | 55 ++++++++++++++++++++++++++++++++++++++ + lib/hcrypto/evp.h | 2 +- + lib/hcrypto/test_cipher.c | 6 ----- + lib/hcrypto/test_crypto.in | 10 +++---- + 5 files changed, 85 insertions(+), 12 deletions(-) + +diff --git a/lib/hcrypto/evp-openssl.c b/lib/hcrypto/evp-openssl.c +index e7b119475e..c6ab8034c8 100644 +--- a/lib/hcrypto/evp-openssl.c ++++ b/lib/hcrypto/evp-openssl.c +@@ -80,6 +80,24 @@ + #define EVP_MD_CTX_free EVP_MD_CTX_destroy + #endif + ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++int _heim_openssl_fips_enabled(void); ++int ++_heim_openssl_fips_enabled(void) ++{ ++ static int fips_enabled_res = -1; ++ ++ if (fips_enabled_res != -1) ++ return fips_enabled_res; ++ ++#ifdef HAVE_OPENSSL_30 ++ return fips_enabled_res = !!EVP_default_properties_is_fips_enabled(NULL); ++#else ++ return fips_enabled_res = !!FIPS_mode(); ++#endif ++} ++#endif ++ + /* A HEIM_BASE_ONCE argument struct for per-EVP one-time initialization */ + struct once_init_cipher_ctx { + const hc_EVP_CIPHER **hc_memoizep; +@@ -438,7 +456,9 @@ OSSL_CIPHER_ALGORITHM(des_ede3_cbc, hc_EVP_CIPH_CBC_MODE) + * + * @ingroup hcrypto_evp + */ ++#ifndef HAVE_OPENSSL_30 + OSSL_CIPHER_ALGORITHM(des_cbc, hc_EVP_CIPH_CBC_MODE) ++#endif + + /** + * The AES-128 cipher type (OpenSSL provider) +@@ -494,6 +514,7 @@ OSSL_CIPHER_ALGORITHM(aes_192_cfb8, hc_EVP_CIPH_CFB8_MODE) + */ + OSSL_CIPHER_ALGORITHM(aes_256_cfb8, hc_EVP_CIPH_CFB8_MODE) + ++#ifndef HAVE_OPENSSL_30 + /* + * RC2 is only needed for tests of PKCS#12 support, which currently uses + * the RC2 PBE. So no RC2 -> tests fail. +@@ -530,6 +551,7 @@ OSSL_CIPHER_ALGORITHM(rc2_40_cbc, + OSSL_CIPHER_ALGORITHM(rc2_64_cbc, + hc_EVP_CIPH_CBC_MODE | + hc_EVP_CIPH_VARIABLE_LENGTH) ++#endif + + /** + * The Camellia-128 cipher type - OpenSSL +@@ -558,6 +580,7 @@ OSSL_CIPHER_ALGORITHM(camellia_192_cbc, hc_EVP_CIPH_CBC_MODE) + */ + OSSL_CIPHER_ALGORITHM(camellia_256_cbc, hc_EVP_CIPH_CBC_MODE) + ++#ifndef HAVE_OPENSSL_30 + /** + * The RC4 cipher type (OpenSSL provider) + * +@@ -588,6 +611,7 @@ OSSL_CIPHER_ALGORITHM(rc4_40, + * @ingroup hcrypto_evp + */ + OSSL_MD_ALGORITHM(md4) ++#endif + + /** + * The MD5 hash algorithm (OpenSSL provider) +diff --git a/lib/hcrypto/evp.c b/lib/hcrypto/evp.c +index 2b7df3ddef..54fa32be64 100644 +--- a/lib/hcrypto/evp.c ++++ b/lib/hcrypto/evp.c +@@ -59,6 +59,7 @@ + # define HCRYPTO_DEF_PROVIDER pkcs11_hcrypto + # elif HAVE_HCRYPTO_W_OPENSSL + # define HCRYPTO_DEF_PROVIDER ossl ++# define HCRYPTO_DEF_PROVIDER_IS_OPENSSL + # else + # define HCRYPTO_DEF_PROVIDER hcrypto + # endif +@@ -69,6 +70,11 @@ + + #define EVP_DEF_OP(_prov,_op) HC_CONCAT4(EVP_,_prov,_,_op)() + ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++extern int _heim_openssl_fips_enabled(void); ++#endif ++ ++ + /** + * @page page_evp EVP - generic crypto interface + * +@@ -463,6 +469,13 @@ const EVP_MD * + EVP_md4(void) HC_DEPRECATED_CRYPTO + { + hcrypto_validate(); ++#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++ if (_heim_openssl_fips_enabled()) ++ return NULL; ++#endif ++ return EVP_DEF_OP(hcrypto, md4); ++#endif + return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, md4); + } + +@@ -1049,6 +1062,13 @@ const EVP_CIPHER * + EVP_rc2_cbc(void) + { + hcrypto_validate(); ++#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++ if (_heim_openssl_fips_enabled()) ++ return NULL; ++#endif ++ return EVP_DEF_OP(hcrypto, rc2_cbc); ++#endif + return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc2_cbc); + } + +@@ -1064,6 +1084,13 @@ const EVP_CIPHER * + EVP_rc2_40_cbc(void) + { + hcrypto_validate(); ++#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++ if (_heim_openssl_fips_enabled()) ++ return NULL; ++#endif ++ return EVP_DEF_OP(hcrypto, rc2_40_cbc); ++#endif + return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc2_40_cbc); + } + +@@ -1079,6 +1106,13 @@ const EVP_CIPHER * + EVP_rc2_64_cbc(void) + { + hcrypto_validate(); ++#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++ if (_heim_openssl_fips_enabled()) ++ return NULL; ++#endif ++ return EVP_DEF_OP(hcrypto, rc2_64_cbc); ++#endif + return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc2_64_cbc); + } + +@@ -1094,6 +1128,13 @@ const EVP_CIPHER * + EVP_rc4(void) + { + hcrypto_validate(); ++#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++ if (_heim_openssl_fips_enabled()) ++ return NULL; ++#endif ++ return EVP_DEF_OP(hcrypto, rc4); ++#endif + return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc4); + } + +@@ -1109,6 +1150,13 @@ const EVP_CIPHER * + EVP_rc4_40(void) + { + hcrypto_validate(); ++#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++ if (_heim_openssl_fips_enabled()) ++ return NULL; ++#endif ++ return EVP_DEF_OP(hcrypto, rc4_40); ++#endif + return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc4_40); + } + +@@ -1124,6 +1172,13 @@ const EVP_CIPHER * + EVP_des_cbc(void) + { + hcrypto_validate(); ++#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) ++#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) ++ if (_heim_openssl_fips_enabled()) ++ return NULL; ++#endif ++ return EVP_DEF_OP(hcrypto, des_cbc); ++#endif + return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, des_cbc); + } + +diff --git a/lib/hcrypto/evp.h b/lib/hcrypto/evp.h +index 820c500f42..7019abafd6 100644 +--- a/lib/hcrypto/evp.h ++++ b/lib/hcrypto/evp.h +@@ -193,7 +193,7 @@ struct hc_CIPHER_CTX { + unsigned long flags; + void *cipher_data; + int final_used; +- int block_mask; ++ unsigned block_mask; + unsigned char final[EVP_MAX_BLOCK_LENGTH]; + }; + +diff --git a/lib/hcrypto/test_cipher.c b/lib/hcrypto/test_cipher.c +index 26bf42c1b0..f648b657a6 100644 +--- a/lib/hcrypto/test_cipher.c ++++ b/lib/hcrypto/test_cipher.c +@@ -421,14 +421,8 @@ main(int argc, char **argv) + ret += test_cipher(i, EVP_ossl_aes_256_cbc(), &aes_tests[i]); + for (i = 0; i < sizeof(aes_cfb_tests)/sizeof(aes_cfb_tests[0]); i++) + ret += test_cipher(i, EVP_ossl_aes_128_cfb8(), &aes_cfb_tests[i]); +- for (i = 0; i < sizeof(rc2_tests)/sizeof(rc2_tests[0]); i++) +- ret += test_cipher(i, EVP_ossl_rc2_cbc(), &rc2_tests[i]); +- for (i = 0; i < sizeof(rc2_40_tests)/sizeof(rc2_40_tests[0]); i++) +- ret += test_cipher(i, EVP_ossl_rc2_40_cbc(), &rc2_40_tests[i]); + for (i = 0; i < sizeof(des_ede3_tests)/sizeof(des_ede3_tests[0]); i++) + ret += test_cipher(i, EVP_ossl_des_ede3_cbc(), &des_ede3_tests[i]); +- for (i = 0; i < sizeof(rc4_tests)/sizeof(rc4_tests[0]); i++) +- ret += test_cipher(i, EVP_ossl_rc4(), &rc4_tests[i]); + #endif /* PKCS11_MODULE_PATH */ + + return ret; +diff --git a/lib/hcrypto/test_crypto.in b/lib/hcrypto/test_crypto.in +index d5b3893164..91c4d8f109 100644 +--- a/lib/hcrypto/test_crypto.in ++++ b/lib/hcrypto/test_crypto.in +@@ -82,11 +82,11 @@ for a in unix fortuna egd w32crypto ;do + { echo "rand output same!" ; exit 1; } + done + +-./example_evp_cipher 1 ${srcdir}/test_crypto.in test-out-1 || \ +- { echo "1 failed" ; exit 1; } +- +-for a in 7 15 16 17 31 32 33 ; do +- ./example_evp_cipher $a ${srcdir}/test_crypto.in test-out-$a ++for a in 1 7 15 16 17 31 32 33 ; do ++ ./example_evp_cipher $a ${srcdir}/test_crypto.in test-out-$a || ++ { echo "$s failed" ; exit 1; } ++done ++for a in 7 15 16 17 31 32 33 ; do + cmp test-out-1 test-out-$a || { echo "cmp $a failed" ; exit 1; } + done + + +From 6aab05177295e2bc79ec81311eb622b1e9329fbb Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Sun, 16 Aug 2020 16:58:51 -0500 +Subject: [PATCH 06/12] hx509: Add hx509_enomem() + +--- + lib/hx509/error.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/lib/hx509/error.c b/lib/hx509/error.c +index be09414bff..58abf0f003 100644 +--- a/lib/hx509/error.c ++++ b/lib/hx509/error.c +@@ -119,6 +119,21 @@ hx509_set_error_string(hx509_context context, int flags, int code, + va_end(ap); + } + ++/** ++ * Sets ENOMEM as the error on a hx509 context. ++ * ++ * @param context A hx509 context. ++ * ++ * @ingroup hx509_error ++ */ ++ ++int ++hx509_enomem(hx509_context context) ++{ ++ hx509_set_error_string(context, 0, ENOMEM, "Out of memory"); ++ return ENOMEM; ++} ++ + /** + * Get an error string from context associated with error_code. + * + +From 917e18ba53c822e7b679dc36a7a0c205801b85ca Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Thu, 4 Jul 2019 17:57:18 -0500 +Subject: [PATCH 07/12] hx509: Add PKCS#8 private key format option + +--- + lib/hx509/crypto.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ + lib/hx509/hx509.h | 3 ++- + 2 files changed, 68 insertions(+), 1 deletion(-) + +diff --git a/lib/hx509/crypto.c b/lib/hx509/crypto.c +index 0df91699b5..84382d8ad3 100644 +--- a/lib/hx509/crypto.c ++++ b/lib/hx509/crypto.c +@@ -1302,6 +1302,34 @@ hx509_parse_private_key(hx509_context context, + + *private_key = NULL; + ++ if (format == HX509_KEY_FORMAT_PKCS8) { ++ PKCS8PrivateKeyInfo ki; ++ hx509_private_key key; ++ ++ ret = decode_PKCS8PrivateKeyInfo(data, len, &ki, NULL); ++ if (ret) { ++ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, ++ "Failed to parse PKCS#8-encoded private " ++ "key"); ++ return HX509_PARSING_KEY_FAILED; ++ } ++ ++ /* Re-enter to parse DER-encoded key from PKCS#8 envelope */ ++ ret = hx509_parse_private_key(context, &ki.privateKeyAlgorithm, ++ ki.privateKey.data, ki.privateKey.length, ++ HX509_KEY_FORMAT_DER, &key); ++ free_PKCS8PrivateKeyInfo(&ki); ++ if (ret) { ++ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, ++ "Failed to parse RSA key from PKCS#8 " ++ "envelope"); ++ return HX509_PARSING_KEY_FAILED; ++ } ++ ++ *private_key = key; ++ return ret; ++ } ++ + ops = hx509_find_private_alg(&keyai->algorithm); + if (ops == NULL) { + hx509_clear_error_string(context); +@@ -1601,6 +1629,44 @@ _hx509_private_key_export(hx509_context context, + hx509_clear_error_string(context); + return HX509_UNIMPLEMENTED_OPERATION; + } ++ if (format == HX509_KEY_FORMAT_PKCS8) { ++ PKCS8PrivateKeyInfo ki; ++ size_t size; ++ int ret; ++ ++ memset(&ki, 0, sizeof(ki)); ++ ki.attributes = NULL; /* No localKeyId needed */ ++ ki.privateKey.data = NULL; ++ ki.privateKeyAlgorithm.algorithm.components = NULL; ++ ret = der_parse_hex_heim_integer("00", &ki.version); ++ if (ret == 0) ++ ret = _hx509_private_key_oid(context, key, ++ &ki.privateKeyAlgorithm.algorithm); ++ if (ret == 0) ++ /* Re-enter */ ++ ret = _hx509_private_key_export(context, key, HX509_KEY_FORMAT_DER, ++ &ki.privateKey); ++ ++ /* ++ * XXX To set ki.privateKeyAlgorithm.parameters we'll need to either ++ * move this code into the *key->ops->export() functions, or expand ++ * their signature to allow them to set it for us, or add a method to ++ * hx509_private_key_ops that allows us to get the parameters from the ++ * backend. ++ */ ++ ki.privateKeyAlgorithm.parameters = NULL; ++ ++ if (ret == 0) ++ ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, data->data, data->length, ++ &ki, &size, ret); ++ free_PKCS8PrivateKeyInfo(&ki); ++ if (ret == 0 && size != data->length) ++ ret = EINVAL; ++ if (ret) ++ hx509_set_error_string(context, 0, ret, ++ "Private key PKCS#8 encoding failed"); ++ return ret; ++ } + return (*key->ops->export)(context, key, format, data); + } + +diff --git a/lib/hx509/hx509.h b/lib/hx509/hx509.h +index 781f4a59cc..10f3c083ab 100644 +--- a/lib/hx509/hx509.h ++++ b/lib/hx509/hx509.h +@@ -81,7 +81,8 @@ enum { + enum { + HX509_KEY_FORMAT_GUESS = 0, + HX509_KEY_FORMAT_DER = 1, +- HX509_KEY_FORMAT_WIN_BACKUPKEY = 2 ++ HX509_KEY_FORMAT_WIN_BACKUPKEY = 2, ++ HX509_KEY_FORMAT_PKCS8 = 3, + }; + typedef uint32_t hx509_key_format_t; + + +From 7e91cd0123c9b7f216cd0447456cc27dfc3341f5 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Sat, 19 Nov 2022 23:43:27 -0600 +Subject: [PATCH 08/12] hx509: Pass PKCS#8 keys to lower layers + +OpenSSL's d2i_ECPrivateKey() is deprecated, so we have to use +d2i_PrivateKey(), but d2i_PrivateKey() wants the whole PKCS#8 blob so it +can know what kind of key it is. So we need to let the hx509 EC layer +get that blob. The internal APIs need some refactoring, so for now we +use a hack where we try to parse the private key with and without the +PKCS#8 wrapper. + +(cherry picked from commit cce8ae99274619daa9c765490757e771e9d04a64) +--- + lib/hx509/collector.c | 10 ++++++++++ + lib/hx509/crypto.c | 46 ++++++++++++++++++++----------------------- + 2 files changed, 31 insertions(+), 25 deletions(-) + +diff --git a/lib/hx509/collector.c b/lib/hx509/collector.c +index 15f8163f80..d02f07095c 100644 +--- a/lib/hx509/collector.c ++++ b/lib/hx509/collector.c +@@ -147,6 +147,16 @@ _hx509_collector_private_key_add(hx509_context context, + key_data->data, key_data->length, + HX509_KEY_FORMAT_DER, + &key->private_key); ++ if (ret && localKeyId) { ++ int ret2; ++ ++ ret2 = hx509_parse_private_key(context, alg, ++ localKeyId->data, localKeyId->length, ++ HX509_KEY_FORMAT_PKCS8, ++ &key->private_key); ++ if (ret2 == 0) ++ ret = 0; ++ } + if (ret) + goto out; + } +diff --git a/lib/hx509/crypto.c b/lib/hx509/crypto.c +index 84382d8ad3..169d35f7c2 100644 +--- a/lib/hx509/crypto.c ++++ b/lib/hx509/crypto.c +@@ -1302,10 +1302,27 @@ hx509_parse_private_key(hx509_context context, + + *private_key = NULL; + +- if (format == HX509_KEY_FORMAT_PKCS8) { ++ ops = hx509_find_private_alg(&keyai->algorithm); ++ if (ops == NULL) { ++ hx509_clear_error_string(context); ++ return HX509_SIG_ALG_NO_SUPPORTED; ++ } ++ ++ ret = hx509_private_key_init(private_key, ops, NULL); ++ if (ret) { ++ hx509_set_error_string(context, 0, ret, "out of memory"); ++ return ret; ++ } ++ ++ ret = (*ops->import)(context, keyai, data, len, format, *private_key); ++ if (ret) ++ hx509_private_key_free(private_key); ++ ++ if (ret && format == HX509_KEY_FORMAT_PKCS8) { + PKCS8PrivateKeyInfo ki; + hx509_private_key key; + ++ /* Re-enter to try parsing the DER-encoded key from PKCS#8 envelope */ + ret = decode_PKCS8PrivateKeyInfo(data, len, &ki, NULL); + if (ret) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, +@@ -1313,39 +1330,18 @@ hx509_parse_private_key(hx509_context context, + "key"); + return HX509_PARSING_KEY_FAILED; + } +- +- /* Re-enter to parse DER-encoded key from PKCS#8 envelope */ + ret = hx509_parse_private_key(context, &ki.privateKeyAlgorithm, + ki.privateKey.data, ki.privateKey.length, + HX509_KEY_FORMAT_DER, &key); + free_PKCS8PrivateKeyInfo(&ki); + if (ret) { +- hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, +- "Failed to parse RSA key from PKCS#8 " ++ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, ++ "Failed to parse RSA key from PKCS#8 " + "envelope"); +- return HX509_PARSING_KEY_FAILED; ++ return HX509_PARSING_KEY_FAILED; + } +- + *private_key = key; +- return ret; +- } +- +- ops = hx509_find_private_alg(&keyai->algorithm); +- if (ops == NULL) { +- hx509_clear_error_string(context); +- return HX509_SIG_ALG_NO_SUPPORTED; + } +- +- ret = hx509_private_key_init(private_key, ops, NULL); +- if (ret) { +- hx509_set_error_string(context, 0, ret, "out of memory"); +- return ret; +- } +- +- ret = (*ops->import)(context, keyai, data, len, format, *private_key); +- if (ret) +- hx509_private_key_free(private_key); +- + return ret; + } + + +From f266859ce927571abd0feeb478cd2d4952382ae8 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Tue, 22 Nov 2022 11:57:49 -0500 +Subject: [PATCH 09/12] hx509: PKCS#12 missing error check + +lib/hx509/test_req.in change left out due to conflicts. + +(Partial cherry-pick from commit 0d5b2381861f14fed24be2ca73592a06d17e5adc) +--- + lib/hx509/ks_p12.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/lib/hx509/ks_p12.c b/lib/hx509/ks_p12.c +index b7df0be32a..941b104137 100644 +--- a/lib/hx509/ks_p12.c ++++ b/lib/hx509/ks_p12.c +@@ -84,14 +84,14 @@ keyBag_parser(hx509_context context, + if (ret) + return ret; + +- _hx509_collector_private_key_add(context, +- c, +- &ki.privateKeyAlgorithm, +- NULL, +- &ki.privateKey, +- os); ++ ret = _hx509_collector_private_key_add(context, ++ c, ++ &ki.privateKeyAlgorithm, ++ NULL, ++ &ki.privateKey, ++ os); + free_PKCS8PrivateKeyInfo(&ki); +- return 0; ++ return ret; + } + + static int + +From 55c186b86ee1a93caa6bffe87455f9c159575cd3 Mon Sep 17 00:00:00 2001 +From: Jeffrey Altman +Date: Tue, 22 Nov 2022 12:15:37 -0500 +Subject: [PATCH 10/12] hx509: OpenSSL 3.0 support + +(cherry-picked from commit 264f0bd1a2477640143e5d11fd4027cbdcd619bf) +--- + lib/hx509/crypto-ec.c | 506 ++++++++++++++++++++++++++------- + lib/hx509/libhx509-exports.def | 5 + + lib/hx509/version-script.map | 2 +- + lib/libedit/config.h.in | 100 +++++-- + 4 files changed, 493 insertions(+), 120 deletions(-) + +diff --git a/lib/hx509/crypto-ec.c b/lib/hx509/crypto-ec.c +index 4777171cae..0f3c1f14c4 100644 +--- a/lib/hx509/crypto-ec.c ++++ b/lib/hx509/crypto-ec.c +@@ -34,11 +34,15 @@ + #include + + #ifdef HAVE_HCRYPTO_W_OPENSSL ++#include + #include + #include + #include + #include + #include ++#ifdef HAVE_OPENSSL_30 ++#include ++#endif + #define HEIM_NO_CRYPTO_HDRS + #endif /* HAVE_HCRYPTO_W_OPENSSL */ + +@@ -53,43 +57,50 @@ void + _hx509_private_eckey_free(void *eckey) + { + #ifdef HAVE_HCRYPTO_W_OPENSSL ++#ifdef HAVE_OPENSSL_30 ++ EVP_PKEY_free(eckey); ++#else + EC_KEY_free(eckey); + #endif ++#endif + } + + #ifdef HAVE_HCRYPTO_W_OPENSSL +-static int +-heim_oid2ecnid(heim_oid *oid) +-{ +- /* +- * Now map to openssl OID fun +- */ +- +- if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP256R1) == 0) +- return NID_X9_62_prime256v1; ++static struct oid2nid_st { ++ const heim_oid *oid; ++ int nid; ++} oid2nid[] = { ++ { ASN1_OID_ID_EC_GROUP_SECP256R1, NID_X9_62_prime256v1 }, + #ifdef NID_secp521r1 +- else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP521R1) == 0) +- return NID_secp521r1; ++ { ASN1_OID_ID_EC_GROUP_SECP521R1, NID_secp521r1 }, + #endif + #ifdef NID_secp384r1 +- else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP384R1) == 0) +- return NID_secp384r1; ++ { ASN1_OID_ID_EC_GROUP_SECP384R1, NID_secp384r1 }, + #endif + #ifdef NID_secp160r1 +- else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R1) == 0) +- return NID_secp160r1; ++ { ASN1_OID_ID_EC_GROUP_SECP160R1, NID_secp160r1 }, + #endif + #ifdef NID_secp160r2 +- else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R2) == 0) +- return NID_secp160r2; ++ { ASN1_OID_ID_EC_GROUP_SECP160R2, NID_secp160r2 }, + #endif ++ /* XXX Add more! Add X25519! */ ++}; ++ ++int ++_hx509_ossl_oid2nid(heim_oid *oid) ++{ ++ size_t i; + ++ for (i = 0; i < sizeof(oid2nid)/sizeof(oid2nid[0]); i++) ++ if (der_heim_oid_cmp(oid, oid2nid[i].oid) == 0) ++ return oid2nid[i].nid; + return NID_undef; + } + + static int +-parse_ECParameters(hx509_context context, +- heim_octet_string *parameters, int *nid) ++ECParameters2nid(hx509_context context, ++ heim_octet_string *parameters, ++ int *nid) + { + ECParameters ecparam; + size_t size; +@@ -117,7 +128,7 @@ parse_ECParameters(hx509_context context, + return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + +- *nid = heim_oid2ecnid(&ecparam.u.namedCurve); ++ *nid = _hx509_ossl_oid2nid(&ecparam.u.namedCurve); + free_ECParameters(&ecparam); + if (*nid == NID_undef) { + hx509_set_error_string(context, 0, ret, +@@ -127,6 +138,39 @@ parse_ECParameters(hx509_context context, + return 0; + } + ++#ifdef HAVE_OPENSSL_30 ++static const EVP_MD * ++signature_alg2digest_evp_md(hx509_context context, ++ const AlgorithmIdentifier *digest_alg) ++{ ++ if ((&digest_alg->algorithm == &asn1_oid_id_sha512 || ++ der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_sha512) == 0)) ++ return EVP_sha512(); ++ if ((&digest_alg->algorithm == &asn1_oid_id_sha384 || ++ der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_sha384) == 0)) ++ return EVP_sha384(); ++ if ((&digest_alg->algorithm == &asn1_oid_id_sha256 || ++ der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_sha256) == 0)) ++ return EVP_sha256(); ++ if ((&digest_alg->algorithm == &asn1_oid_id_secsig_sha_1 || ++ der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_secsig_sha_1) == 0)) ++ return EVP_sha1(); ++ if ((&digest_alg->algorithm == &asn1_oid_id_rsa_digest_md5 || ++ der_heim_oid_cmp(&digest_alg->algorithm, ++ &asn1_oid_id_rsa_digest_md5) == 0)) ++ return EVP_md5(); ++ ++ /* ++ * XXX Decode the `digest_alg->algorithm' OID and include it in the error ++ * message. ++ */ ++ hx509_set_error_string(context, 0, EINVAL, ++ "Digest algorithm not found"); ++ return NULL; ++} ++#endif ++ ++ + + /* + * +@@ -140,6 +184,106 @@ ecdsa_verify_signature(hx509_context context, + const heim_octet_string *data, + const heim_octet_string *sig) + { ++#ifdef HAVE_OPENSSL_30 ++ const AlgorithmIdentifier *digest_alg = sig_alg->digest_alg; ++ const EVP_MD *md = signature_alg2digest_evp_md(context, digest_alg); ++ const SubjectPublicKeyInfo *spi; ++ const char *curve_sn = NULL; /* sn == short name in OpenSSL parlance */ ++ OSSL_PARAM params[2]; ++ EVP_PKEY_CTX *pctx = NULL; ++ EVP_MD_CTX *mdctx = NULL; ++ EVP_PKEY *template = NULL; ++ EVP_PKEY *public = NULL; ++ const unsigned char *p; ++ size_t len; ++ char *curve_sn_dup = NULL; ++ int groupnid; ++ int ret = 0; ++ ++ spi = &signer->tbsCertificate.subjectPublicKeyInfo; ++ if (der_heim_oid_cmp(&spi->algorithm.algorithm, ++ ASN1_OID_ID_ECPUBLICKEY) != 0) ++ hx509_set_error_string(context, 0, ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT, ++ /* XXX Include the OID in the message */ ++ "Unsupported subjectPublicKey algorithm"); ++ if (ret == 0) ++ ret = ECParameters2nid(context, spi->algorithm.parameters, &groupnid); ++ if (ret == 0 && (curve_sn = OBJ_nid2sn(groupnid)) == NULL) ++ hx509_set_error_string(context, 0, ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT, ++ "Could not resolve curve NID %d to its short name", ++ groupnid); ++ if (ret == 0 && (curve_sn_dup = strdup(curve_sn)) == NULL) ++ ret = hx509_enomem(context); ++ if (ret == 0 && (mdctx = EVP_MD_CTX_new()) == NULL) ++ ret = hx509_enomem(context); ++ ++ /* ++ * In order for d2i_PublicKey() to work we need to create a template key ++ * that has the curve parameters for the subjectPublicKey. ++ * ++ * Or maybe we could learn to use the OSSL_DECODER(3) API. But this works, ++ * at least until OpenSSL deprecates d2i_PublicKey() and forces us to use ++ * OSSL_DECODER(3). ++ */ ++ if (ret == 0) { ++ /* ++ * Apparently there's no error checking to be done here? Why does ++ * OSSL_PARAM_construct_utf8_string() want a non-const for the value? ++ * Is that a bug in OpenSSL? ++ */ ++ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, ++ curve_sn_dup, 0); ++ params[1] = OSSL_PARAM_construct_end(); ++ ++ if ((pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL) ++ ret = hx509_enomem(context); ++ } ++ if (ret == 0 && EVP_PKEY_fromdata_init(pctx) != 1) ++ ret = hx509_enomem(context); ++ if (ret == 0 && ++ EVP_PKEY_fromdata(pctx, &template, ++ OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, params) != 1) ++ hx509_set_error_string(context, 0, ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT, ++ "Could not set up to parse key for curve %s", ++ curve_sn); ++ ++ /* Finally we can decode the subjectPublicKey */ ++ p = spi->subjectPublicKey.data; ++ len = spi->subjectPublicKey.length / 8; ++ if (ret == 0 && ++ (public = d2i_PublicKey(EVP_PKEY_EC, &template, &p, len)) == NULL) ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT; ++ ++ /* EVP_DigestVerifyInit() will allocate a new pctx */ ++ EVP_PKEY_CTX_free(pctx); ++ pctx = NULL; ++ ++ if (ret == 0 && ++ EVP_DigestVerifyInit(mdctx, &pctx, md, NULL, public) != 1) ++ hx509_set_error_string(context, 0, ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT, ++ "Could not initialize " ++ "OpenSSL signature verification"); ++ if (ret == 0 && ++ EVP_DigestVerifyUpdate(mdctx, data->data, data->length) != 1) ++ hx509_set_error_string(context, 0, ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT, ++ "Could not initialize " ++ "OpenSSL signature verification"); ++ if (ret == 0 && ++ EVP_DigestVerifyFinal(mdctx, sig->data, sig->length) != 1) ++ hx509_set_error_string(context, 0, ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT, ++ "Signature verification failed"); ++ ++ EVP_MD_CTX_free(mdctx); ++ EVP_PKEY_free(template); ++ free(curve_sn_dup); ++ return ret; ++#else + const AlgorithmIdentifier *digest_alg; + const SubjectPublicKeyInfo *spi; + heim_octet_string digest; +@@ -153,28 +297,28 @@ ecdsa_verify_signature(hx509_context context, + digest_alg = sig_alg->digest_alg; + + ret = _hx509_create_signature(context, +- NULL, +- digest_alg, +- data, +- NULL, +- &digest); ++ NULL, ++ digest_alg, ++ data, ++ NULL, ++ &digest); + if (ret) +- return ret; ++ return ret; + + /* set up EC KEY */ + spi = &signer->tbsCertificate.subjectPublicKeyInfo; + + if (der_heim_oid_cmp(&spi->algorithm.algorithm, ASN1_OID_ID_ECPUBLICKEY) != 0) +- return HX509_CRYPTO_SIG_INVALID_FORMAT; ++ return HX509_CRYPTO_SIG_INVALID_FORMAT; + + /* + * Find the group id + */ + +- ret = parse_ECParameters(context, spi->algorithm.parameters, &groupnid); ++ ret = ECParameters2nid(context, spi->algorithm.parameters, &groupnid); + if (ret) { +- der_free_octet_string(&digest); +- return ret; ++ der_free_octet_string(&digest); ++ return ret; + } + + /* +@@ -190,20 +334,21 @@ ecdsa_verify_signature(hx509_context context, + len = spi->subjectPublicKey.length / 8; + + if (o2i_ECPublicKey(&key, &p, len) == NULL) { +- EC_KEY_free(key); +- return HX509_CRYPTO_SIG_INVALID_FORMAT; ++ EC_KEY_free(key); ++ return HX509_CRYPTO_SIG_INVALID_FORMAT; + } + + ret = ECDSA_verify(-1, digest.data, digest.length, +- sig->data, sig->length, key); ++ sig->data, sig->length, key); + der_free_octet_string(&digest); + EC_KEY_free(key); + if (ret != 1) { +- ret = HX509_CRYPTO_SIG_INVALID_FORMAT; +- return ret; ++ ret = HX509_CRYPTO_SIG_INVALID_FORMAT; ++ return ret; + } + + return 0; ++#endif + } + + static int +@@ -215,6 +360,56 @@ ecdsa_create_signature(hx509_context context, + AlgorithmIdentifier *signatureAlgorithm, + heim_octet_string *sig) + { ++#ifdef HAVE_OPENSSL_30 ++ const AlgorithmIdentifier *digest_alg = sig_alg->digest_alg; ++ const EVP_MD *md = signature_alg2digest_evp_md(context, digest_alg); ++ EVP_MD_CTX *mdctx = NULL; ++ EVP_PKEY_CTX *pctx = NULL; ++ const heim_oid *sig_oid; ++ int ret = 0; ++ ++ sig->data = NULL; ++ sig->length = 0; ++ if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0) ++ _hx509_abort("internal error passing private key to wrong ops"); ++ ++ sig_oid = sig_alg->sig_oid; ++ digest_alg = sig_alg->digest_alg; ++ ++ if (signatureAlgorithm) ++ ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid, ++ "\x05\x00", 2); ++ mdctx = EVP_MD_CTX_new(); ++ if (mdctx == NULL) ++ ret = hx509_enomem(context); ++ if (ret == 0 && EVP_DigestSignInit(mdctx, &pctx, md, NULL, ++ signer->private_key.ecdsa) != 1) ++ ret = HX509_CMS_FAILED_CREATE_SIGATURE; ++ if (ret == 0 && EVP_DigestSignUpdate(mdctx, data->data, data->length) != 1) ++ ret = HX509_CMS_FAILED_CREATE_SIGATURE; ++ if (ret == 0 && EVP_DigestSignFinal(mdctx, NULL, &sig->length) != 1) ++ ret = HX509_CMS_FAILED_CREATE_SIGATURE; ++ if (ret == 0 && (sig->data = malloc(sig->length)) == NULL) ++ ret = hx509_enomem(context); ++ if (ret == 0 && EVP_DigestSignFinal(mdctx, sig->data, &sig->length) != 1) ++ ret = HX509_CMS_FAILED_CREATE_SIGATURE; ++ ++ if (ret == HX509_CMS_FAILED_CREATE_SIGATURE) { ++ /* XXX Extract error detail from OpenSSL */ ++ hx509_set_error_string(context, 0, ret, ++ "ECDSA sign failed"); ++ } ++ ++ if (ret) { ++ if (signatureAlgorithm) ++ free_AlgorithmIdentifier(signatureAlgorithm); ++ free(sig->data); ++ sig->data = NULL; ++ sig->length = 0; ++ } ++ EVP_MD_CTX_free(mdctx); ++ return ret; ++#else + const AlgorithmIdentifier *digest_alg; + heim_octet_string indata; + const heim_oid *sig_oid; +@@ -222,7 +417,7 @@ ecdsa_create_signature(hx509_context context, + int ret; + + if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0) +- _hx509_abort("internal error passing private key to wrong ops"); ++ _hx509_abort("internal error passing private key to wrong ops"); + + sig_oid = sig_alg->sig_oid; + digest_alg = sig_alg->digest_alg; +@@ -230,59 +425,63 @@ ecdsa_create_signature(hx509_context context, + if (signatureAlgorithm) { + ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid, + "\x05\x00", 2); +- if (ret) { +- hx509_clear_error_string(context); +- return ret; +- } ++ if (ret) { ++ hx509_clear_error_string(context); ++ return ret; ++ } + } + + ret = _hx509_create_signature(context, +- NULL, +- digest_alg, +- data, +- NULL, +- &indata); ++ NULL, ++ digest_alg, ++ data, ++ NULL, ++ &indata); + if (ret) +- goto error; ++ goto error; + + sig->length = ECDSA_size(signer->private_key.ecdsa); + sig->data = malloc(sig->length); + if (sig->data == NULL) { +- der_free_octet_string(&indata); +- ret = ENOMEM; +- hx509_set_error_string(context, 0, ret, "out of memory"); +- goto error; ++ der_free_octet_string(&indata); ++ ret = ENOMEM; ++ hx509_set_error_string(context, 0, ret, "out of memory"); ++ goto error; + } + + siglen = sig->length; + + ret = ECDSA_sign(-1, indata.data, indata.length, +- sig->data, &siglen, signer->private_key.ecdsa); ++ sig->data, &siglen, signer->private_key.ecdsa); + der_free_octet_string(&indata); + if (ret != 1) { +- ret = HX509_CMS_FAILED_CREATE_SIGATURE; +- hx509_set_error_string(context, 0, ret, +- "ECDSA sign failed: %d", ret); +- goto error; ++ ret = HX509_CMS_FAILED_CREATE_SIGATURE; ++ hx509_set_error_string(context, 0, ret, ++ "ECDSA sign failed: %d", ret); ++ goto error; + } + if (siglen > sig->length) +- _hx509_abort("ECDSA signature prelen longer the output len"); ++ _hx509_abort("ECDSA signature prelen longer the output len"); + + sig->length = siglen; + + return 0; +- error: ++error: + if (signatureAlgorithm) +- free_AlgorithmIdentifier(signatureAlgorithm); ++ free_AlgorithmIdentifier(signatureAlgorithm); + return ret; ++#endif + } + + static int + ecdsa_available(const hx509_private_key signer, + const AlgorithmIdentifier *sig_alg) + { ++#ifdef HAVE_OPENSSL_30 + const struct signature_alg *sig; +- const EC_GROUP *group; ++ size_t group_name_len = 0; ++ char group_name_buf[96]; ++ EC_GROUP *group = NULL; + BN_CTX *bnctx = NULL; + BIGNUM *order = NULL; + int ret = 0; +@@ -291,34 +490,75 @@ ecdsa_available(const hx509_private_key signer, + _hx509_abort("internal error passing private key to wrong ops"); + + sig = _hx509_find_sig_alg(&sig_alg->algorithm); +- + if (sig == NULL || sig->digest_size == 0) + return 0; + ++ if (EVP_PKEY_get_group_name(signer->private_key.ecdsa, group_name_buf, ++ sizeof(group_name_buf), ++ &group_name_len) != 1 || ++ group_name_len >= sizeof(group_name_buf)) { ++ return 0; ++ } ++ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(group_name_buf)); ++ bnctx = BN_CTX_new(); ++ order = BN_new(); ++ if (group && bnctx && order && ++ EC_GROUP_get_order(group, order, bnctx) == 1) ++ ret = 1; ++ ++#if 0 ++ /* ++ * If anything, require a digest at least as wide as the EC key size ++ * ++ * if (BN_num_bytes(order) > sig->digest_size) ++ * ret = 0; ++ */ ++#endif ++ ++ BN_CTX_free(bnctx); ++ BN_clear_free(order); ++ EC_GROUP_free(group); ++ return ret; ++#else ++ const struct signature_alg *sig; ++ const EC_GROUP *group; ++ BN_CTX *bnctx = NULL; ++ BIGNUM *order = NULL; ++ int ret = 0; ++ ++ if (der_heim_oid_cmp(signer->ops->key_oid, &asn1_oid_id_ecPublicKey) != 0) ++ _hx509_abort("internal error passing private key to wrong ops"); ++ ++ sig = _hx509_find_sig_alg(&sig_alg->algorithm); ++ ++ if (sig == NULL || sig->digest_size == 0) ++ return 0; ++ + group = EC_KEY_get0_group(signer->private_key.ecdsa); + if (group == NULL) +- return 0; ++ return 0; + + bnctx = BN_CTX_new(); + order = BN_new(); + if (order == NULL) +- goto err; ++ goto err; + + if (EC_GROUP_get_order(group, order, bnctx) != 1) +- goto err; ++ goto err; + + #if 0 + /* If anything, require a digest at least as wide as the EC key size */ + if (BN_num_bytes(order) > sig->digest_size) + #endif +- ret = 1; ++ ret = 1; + err: + if (bnctx) +- BN_CTX_free(bnctx); ++ BN_CTX_free(bnctx); + if (order) +- BN_clear_free(order); ++ BN_clear_free(order); + +- return ret; ++ return ret; ++#endif + } + + static int +@@ -347,55 +587,119 @@ ecdsa_private_key_import(hx509_context context, + hx509_key_format_t format, + hx509_private_key private_key) + { ++#ifdef HAVE_OPENSSL_30 ++ const unsigned char *p = data; ++ EVP_PKEY *key = NULL; ++ int ret = 0; ++ ++ switch (format) { ++ case HX509_KEY_FORMAT_PKCS8: ++ key = d2i_PrivateKey(EVP_PKEY_EC, NULL, &p, len); ++ if (key == NULL) { ++ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, ++ "Failed to parse EC private key"); ++ return HX509_PARSING_KEY_FAILED; ++ } ++ break; ++ ++ default: ++ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; ++ } ++ ++ /* ++ * We used to have to call EC_KEY_new(), then EC_KEY_set_group() the group ++ * (curve) on the resulting EC_KEY _before_ we could d2i_ECPrivateKey() the ++ * key, but that's all deprecated in OpenSSL 3.0. ++ * ++ * In fact, it's not clear how ever to assign a group to a private key, ++ * but that's what the documentation for d2i_PrivateKey() says: that ++ * its `EVP_PKEY **' argument must be non-NULL pointing to a key that ++ * has had the group set. ++ * ++ * However, from code inspection it's clear that when the ECParameters ++ * are present in the private key payload passed to d2i_PrivateKey(), ++ * the group will be taken from that. ++ * ++ * What we'll do is that if we have `keyai->parameters' we'll check if the ++ * key we got is for the same group. ++ */ ++ if (keyai->parameters) { ++ size_t gname_len = 0; ++ char buf[96]; ++ int got_group_nid = NID_undef; ++ int want_groupnid = NID_undef; ++ ++ ret = ECParameters2nid(context, keyai->parameters, &want_groupnid); ++ if (ret == 0 && ++ (EVP_PKEY_get_group_name(key, buf, sizeof(buf), &gname_len) != 1 || ++ gname_len >= sizeof(buf))) ++ ret = HX509_ALG_NOT_SUPP; ++ if (ret == 0) ++ got_group_nid = OBJ_txt2nid(buf); ++ if (ret == 0 && ++ (got_group_nid == NID_undef || want_groupnid != got_group_nid)) ++ ret = HX509_ALG_NOT_SUPP; ++ } ++ ++ if (ret == 0) { ++ private_key->private_key.ecdsa = key; ++ private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; ++ key = NULL; ++ } ++ ++ EVP_PKEY_free(key); ++ return ret; ++#else + const unsigned char *p = data; + EC_KEY **pkey = NULL; + EC_KEY *key; + + if (keyai->parameters) { +- EC_GROUP *group; +- int groupnid; +- int ret; +- +- ret = parse_ECParameters(context, keyai->parameters, &groupnid); +- if (ret) +- return ret; +- +- key = EC_KEY_new(); +- if (key == NULL) +- return ENOMEM; +- +- group = EC_GROUP_new_by_curve_name(groupnid); +- if (group == NULL) { +- EC_KEY_free(key); +- return ENOMEM; +- } +- EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); +- if (EC_KEY_set_group(key, group) == 0) { +- EC_KEY_free(key); +- EC_GROUP_free(group); +- return ENOMEM; +- } +- EC_GROUP_free(group); +- pkey = &key; ++ EC_GROUP *group; ++ int groupnid; ++ int ret; ++ ++ ret = ECParameters2nid(context, keyai->parameters, &groupnid); ++ if (ret) ++ return ret; ++ ++ key = EC_KEY_new(); ++ if (key == NULL) ++ return ENOMEM; ++ ++ group = EC_GROUP_new_by_curve_name(groupnid); ++ if (group == NULL) { ++ EC_KEY_free(key); ++ return ENOMEM; ++ } ++ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); ++ if (EC_KEY_set_group(key, group) != 1) { ++ EC_KEY_free(key); ++ EC_GROUP_free(group); ++ return ENOMEM; ++ } ++ EC_GROUP_free(group); ++ pkey = &key; + } + + switch (format) { + case HX509_KEY_FORMAT_DER: + +- private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len); +- if (private_key->private_key.ecdsa == NULL) { +- hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, +- "Failed to parse EC private key"); +- return HX509_PARSING_KEY_FAILED; +- } +- private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; +- break; ++ private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len); ++ if (private_key->private_key.ecdsa == NULL) { ++ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, ++ "Failed to parse EC private key"); ++ return HX509_PARSING_KEY_FAILED; ++ } ++ private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; ++ break; + + default: +- return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; ++ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; + } + + return 0; ++#endif + } + + static int +diff --git a/lib/hx509/libhx509-exports.def b/lib/hx509/libhx509-exports.def +index f441773015..8bd98fc349 100644 +--- a/lib/hx509/libhx509-exports.def ++++ b/lib/hx509/libhx509-exports.def +@@ -14,6 +14,11 @@ EXPORTS + _hx509_generate_private_key_is_ca + _hx509_map_file_os + _hx509_name_from_Name ++ _hx509_private_key_export ++ _hx509_private_key_exportable ++ _hx509_private_key_get_internal ++ _hx509_private_key_oid ++ _hx509_private_key_ref + hx509_private_key2SPKI + hx509_private_key_free + _hx509_private_key_ref +diff --git a/lib/hx509/version-script.map b/lib/hx509/version-script.map +index f040cd8344..15a8aa7d4d 100644 +--- a/lib/hx509/version-script.map ++++ b/lib/hx509/version-script.map +@@ -16,6 +16,7 @@ HEIMDAL_X509_1.2 { + _hx509_generate_private_key_is_ca; + _hx509_map_file_os; + _hx509_name_from_Name; ++ _hx509_ossl_oid2nid; + _hx509_private_key_ref; + _hx509_request_add_dns_name; + _hx509_request_add_email; +@@ -252,4 +253,3 @@ HEIMDAL_X509_1.3 { + global: + hx509_ca_tbs_set_signature_algorithm; + }; +- +From 7e818fa77eb980a7bddd043c26c3d2028a1c9450 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Sat, 19 Nov 2022 15:09:47 -0600 +Subject: [PATCH 11/12] krb5: OpenSSL 3.0 support + +(cherry picked from commit 6336cf69d43665a6e97434be453ed895f4b25509) +--- + lib/krb5/pkinit-ec.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + +diff --git a/lib/krb5/pkinit-ec.c b/lib/krb5/pkinit-ec.c +index 33bc62c8dc..34cefd506f 100644 +--- a/lib/krb5/pkinit-ec.c ++++ b/lib/krb5/pkinit-ec.c +@@ -56,6 +56,7 @@ + #include + #include + #include ++#include + #define HEIM_NO_CRYPTO_HDRS + #endif + +@@ -125,6 +126,9 @@ _krb5_build_authpack_subjectPK_EC(krb5_context context, + if (ret) + return ret; + ++#ifdef HAVE_OPENSSL_30 ++ ctx->u.eckey = EVP_EC_gen(OSSL_EC_curve_nid2name(NID_X9_62_prime256v1)); ++#else + ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ctx->u.eckey == NULL) + return krb5_enomem(context); +@@ -132,8 +136,13 @@ _krb5_build_authpack_subjectPK_EC(krb5_context context, + ret = EC_KEY_generate_key(ctx->u.eckey); + if (ret != 1) + return EINVAL; ++#endif + ++#ifdef HAVE_OPENSSL_30 ++ xlen = i2d_PublicKey(ctx->u.eckey, NULL); ++#else + xlen = i2o_ECPublicKey(ctx->u.eckey, NULL); ++#endif + if (xlen <= 0) + return EINVAL; + +@@ -143,7 +152,11 @@ _krb5_build_authpack_subjectPK_EC(krb5_context context, + + a->clientPublicValue->subjectPublicKey.data = p; + ++#ifdef HAVE_OPENSSL_30 ++ xlen = i2d_PublicKey(ctx->u.eckey, &p); ++#else + xlen = i2o_ECPublicKey(ctx->u.eckey, &p); ++#endif + if (xlen <= 0) { + a->clientPublicValue->subjectPublicKey.data = NULL; + free(p); +@@ -171,6 +184,61 @@ _krb5_pk_rd_pa_reply_ecdh_compute_key(krb5_context context, + int *out_sz) + { + #ifdef HAVE_HCRYPTO_W_OPENSSL ++#ifdef HAVE_OPENSSL_30 ++ krb5_error_code ret = 0; ++ EVP_PKEY_CTX *pctx = NULL; ++ EVP_PKEY *template = NULL; ++ EVP_PKEY *public = NULL; ++ size_t shared_len = 0; ++ ++ if ((template = EVP_PKEY_new()) == NULL) ++ ret = krb5_enomem(context); ++ if (ret == 0 && ++ EVP_PKEY_copy_parameters(template, ctx->u.eckey) != 1) ++ ret = krb5_enomem(context); ++ if (ret == 0 && (pctx = EVP_PKEY_CTX_new(ctx->u.eckey, NULL)) == NULL) ++ ret = krb5_enomem(context); ++ if (ret == 0 && EVP_PKEY_derive_init(pctx) != 1) ++ ret = krb5_enomem(context); ++ if (ret == 0 && ++ EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_NONE) != 1) ++ ret = krb5_enomem(context); ++ if (ret == 0 && ++ (public = d2i_PublicKey(EVP_PKEY_EC, &template, &in, in_sz)) == NULL) ++ krb5_set_error_message(context, ++ ret = HX509_PARSING_KEY_FAILED, ++ "PKINIT: Can't parse the KDC's ECDH public key"); ++ if (ret == 0 && ++ EVP_PKEY_derive_set_peer_ex(pctx, public, 1) != 1) ++ krb5_set_error_message(context, ++ ret = KRB5KRB_ERR_GENERIC, ++ "Could not derive ECDH shared secret for PKINIT key exchange " ++ "(EVP_PKEY_derive_set_peer_ex)"); ++ if (ret == 0 && ++ (EVP_PKEY_derive(pctx, NULL, &shared_len) != 1 || shared_len == 0)) ++ krb5_set_error_message(context, ++ ret = KRB5KRB_ERR_GENERIC, ++ "Could not derive ECDH shared secret for PKINIT key exchange " ++ "(EVP_PKEY_derive to get length)"); ++ if (ret == 0 && shared_len > INT_MAX) ++ krb5_set_error_message(context, ++ ret = KRB5KRB_ERR_GENERIC, ++ "Could not derive ECDH shared secret for PKINIT key exchange " ++ "(shared key too large)"); ++ if (ret == 0 && (*out = malloc(shared_len)) == NULL) ++ ret = krb5_enomem(context); ++ if (ret == 0 && EVP_PKEY_derive(pctx, *out, &shared_len) != 1) ++ krb5_set_error_message(context, ++ ret = KRB5KRB_ERR_GENERIC, ++ "Could not derive ECDH shared secret for PKINIT key exchange " ++ "(EVP_PKEY_derive)"); ++ if (ret == 0) ++ *out_sz = shared_len; ++ EVP_PKEY_CTX_free(pctx); // move ++ EVP_PKEY_free(template); ++ ++ return ret; ++#else + krb5_error_code ret = 0; + int dh_gen_keylen; + +@@ -219,6 +287,7 @@ _krb5_pk_rd_pa_reply_ecdh_compute_key(krb5_context context, + *out_sz = dh_gen_keylen; + + return ret; ++#endif + #else + krb5_set_error_message(context, ENOTSUP, + N_("PKINIT: ECDH not supported", "")); +@@ -230,8 +299,12 @@ void + _krb5_pk_eckey_free(void *eckey) + { + #ifdef HAVE_HCRYPTO_W_OPENSSL ++#ifdef HAVE_OPENSSL_30 ++ EVP_PKEY_free(eckey); ++#else + EC_KEY_free(eckey); + #endif ++#endif + } + + #else + +From cad7cd5ce178b2e5f03b383c559f980a00b20063 Mon Sep 17 00:00:00 2001 +From: Nicolas Williams +Date: Sat, 19 Nov 2022 22:03:50 -0600 +Subject: [PATCH 12/12] kdc: OpenSSL 3.0 support + +(cherry picked from commit cd02c50be56e4345373e009ed6950b9550df94ff) +--- + kdc/pkinit-ec.c | 350 +++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 290 insertions(+), 60 deletions(-) + +diff --git a/kdc/pkinit-ec.c b/kdc/pkinit-ec.c +index c718aa7996..31a5fe7dec 100644 +--- a/kdc/pkinit-ec.c ++++ b/kdc/pkinit-ec.c +@@ -52,10 +52,16 @@ + */ + + #ifdef HAVE_HCRYPTO_W_OPENSSL +-#include +-#include + #include ++#include ++#include ++#include + #include ++#include ++#include ++#ifdef HAVE_OPENSSL_30 ++#include ++#endif + #define HEIM_NO_CRYPTO_HDRS + #endif /* HAVE_HCRYPTO_W_OPENSSL */ + +@@ -69,37 +75,101 @@ + #include + + #include +- +-#ifdef HAVE_HCRYPTO_W_OPENSSL +-static void +-free_client_ec_param(krb5_context context, +- EC_KEY *ec_key_pk, +- EC_KEY *ec_key_key) +-{ +- if (ec_key_pk != NULL) +- EC_KEY_free(ec_key_pk); +- if (ec_key_key != NULL) +- EC_KEY_free(ec_key_key); +-} +-#endif ++#include "../lib/hx509/hx_locl.h" ++#include + + void + _kdc_pk_free_client_ec_param(krb5_context context, +- void *ec_key_pk, +- void *ec_key_key) ++ void *k0, ++ void *k1) + { + #ifdef HAVE_HCRYPTO_W_OPENSSL +- free_client_ec_param(context, ec_key_pk, ec_key_key); ++#ifdef HAVE_OPENSSL_30 ++ EVP_PKEY_free(k0); ++ EVP_PKEY_free(k1); ++#else ++ EC_KEY_free(k0); ++ EC_KEY_free(k1); ++#endif + #endif + } + + #ifdef HAVE_HCRYPTO_W_OPENSSL ++#ifdef HAVE_OPENSSL_30 ++static krb5_error_code ++generate_ecdh_keyblock_ossl30(krb5_context context, ++ EVP_PKEY *ec_key_pub, /* the client's public key */ ++ EVP_PKEY **ec_key_priv, /* the KDC's ephemeral private */ ++ unsigned char **dh_gen_key, /* shared secret */ ++ size_t *dh_gen_keylen) ++{ ++ EVP_PKEY_CTX *pctx = NULL; ++ EVP_PKEY *ephemeral = NULL; ++ krb5_error_code ret = 0; ++ unsigned char *p = NULL; ++ size_t size = 0; ++ ++ if (ec_key_pub == NULL) ++ /* XXX This seems like an internal error that should be impossible */ ++ krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, ++ "Missing client ECDH key agreement public key"); ++ if (ret == 0 && ++ (ephemeral = ++ EVP_EC_gen(OSSL_EC_curve_nid2name(NID_X9_62_prime256v1))) == NULL) ++ krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, ++ "Could not generate an ECDH key agreement private key"); ++ if (ret == 0 && ++ (pctx = EVP_PKEY_CTX_new(ephemeral, NULL)) == NULL) ++ ret = krb5_enomem(context); ++ if (ret == 0 && EVP_PKEY_derive_init(pctx) != 1) ++ ret = krb5_enomem(context); ++ if (ret == 0 && ++ EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_NONE) != 1) ++ krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, ++ "Could not generate an ECDH key agreement private key " ++ "(EVP_PKEY_CTX_set_dh_kdf_type)"); ++ if (ret == 0 && ++ EVP_PKEY_derive_set_peer_ex(pctx, ec_key_pub, 1) != 1) ++ krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, ++ "Could not generate an ECDH key agreement private key " ++ "(EVP_PKEY_derive_set_peer_ex)"); ++ if (ret == 0 && ++ (EVP_PKEY_derive(pctx, NULL, &size) != 1 || size == 0)) ++ krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, ++ "Could not generate an ECDH key agreement private key " ++ "(EVP_PKEY_derive)"); ++ if (ret == 0 && (p = malloc(size)) == NULL) ++ ret = krb5_enomem(context); ++ if (ret == 0 && ++ (EVP_PKEY_derive(pctx, p, &size) != 1 || size == 0)) ++ krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, ++ "Could not generate an ECDH key agreement private key " ++ "(EVP_PKEY_derive)"); ++ ++ if (ret) { ++ EVP_PKEY_free(ephemeral); ++ ephemeral = NULL; ++ free(p); ++ p = NULL; ++ size = 0; ++ } ++ ++ *ec_key_priv = ephemeral; ++ *dh_gen_keylen = size; ++ *dh_gen_key = p; ++ ++ EVP_PKEY_CTX_free(pctx); ++ return ret; ++} ++#else ++ ++/* The empty line above is intentional to work around an mkproto bug */ + static krb5_error_code +-generate_ecdh_keyblock(krb5_context context, +- EC_KEY *ec_key_pk, /* the client's public key */ +- EC_KEY **ec_key_key, /* the KDC's ephemeral private */ +- unsigned char **dh_gen_key, /* shared secret */ +- size_t *dh_gen_keylen) ++generate_ecdh_keyblock_ossl11(krb5_context context, ++ EC_KEY *ec_key_pk, /* the client's public key */ ++ EC_KEY **ec_key_key, /* the KDC's ephemeral private */ ++ unsigned char **dh_gen_key, /* shared secret */ ++ size_t *dh_gen_keylen) + { + const EC_GROUP *group; + EC_KEY *ephemeral; +@@ -136,7 +206,7 @@ generate_ecdh_keyblock(krb5_context context, + EC_KEY_set_group(ephemeral, group); + + if (EC_KEY_generate_key(ephemeral) != 1) { +- EC_KEY_free(ephemeral); ++ EC_KEY_free(ephemeral); + return krb5_enomem(context); + } + +@@ -165,6 +235,7 @@ generate_ecdh_keyblock(krb5_context context, + + return 0; + } ++#endif + #endif /* HAVE_HCRYPTO_W_OPENSSL */ + + krb5_error_code +@@ -175,20 +246,128 @@ _kdc_generate_ecdh_keyblock(krb5_context context, + size_t *dh_gen_keylen) + { + #ifdef HAVE_HCRYPTO_W_OPENSSL +- return generate_ecdh_keyblock(context, ec_key_pk, +- (EC_KEY **)ec_key_key, +- dh_gen_key, dh_gen_keylen); ++#ifdef HAVE_OPENSSL_30 ++ return generate_ecdh_keyblock_ossl30(context, ec_key_pk, ++ (EVP_PKEY **)ec_key_key, ++ dh_gen_key, dh_gen_keylen); ++#else ++ return generate_ecdh_keyblock_ossl11(context, ec_key_pk, ++ (EC_KEY **)ec_key_key, ++ dh_gen_key, dh_gen_keylen); ++#endif + #else + return ENOTSUP; + #endif /* HAVE_HCRYPTO_W_OPENSSL */ + } + + #ifdef HAVE_HCRYPTO_W_OPENSSL ++#ifdef HAVE_OPENSSL_30 + static krb5_error_code +-get_ecdh_param(krb5_context context, +- krb5_kdc_configuration *config, +- SubjectPublicKeyInfo *dh_key_info, +- EC_KEY **out) ++get_ecdh_param_ossl30(krb5_context context, ++ krb5_kdc_configuration *config, ++ SubjectPublicKeyInfo *dh_key_info, ++ EVP_PKEY **out) ++{ ++ EVP_PKEY_CTX *pctx = NULL; ++ EVP_PKEY *template = NULL; ++ EVP_PKEY *public = NULL; ++ OSSL_PARAM params[2]; ++ krb5_error_code ret = 0; ++ ECParameters ecp; ++ const unsigned char *p; ++ const char *curve_sn = NULL; ++ size_t len; ++ char *curve_sn_dup = NULL; ++ int groupnid = NID_undef; ++ ++ /* XXX Algorithm agility; XXX KRB5_BADMSGTYPE?? */ ++ ++ /* ++ * In order for d2i_PublicKey() to work we need to create a template key ++ * that has the curve parameters for the subjectPublicKey. ++ * ++ * Or maybe we could learn to use the OSSL_DECODER(3) API. But this works, ++ * at least until OpenSSL deprecates d2i_PublicKey() and forces us to use ++ * OSSL_DECODER(3). ++ */ ++ ++ memset(&ecp, 0, sizeof(ecp)); ++ ++ if (dh_key_info->algorithm.parameters == NULL) ++ krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, ++ "PKINIT missing algorithm parameter " ++ "in clientPublicValue"); ++ if (ret == 0) ++ ret = decode_ECParameters(dh_key_info->algorithm.parameters->data, ++ dh_key_info->algorithm.parameters->length, ++ &ecp, &len); ++ if (ret == 0 && ecp.element != choice_ECParameters_namedCurve) ++ krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, ++ "PKINIT client used an unnamed curve"); ++ if (ret == 0 && ++ (groupnid = _hx509_ossl_oid2nid(&ecp.u.namedCurve)) == NID_undef) ++ krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, ++ "PKINIT client used an unsupported curve"); ++ if (ret == 0 && (curve_sn = OBJ_nid2sn(groupnid)) == NULL) ++ krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, ++ "Could not resolve curve NID %d to its short name", ++ groupnid); ++ if (ret == 0 && (curve_sn_dup = strdup(curve_sn)) == NULL) ++ ret = krb5_enomem(context); ++ if (ret == 0) { ++ if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) != 0) ++ krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, ++ "PKINIT client used an unsupported curve"); ++ } ++ if (ret == 0) { ++ /* ++ * Apparently there's no error checking to be done here? Why does ++ * OSSL_PARAM_construct_utf8_string() want a non-const for the value? ++ * Is that a bug in OpenSSL? ++ */ ++ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, ++ curve_sn_dup, 0); ++ params[1] = OSSL_PARAM_construct_end(); ++ ++ if ((pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL) ++ ret = krb5_enomem(context); ++ } ++ if (ret == 0 && EVP_PKEY_fromdata_init(pctx) != 1) ++ ret = krb5_enomem(context); ++ if (ret == 0 && ++ EVP_PKEY_fromdata(pctx, &template, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, ++ params) != 1) ++ krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, ++ "Could not set up to parse key for curve %s", ++ curve_sn); ++ ++ p = dh_key_info->subjectPublicKey.data; ++ len = dh_key_info->subjectPublicKey.length / 8; ++ if (ret == 0 && ++ (public = d2i_PublicKey(EVP_PKEY_EC, &template, &p, len)) == NULL) ++ krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, ++ "Could not decode PKINIT client ECDH key"); ++ ++ if (ret) { ++ EVP_PKEY_free(public); ++ public = NULL; ++ } ++ ++ *out = public; ++ ++ /* FYI the EVP_PKEY_CTX takes ownership of the `template' key */ ++ EVP_PKEY_CTX_free(pctx); ++ free_ECParameters(&ecp); ++ free(curve_sn_dup); ++ return ret; ++} ++#else ++ ++static krb5_error_code ++get_ecdh_param_ossl11(krb5_context context, ++ krb5_kdc_configuration *config, ++ SubjectPublicKeyInfo *dh_key_info, ++ EC_KEY **out) + { + ECParameters ecp; + EC_KEY *public = NULL; +@@ -198,30 +377,31 @@ get_ecdh_param(krb5_context context, + int nid; + + if (dh_key_info->algorithm.parameters == NULL) { +- krb5_set_error_message(context, KRB5_BADMSGTYPE, +- "PKINIT missing algorithm parameter " +- "in clientPublicValue"); +- return KRB5_BADMSGTYPE; ++ krb5_set_error_message(context, KRB5_BADMSGTYPE, ++ "PKINIT missing algorithm parameter " ++ "in clientPublicValue"); ++ return KRB5_BADMSGTYPE; + } ++ /* XXX Algorithm agility; XXX KRB5_BADMSGTYPE?? */ + + memset(&ecp, 0, sizeof(ecp)); + + ret = decode_ECParameters(dh_key_info->algorithm.parameters->data, +- dh_key_info->algorithm.parameters->length, &ecp, &len); ++ dh_key_info->algorithm.parameters->length, &ecp, &len); + if (ret) +- goto out; ++ goto out; + + if (ecp.element != choice_ECParameters_namedCurve) { +- ret = KRB5_BADMSGTYPE; +- goto out; ++ ret = KRB5_BADMSGTYPE; ++ goto out; + } + + if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0) +- nid = NID_X9_62_prime256v1; ++ nid = NID_X9_62_prime256v1; + else { +- ret = KRB5_BADMSGTYPE; +- goto out; +- } ++ ret = KRB5_BADMSGTYPE; ++ goto out; ++ } + + /* XXX verify group is ok */ + +@@ -230,20 +410,21 @@ get_ecdh_param(krb5_context context, + p = dh_key_info->subjectPublicKey.data; + len = dh_key_info->subjectPublicKey.length / 8; + if (o2i_ECPublicKey(&public, &p, len) == NULL) { +- ret = KRB5_BADMSGTYPE; +- krb5_set_error_message(context, ret, +- "PKINIT failed to decode ECDH key"); +- goto out; ++ ret = KRB5_BADMSGTYPE; ++ krb5_set_error_message(context, ret, ++ "PKINIT failed to decode ECDH key"); ++ goto out; + } + *out = public; + public = NULL; + + out: + if (public) +- EC_KEY_free(public); ++ EC_KEY_free(public); + free_ECParameters(&ecp); + return ret; + } ++#endif + #endif /* HAVE_HCRYPTO_W_OPENSSL */ + + krb5_error_code +@@ -253,7 +434,11 @@ _kdc_get_ecdh_param(krb5_context context, + void **out) + { + #ifdef HAVE_HCRYPTO_W_OPENSSL +- return get_ecdh_param(context, config, dh_key_info, (EC_KEY **)out); ++#ifdef HAVE_OPENSSL_30 ++ return get_ecdh_param_ossl30(context, config, dh_key_info, (EVP_PKEY **)out); ++#else ++ return get_ecdh_param_ossl11(context, config, dh_key_info, (EC_KEY **)out); ++#endif + #else + return ENOTSUP; + #endif /* HAVE_HCRYPTO_W_OPENSSL */ +@@ -265,13 +450,51 @@ _kdc_get_ecdh_param(krb5_context context, + */ + + #ifdef HAVE_HCRYPTO_W_OPENSSL ++#ifdef HAVE_OPENSSL_30 + static krb5_error_code +-serialize_ecdh_key(krb5_context context, +- EC_KEY *key, +- unsigned char **out, +- size_t *out_len) ++serialize_ecdh_key_ossl30(krb5_context context, ++ EVP_PKEY *key, ++ unsigned char **out, ++ size_t *out_len) ++{ ++ unsigned char *p; ++ int len; ++ ++ *out = NULL; ++ *out_len = 0; ++ ++ len = i2d_PublicKey(key, NULL); ++ if (len <= 0) { ++ krb5_set_error_message(context, EOVERFLOW, ++ "PKINIT failed to encode ECDH key"); ++ return EOVERFLOW; ++ } ++ ++ *out = malloc(len); ++ if (*out == NULL) ++ return krb5_enomem(context); ++ ++ p = *out; ++ len = i2d_PublicKey(key, &p); ++ if (len <= 0) { ++ free(*out); ++ *out = NULL; ++ krb5_set_error_message(context, EINVAL /* XXX Better error please */, ++ "PKINIT failed to encode ECDH key"); ++ return EINVAL; ++ } ++ ++ *out_len = len * 8; ++ return 0; ++} ++#else ++ ++static krb5_error_code ++serialize_ecdh_key_ossl11(krb5_context context, ++ EC_KEY *key, ++ unsigned char **out, ++ size_t *out_len) + { +- krb5_error_code ret = 0; + unsigned char *p; + int len; + +@@ -279,8 +502,11 @@ serialize_ecdh_key(krb5_context context, + *out_len = 0; + + len = i2o_ECPublicKey(key, NULL); +- if (len <= 0) ++ if (len <= 0) { ++ krb5_set_error_message(context, EOVERFLOW, ++ "PKINIT failed to encode ECDH key"); + return EOVERFLOW; ++ } + + *out = malloc(len); + if (*out == NULL) +@@ -291,16 +517,16 @@ serialize_ecdh_key(krb5_context context, + if (len <= 0) { + free(*out); + *out = NULL; +- ret = EINVAL; /* XXX Better error please */ +- krb5_set_error_message(context, ret, ++ krb5_set_error_message(context, EINVAL /* XXX Better error please */, + "PKINIT failed to encode ECDH key"); +- return ret; ++ return EINVAL; + } + + *out_len = len * 8; +- return ret; ++ return 0; + } + #endif ++#endif + + krb5_error_code + _kdc_serialize_ecdh_key(krb5_context context, +@@ -309,7 +535,11 @@ _kdc_serialize_ecdh_key(krb5_context context, + size_t *out_len) + { + #ifdef HAVE_HCRYPTO_W_OPENSSL +- return serialize_ecdh_key(context, key, out, out_len); ++#ifdef HAVE_OPENSSL_30 ++ return serialize_ecdh_key_ossl30(context, key, out, out_len); ++#else ++ return serialize_ecdh_key_ossl11(context, key, out, out_len); ++#endif + #else + return ENOTSUP; + #endif diff --git a/heimdal/PKGBUILD b/heimdal/PKGBUILD index 81acf529..3c8621a9 100644 --- a/heimdal/PKGBUILD +++ b/heimdal/PKGBUILD @@ -3,7 +3,7 @@ pkgbase=heimdal pkgname=('heimdal' 'heimdal-libs' 'heimdal-devel') pkgver=7.8.0 -pkgrel=1 +pkgrel=2 pkgdesc="Implementation of Kerberos V5 libraries" arch=('i686' 'x86_64') url="https://www.h5l.org/" @@ -19,7 +19,8 @@ source=(https://github.com/heimdal/heimdal/releases/download/heimdal-${pkgver}/h 1.5.3-missing-libs-pkg-config.patch 1.5.3-fix-detect-libedit.patch 7.5.0-hcrypto-build-fix.patch - 7.5.0-names-clash-with-openssl.patch) + 7.5.0-names-clash-with-openssl.patch + 1041.patch) sha256sums=('fd87a207846fa650fd377219adc4b8a8193e55904d8a752c2c3715b4155d8d38' 'SKIP' 'b18c9ca3f2db08cea0a4b7d7f07e8656360af848b3a30a5fa8d748240989da49' @@ -29,7 +30,8 @@ sha256sums=('fd87a207846fa650fd377219adc4b8a8193e55904d8a752c2c3715b4155d8d38' '5039112bbe27b814df115eca40607b957c13dd40bdc1d2d1ccc62b31fcfe87ac' 'bb96587539084c2ea9e06da8d2b4c768edd8dd422587a045ea60d1aa3eeb6a8a' '1886fcb422902f80246ee104bab79a95bf8c26442038fe32977f17653e45ee2f' - 'bfe321c0391d6f7e23abecc22e9941bcb79d0b67abb9975246a9f462ec32513b') + 'bfe321c0391d6f7e23abecc22e9941bcb79d0b67abb9975246a9f462ec32513b' + 'bff78505a2f8bb29c7bfe60aa0cf4e586c00b1123329d2b840720b67b00d9326') validpgpkeys=('FB925C7AFA000F52B4BBD1ED9A077911BB7DC320') # Heimdal Security prepare() { @@ -45,6 +47,9 @@ prepare() { patch -p1 -i "${srcdir}"/7.5.0-hcrypto-build-fix.patch patch -p1 -i "${srcdir}"/7.5.0-names-clash-with-openssl.patch + # https://github.com/heimdal/heimdal/pull/1041 + patch -p1 -i "${srcdir}"/1041.patch + ./autogen.sh }