From 9f72db9f51a48fc17e698ebbed1fecb527da8109 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 31 Oct 2023 15:11:30 +0100 Subject: [PATCH 01/54] ossl_quic_new(): Fix a leak found by error injection Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22572) Signed-off-by: fly2x --- ssl/quic/quic_impl.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 0c8e1b15a6..dd689865e4 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -384,6 +384,12 @@ SSL *ossl_quic_new(SSL_CTX *ctx) QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); goto err; } +#if defined(OPENSSL_THREADS) + if ((qc->mutex = ossl_crypto_mutex_new()) == NULL) { + QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); + goto err; + } +#endif /* Initialise the QUIC_CONNECTION's stub header. */ ssl_base = &qc->ssl; @@ -406,13 +412,6 @@ SSL *ossl_quic_new(SSL_CTX *ctx) sc->options &= OSSL_QUIC_PERMITTED_OPTIONS_CONN; sc->pha_enabled = 0; -#if defined(OPENSSL_THREADS) - if ((qc->mutex = ossl_crypto_mutex_new()) == NULL) { - QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); - goto err; - } -#endif - #if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST) qc->is_thread_assisted = (ssl_base->method == OSSL_QUIC_client_thread_method()); @@ -450,14 +449,14 @@ SSL *ossl_quic_new(SSL_CTX *ctx) return ssl_base; err: - if (qc != NULL) { + if (ssl_base == NULL) { #if defined(OPENSSL_THREADS) ossl_crypto_mutex_free(qc->mutex); #endif - ossl_quic_channel_free(qc->ch); - SSL_free(qc->tls); + OPENSSL_free(qc); + } else { + SSL_free(ssl_base); } - OPENSSL_free(qc); return NULL; } -- Gitee From fc3572f8a3a63af1f4a21dd882e03a9c9eb6376f Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 26 Oct 2023 11:36:31 +0100 Subject: [PATCH 02/54] QUIC CHANNEL: Set reason string for missing tparams extension Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22523) Signed-off-by: fly2x --- ssl/quic/quic_channel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index a3fcac1696..d3b7947fb4 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -1283,8 +1283,10 @@ static int ch_on_transport_params(const unsigned char *params, QUIC_CONN_ID cid; const char *reason = "bad transport parameter"; - if (ch->got_remote_transport_params) + if (ch->got_remote_transport_params) { + reason = "multiple transport parameter extensions"; goto malformed; + } if (!PACKET_buf_init(&pkt, params, params_len)) { ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_INTERNAL_ERROR, 0, -- Gitee From 87cb644a102394340baa53db30e0430167ac6ff3 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 26 Oct 2023 11:36:51 +0100 Subject: [PATCH 03/54] QUIC WIRE: Refuse integer transport params with trailing body bytes Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22523) Signed-off-by: fly2x --- ssl/quic/quic_wire.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ssl/quic/quic_wire.c b/ssl/quic/quic_wire.c index 6f8da05124..425e7efc2e 100644 --- a/ssl/quic/quic_wire.c +++ b/ssl/quic/quic_wire.c @@ -950,6 +950,9 @@ int ossl_quic_wire_decode_transport_param_int(PACKET *pkt, if (!PACKET_get_quic_vlint(&sub, value)) return 0; + if (PACKET_remaining(&sub) > 0) + return 0; + return 1; } -- Gitee From 55289b36e8ceeff5635f89b82251c59df59ac0dc Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 26 Oct 2023 11:37:21 +0100 Subject: [PATCH 04/54] QUIC QTEST_FAULT: Allow deleted TLS extension to be output Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22523) Signed-off-by: fly2x --- test/helpers/quictestlib.c | 21 +++++++++++++++++++-- test/helpers/quictestlib.h | 6 ++++-- test/quicfaultstest.c | 2 +- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/test/helpers/quictestlib.c b/test/helpers/quictestlib.c index 26cd67e236..2c74b26252 100644 --- a/test/helpers/quictestlib.c +++ b/test/helpers/quictestlib.c @@ -929,12 +929,14 @@ int qtest_fault_resize_message(QTEST_FAULT *fault, size_t newlen) int qtest_fault_delete_extension(QTEST_FAULT *fault, unsigned int exttype, unsigned char *ext, - size_t *extlen) + size_t *extlen, + BUF_MEM *old_ext) { PACKET pkt, sub, subext; + WPACKET old_ext_wpkt; unsigned int type; const unsigned char *start, *end; - size_t newlen; + size_t newlen, w; size_t msglen = fault->handbuflen; if (!PACKET_buf_init(&pkt, ext, *extlen)) @@ -954,6 +956,21 @@ int qtest_fault_delete_extension(QTEST_FAULT *fault, /* Found it */ end = PACKET_data(&sub); + if (old_ext != NULL) { + if (!WPACKET_init(&old_ext_wpkt, old_ext)) + return 0; + + if (!WPACKET_memcpy(&old_ext_wpkt, PACKET_data(&subext), + PACKET_remaining(&subext)) + || !WPACKET_get_total_written(&old_ext_wpkt, &w)) { + WPACKET_cleanup(&old_ext_wpkt); + return 0; + } + + WPACKET_finish(&old_ext_wpkt); + old_ext->length = w; + } + /* * If we're not the last extension we need to move the rest earlier. The * cast below is safe because we own the underlying buffer and we're no diff --git a/test/helpers/quictestlib.h b/test/helpers/quictestlib.h index d1ac350c24..79de17d153 100644 --- a/test/helpers/quictestlib.h +++ b/test/helpers/quictestlib.h @@ -194,11 +194,13 @@ int qtest_fault_resize_message(QTEST_FAULT *fault, size_t newlen); * Helper function to delete an extension from an extension block. |exttype| is * the type of the extension to be deleted. |ext| points to the extension block. * On entry |*extlen| contains the length of the extension block. It is updated - * with the new length on exit. + * with the new length on exit. If old_ext is non-NULL, the deleted extension + * is appended to the given BUF_MEM. */ int qtest_fault_delete_extension(QTEST_FAULT *fault, unsigned int exttype, unsigned char *ext, - size_t *extlen); + size_t *extlen, + BUF_MEM *old_ext); /* * Add additional helper functions for querying extensions here (e.g. diff --git a/test/quicfaultstest.c b/test/quicfaultstest.c index 28f52cd6f2..ad200f7ace 100644 --- a/test/quicfaultstest.c +++ b/test/quicfaultstest.c @@ -168,7 +168,7 @@ static int drop_extensions_cb(QTEST_FAULT *fault, int *ext = (int *)encextcbarg; if (!qtest_fault_delete_extension(fault, *ext, ee->extensions, - &ee->extensionslen)) + &ee->extensionslen, NULL)) return 0; return 1; -- Gitee From cca1d633a8b42cd36270dda1ca2915833630d062 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 26 Oct 2023 11:39:06 +0100 Subject: [PATCH 05/54] QUIC: Test missing/malformed/duplicate/etc. transport parameters Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22523) Signed-off-by: fly2x --- test/quicapitest.c | 517 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 517 insertions(+) diff --git a/test/quicapitest.c b/test/quicapitest.c index 51d6eea245..77c9598c69 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -19,6 +19,7 @@ #include "testutil.h" #include "testutil/output.h" #include "../ssl/ssl_local.h" +#include "internal/quic_error.h" static OSSL_LIB_CTX *libctx = NULL; static OSSL_PROVIDER *defctxnull = NULL; @@ -1553,6 +1554,521 @@ static int test_noisy_dgram(int idx) return testresult; } +enum { + TPARAM_OP_DUP, + TPARAM_OP_DROP, + TPARAM_OP_INJECT, + TPARAM_OP_INJECT_TWICE, + TPARAM_OP_INJECT_RAW, + TPARAM_OP_DROP_INJECT, + TPARAM_OP_MUTATE +}; + +#define TPARAM_CHECK_DUP(name, reason) \ + { QUIC_TPARAM_##name, TPARAM_OP_DUP, (reason) }, +#define TPARAM_CHECK_DROP(name, reason) \ + { QUIC_TPARAM_##name, TPARAM_OP_DROP, (reason) }, +#define TPARAM_CHECK_INJECT(name, buf, buf_len, reason) \ + { QUIC_TPARAM_##name, TPARAM_OP_INJECT, (reason), \ + (buf), (buf_len) }, +#define TPARAM_CHECK_INJECT_A(name, buf, reason) \ + TPARAM_CHECK_INJECT(name, buf, sizeof(buf), reason) +#define TPARAM_CHECK_DROP_INJECT(name, buf, buf_len, reason) \ + { QUIC_TPARAM_##name, TPARAM_OP_DROP_INJECT, (reason), \ + (buf), (buf_len) }, +#define TPARAM_CHECK_DROP_INJECT_A(name, buf, reason) \ + TPARAM_CHECK_DROP_INJECT(name, buf, sizeof(buf), reason) +#define TPARAM_CHECK_INJECT_TWICE(name, buf, buf_len, reason) \ + { QUIC_TPARAM_##name, TPARAM_OP_INJECT_TWICE, (reason), \ + (buf), (buf_len) }, +#define TPARAM_CHECK_INJECT_TWICE_A(name, buf, reason) \ + TPARAM_CHECK_INJECT_TWICE(name, buf, sizeof(buf), reason) +#define TPARAM_CHECK_INJECT_RAW(buf, buf_len, reason) \ + { 0, TPARAM_OP_INJECT_RAW, (reason), \ + (buf), (buf_len) }, +#define TPARAM_CHECK_INJECT_RAW_A(buf, reason) \ + TPARAM_CHECK_INJECT_RAW(buf, sizeof(buf), reason) +#define TPARAM_CHECK_MUTATE(name, reason) \ + { QUIC_TPARAM_##name, TPARAM_OP_MUTATE, (reason) }, +#define TPARAM_CHECK_INT(name, reason) \ + TPARAM_CHECK_DROP_INJECT(name, NULL, 0, reason) \ + TPARAM_CHECK_DROP_INJECT_A(name, bogus_int, reason) \ + TPARAM_CHECK_DROP_INJECT_A(name, int_with_trailer, reason) + +struct tparam_test { + uint64_t id; + int op; + const char *expect_fail; /* substring to expect in reason */ + const void *buf; + size_t buf_len; +}; + +static const unsigned char retry_scid_1[8] = { 0 }; + +static const unsigned char disable_active_migration_1[] = { + 0x00 +}; + +static const unsigned char malformed_stateless_reset_token_1[] = { + 0x02, 0xff +}; + +static const unsigned char malformed_stateless_reset_token_2[] = { + 0x01 +}; + +static const unsigned char malformed_stateless_reset_token_3[15] = { 0 }; + +static const unsigned char malformed_stateless_reset_token_4[17] = { 0 }; + +static const unsigned char malformed_preferred_addr_1[] = { + 0x0d, 0xff +}; + +static const unsigned char malformed_preferred_addr_2[42] = { + 0x0d, 0x28, /* too short */ +}; + +static const unsigned char malformed_preferred_addr_3[64] = { + 0x0d, 0x3e, /* too long */ +}; + +static const unsigned char malformed_preferred_addr_4[] = { + /* TPARAM too short for CID length indicated */ + 0x0d, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x55, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const unsigned char malformed_unknown_1[] = { + 0xff +}; + +static const unsigned char malformed_unknown_2[] = { + 0x55, 0x55, +}; + +static const unsigned char malformed_unknown_3[] = { + 0x55, 0x55, 0x01, +}; + +static const unsigned char ack_delay_exp[] = { + 0x03 +}; + +static const unsigned char stateless_reset_token[16] = { 0x42 }; + +static const unsigned char preferred_addr[] = { + 0x44, 0x44, 0x44, 0x44, + 0x55, 0x55, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x77, 0x77, + 0x02, 0xAA, 0xBB, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, +}; + +static const unsigned char long_cid[21] = { 0x42 }; + +static const unsigned char excess_ack_delay_exp[] = { + 0x15, +}; + +static const unsigned char excess_max_ack_delay[] = { + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, +}; + +static const unsigned char excess_initial_max_streams[] = { + 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +}; + +static const unsigned char undersize_udp_payload_size[] = { + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xaf, +}; + +static const unsigned char undersize_active_conn_id_limit[] = { + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +}; + +static const unsigned char bogus_int[9] = { 0 }; + +static const unsigned char int_with_trailer[2] = { 0x01 }; + +#define QUIC_TPARAM_UNKNOWN_1 0xf1f1 + +static const struct tparam_test tparam_tests[] = { + TPARAM_CHECK_DUP(ORIG_DCID, + "ORIG_DCID appears multiple times") + TPARAM_CHECK_DUP(INITIAL_SCID, + "INITIAL_SCID appears multiple times") + TPARAM_CHECK_DUP(INITIAL_MAX_DATA, + "INITIAL_MAX_DATA appears multiple times") + TPARAM_CHECK_DUP(INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + "INITIAL_MAX_STREAM_DATA_BIDI_LOCAL appears multiple times") + TPARAM_CHECK_DUP(INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + "INITIAL_MAX_STREAM_DATA_BIDI_REMOTE appears multiple times") + TPARAM_CHECK_DUP(INITIAL_MAX_STREAM_DATA_UNI, + "INITIAL_MAX_STREAM_DATA_UNI appears multiple times") + TPARAM_CHECK_DUP(INITIAL_MAX_STREAMS_BIDI, + "INITIAL_MAX_STREAMS_BIDI appears multiple times") + TPARAM_CHECK_DUP(INITIAL_MAX_STREAMS_UNI, + "INITIAL_MAX_STREAMS_UNI appears multiple times") + TPARAM_CHECK_DUP(MAX_IDLE_TIMEOUT, + "MAX_IDLE_TIMEOUT appears multiple times") + TPARAM_CHECK_DUP(MAX_UDP_PAYLOAD_SIZE, + "MAX_UDP_PAYLOAD_SIZE appears multiple times") + TPARAM_CHECK_DUP(ACTIVE_CONN_ID_LIMIT, + "ACTIVE_CONN_ID_LIMIT appears multiple times") + TPARAM_CHECK_DUP(DISABLE_ACTIVE_MIGRATION, + "DISABLE_ACTIVE_MIGRATION appears multiple times") + + TPARAM_CHECK_DROP(INITIAL_SCID, + "INITIAL_SCID was not sent but is required") + TPARAM_CHECK_DROP(ORIG_DCID, + "ORIG_DCID was not sent but is required") + + TPARAM_CHECK_INJECT_A(RETRY_SCID, retry_scid_1, + "RETRY_SCID sent when not performing a retry") + TPARAM_CHECK_DROP_INJECT_A(DISABLE_ACTIVE_MIGRATION, disable_active_migration_1, + "DISABLE_ACTIVE_MIGRATION is malformed") + TPARAM_CHECK_INJECT(UNKNOWN_1, NULL, 0, + NULL) + TPARAM_CHECK_INJECT_RAW_A(malformed_stateless_reset_token_1, + "STATELESS_RESET_TOKEN is malformed") + TPARAM_CHECK_INJECT_A(STATELESS_RESET_TOKEN, + malformed_stateless_reset_token_2, + "STATELESS_RESET_TOKEN is malformed") + TPARAM_CHECK_INJECT_A(STATELESS_RESET_TOKEN, + malformed_stateless_reset_token_3, + "STATELESS_RESET_TOKEN is malformed") + TPARAM_CHECK_INJECT_A(STATELESS_RESET_TOKEN, + malformed_stateless_reset_token_4, + "STATELESS_RESET_TOKEN is malformed") + TPARAM_CHECK_INJECT(STATELESS_RESET_TOKEN, + NULL, 0, + "STATELESS_RESET_TOKEN is malformed") + TPARAM_CHECK_INJECT_RAW_A(malformed_preferred_addr_1, + "PREFERRED_ADDR is malformed") + TPARAM_CHECK_INJECT_RAW_A(malformed_preferred_addr_2, + "PREFERRED_ADDR is malformed") + TPARAM_CHECK_INJECT_RAW_A(malformed_preferred_addr_3, + "PREFERRED_ADDR is malformed") + TPARAM_CHECK_INJECT_RAW_A(malformed_preferred_addr_4, + "PREFERRED_ADDR is malformed") + TPARAM_CHECK_INJECT_RAW_A(malformed_unknown_1, + "bad transport parameter") + TPARAM_CHECK_INJECT_RAW_A(malformed_unknown_2, + "bad transport parameter") + TPARAM_CHECK_INJECT_RAW_A(malformed_unknown_3, + "bad transport parameter") + + TPARAM_CHECK_INJECT_A(ACK_DELAY_EXP, excess_ack_delay_exp, + "ACK_DELAY_EXP is malformed") + TPARAM_CHECK_INJECT_A(MAX_ACK_DELAY, excess_max_ack_delay, + "MAX_ACK_DELAY is malformed") + TPARAM_CHECK_DROP_INJECT_A(INITIAL_MAX_STREAMS_BIDI, excess_initial_max_streams, + "INITIAL_MAX_STREAMS_BIDI is malformed") + TPARAM_CHECK_DROP_INJECT_A(INITIAL_MAX_STREAMS_UNI, excess_initial_max_streams, + "INITIAL_MAX_STREAMS_UNI is malformed") + + TPARAM_CHECK_DROP_INJECT_A(MAX_UDP_PAYLOAD_SIZE, undersize_udp_payload_size, + "MAX_UDP_PAYLOAD_SIZE is malformed") + TPARAM_CHECK_DROP_INJECT_A(ACTIVE_CONN_ID_LIMIT, undersize_active_conn_id_limit, + "ACTIVE_CONN_ID_LIMIT is malformed") + + TPARAM_CHECK_INJECT_TWICE_A(ACK_DELAY_EXP, ack_delay_exp, + "ACK_DELAY_EXP appears multiple times") + TPARAM_CHECK_INJECT_TWICE_A(MAX_ACK_DELAY, ack_delay_exp, + "MAX_ACK_DELAY appears multiple times") + TPARAM_CHECK_INJECT_TWICE_A(STATELESS_RESET_TOKEN, stateless_reset_token, + "STATELESS_RESET_TOKEN appears multiple times") + TPARAM_CHECK_INJECT_TWICE_A(PREFERRED_ADDR, preferred_addr, + "PREFERRED_ADDR appears multiple times") + + TPARAM_CHECK_MUTATE(ORIG_DCID, + "ORIG_DCID does not match expected value") + TPARAM_CHECK_MUTATE(INITIAL_SCID, + "INITIAL_SCID does not match expected value") + + TPARAM_CHECK_DROP_INJECT_A(ORIG_DCID, long_cid, + "ORIG_DCID is malformed") + TPARAM_CHECK_DROP_INJECT_A(INITIAL_SCID, long_cid, + "INITIAL_SCID is malformed") + + TPARAM_CHECK_INT(INITIAL_MAX_DATA, + "INITIAL_MAX_DATA is malformed") + TPARAM_CHECK_INT(INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + "INITIAL_MAX_STREAM_DATA_BIDI_LOCAL is malformed") + TPARAM_CHECK_INT(INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + "INITIAL_MAX_STREAM_DATA_BIDI_REMOTE is malformed") + TPARAM_CHECK_INT(INITIAL_MAX_STREAM_DATA_UNI, + "INITIAL_MAX_STREAM_DATA_UNI is malformed") + TPARAM_CHECK_INT(ACK_DELAY_EXP, + "ACK_DELAY_EXP is malformed") + TPARAM_CHECK_INT(MAX_ACK_DELAY, + "MAX_ACK_DELAY is malformed") + TPARAM_CHECK_INT(INITIAL_MAX_STREAMS_BIDI, + "INITIAL_MAX_STREAMS_BIDI is malformed") + TPARAM_CHECK_INT(INITIAL_MAX_STREAMS_UNI, + "INITIAL_MAX_STREAMS_UNI is malformed") + TPARAM_CHECK_INT(MAX_IDLE_TIMEOUT, + "MAX_IDLE_TIMEOUT is malformed") + TPARAM_CHECK_INT(MAX_UDP_PAYLOAD_SIZE, + "MAX_UDP_PAYLOAD_SIZE is malformed") + TPARAM_CHECK_INT(ACTIVE_CONN_ID_LIMIT, + "ACTIVE_CONN_ID_LIMIT is malformed") +}; + +struct tparam_ctx { + const struct tparam_test *t; +}; + +static int tparam_handle(struct tparam_ctx *ctx, + uint64_t id, unsigned char *data, + size_t data_len, + WPACKET *wpkt) +{ + const struct tparam_test *t = ctx->t; + + switch (t->op) { + case TPARAM_OP_DUP: + if (!TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(wpkt, id, + data, data_len))) + return 0; + + /* + * If this is the matching ID, write it again, duplicating the TPARAM. + */ + if (id == t->id + && !TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(wpkt, id, + data, data_len))) + return 0; + + return 1; + + case TPARAM_OP_DROP: + case TPARAM_OP_DROP_INJECT: + /* Pass through unless ID matches. */ + if (id != t->id + && !TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(wpkt, id, + data, data_len))) + return 0; + + return 1; + + case TPARAM_OP_INJECT: + case TPARAM_OP_INJECT_TWICE: + case TPARAM_OP_INJECT_RAW: + /* Always pass through. */ + if (!TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(wpkt, id, + data, data_len))) + return 0; + + return 1; + + case TPARAM_OP_MUTATE: + if (id == t->id) { + if (!TEST_size_t_gt(data_len, 0)) + return 0; + + data[0] ^= 1; + } + + if (!TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(wpkt, id, + data, data_len))) + return 0; + + if (id == t->id) + data[0] ^= 1; + + return 1; + + default: + return 0; + } +} + +static int tparam_on_enc_ext(QTEST_FAULT *qtf, QTEST_ENCRYPTED_EXTENSIONS *ee, + size_t ee_len, void *arg) +{ + int rc = 0; + struct tparam_ctx *ctx = arg; + PACKET pkt = {0}; + WPACKET wpkt; + int have_wpkt = 0; + BUF_MEM *old_bufm = NULL, *new_bufm = NULL; + unsigned char *tp_p; + size_t tp_len, written, old_len, eb_len; + uint64_t id; + + if (!TEST_ptr(old_bufm = BUF_MEM_new())) + goto err; + + /* + * Delete transport parameters TLS extension and capture the contents of the + * extension which was removed. + */ + if (!TEST_true(qtest_fault_delete_extension(qtf, TLSEXT_TYPE_quic_transport_parameters, + ee->extensions, &ee->extensionslen, + old_bufm))) + goto err; + + if (!TEST_true(PACKET_buf_init(&pkt, (unsigned char *)old_bufm->data, old_bufm->length)) + || !TEST_ptr(new_bufm = BUF_MEM_new()) + || !TEST_true(WPACKET_init(&wpkt, new_bufm))) + goto err; + + have_wpkt = 1; + + /* + * Open transport parameters TLS extension: + * + * u16 Extension ID (quic_transport_parameters) + * u16 Extension Data Length + * ... Extension Data + * + */ + if (!TEST_true(WPACKET_put_bytes_u16(&wpkt, + TLSEXT_TYPE_quic_transport_parameters)) + || !TEST_true(WPACKET_start_sub_packet_u16(&wpkt))) + goto err; + + for (; PACKET_remaining(&pkt) > 0; ) { + tp_p = (unsigned char *)ossl_quic_wire_decode_transport_param_bytes(&pkt, + &id, + &tp_len); + if (!TEST_ptr(tp_p)) { + TEST_mem_eq(PACKET_data(&pkt), PACKET_remaining(&pkt), NULL, 0); + goto err; + } + + if (!TEST_true(tparam_handle(ctx, id, tp_p, tp_len, &wpkt))) + goto err; + } + + if (ctx->t->op == TPARAM_OP_INJECT || ctx->t->op == TPARAM_OP_DROP_INJECT + || ctx->t->op == TPARAM_OP_INJECT_TWICE) { + if (!TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(&wpkt, ctx->t->id, + ctx->t->buf, + ctx->t->buf_len))) + goto err; + + if (ctx->t->op == TPARAM_OP_INJECT_TWICE + && !TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(&wpkt, ctx->t->id, + ctx->t->buf, + ctx->t->buf_len))) + goto err; + } else if (ctx->t->op == TPARAM_OP_INJECT_RAW) { + if (!TEST_true(WPACKET_memcpy(&wpkt, ctx->t->buf, ctx->t->buf_len))) + goto err; + } + + if (!TEST_true(WPACKET_close(&wpkt))) /* end extension data, set length */ + goto err; + + if (!TEST_true(WPACKET_get_total_written(&wpkt, &written))) + goto err; + + WPACKET_finish(&wpkt); + have_wpkt = 0; + + /* + * Append the constructed extension blob to the extension block. + */ + old_len = ee->extensionslen; + + if (!qtest_fault_resize_message(qtf, ee->extensionslen + written)) + goto err; + + memcpy(ee->extensions + old_len, new_bufm->data, written); + + /* Fixup the extension block header (u16 length of entire block). */ + eb_len = (((uint16_t)ee->extensions[0]) << 8) + (uint16_t)ee->extensions[1]; + eb_len += written; + ee->extensions[0] = (unsigned char)((eb_len >> 8) & 0xFF); + ee->extensions[1] = (unsigned char)( eb_len & 0xFF); + + rc = 1; +err: + if (have_wpkt) { + if (rc) + WPACKET_finish(&wpkt); + else + WPACKET_cleanup(&wpkt); + } + BUF_MEM_free(old_bufm); + BUF_MEM_free(new_bufm); + return rc; +} + +static int test_tparam(int idx) +{ + int testresult = 0; + SSL_CTX *c_ctx = NULL; + SSL *c_ssl = NULL; + QUIC_TSERVER *s = NULL; + QTEST_FAULT *qtf = NULL; + struct tparam_ctx ctx = {0}; + + ctx.t = &tparam_tests[idx]; + + if (!TEST_ptr(c_ctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method()))) + goto err; + + if (!TEST_true(qtest_create_quic_objects(libctx, c_ctx, NULL, cert, + privkey, 0, &s, + &c_ssl, &qtf, NULL))) + goto err; + + if (!TEST_true(qtest_fault_set_hand_enc_ext_listener(qtf, tparam_on_enc_ext, + &ctx))) + goto err; + + if (!TEST_true(qtest_create_quic_connection_ex(s, c_ssl, + ctx.t->expect_fail != NULL))) + goto err; + + if (ctx.t->expect_fail != NULL) { + SSL_CONN_CLOSE_INFO info = {0}; + + if (!TEST_true(SSL_get_conn_close_info(c_ssl, &info, sizeof(info)))) + goto err; + + if (!TEST_true((info.flags & SSL_CONN_CLOSE_FLAG_TRANSPORT) != 0) + || !TEST_uint64_t_eq(info.error_code, QUIC_ERR_TRANSPORT_PARAMETER_ERROR) + || !TEST_ptr(strstr(info.reason, ctx.t->expect_fail))) { + TEST_error("expected connection closure information mismatch" + " during TPARAM test: flags=%llu ec=%llu reason='%s'", + (unsigned long long)info.flags, + (unsigned long long)info.error_code, + info.reason); + goto err; + } + } + + testresult = 1; +err: + if (!testresult) { + if (ctx.t->expect_fail != NULL) + TEST_info("failed during test for id=%llu, op=%d, bl=%zu, " + "expected failure='%s'", (unsigned long long)ctx.t->id, + ctx.t->op, ctx.t->buf_len, ctx.t->expect_fail); + else + TEST_info("failed during test for id=%llu, op=%d, bl=%zu", + (unsigned long long)ctx.t->id, ctx.t->op, ctx.t->buf_len); + } + + ossl_quic_tserver_free(s); + SSL_free(c_ssl); + SSL_CTX_free(c_ctx); + qtest_fault_free(qtf); + return testresult; +} +/***********************************************************************************/ OPT_TEST_DECLARE_USAGE("provider config certsdir datadir\n") @@ -1642,6 +2158,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_alpn, 2); ADD_ALL_TESTS(test_noisy_dgram, 2); ADD_TEST(test_get_shutdown); + ADD_ALL_TESTS(test_tparam, OSSL_NELEM(tparam_tests)); return 1; err: -- Gitee From 5070da3658d4780da882927ef59c889fdc2381af Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 26 Oct 2023 12:47:58 +0100 Subject: [PATCH 06/54] TLS: Fix use of an uninitialized value Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22523) Signed-off-by: fly2x --- ssl/statem/extensions_cust.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c index 81f20b5f6b..8b296f1f59 100644 --- a/ssl/statem/extensions_cust.c +++ b/ssl/statem/extensions_cust.c @@ -115,7 +115,7 @@ int custom_ext_parse(SSL_CONNECTION *s, unsigned int context, const unsigned char *ext_data, size_t ext_size, X509 *x, size_t chainidx) { - int al; + int al = 0; custom_ext_methods *exts = &s->cert->custext; custom_ext_method *meth; ENDPOINT role = ENDPOINT_BOTH; -- Gitee From b74235a4e6ceacff74d110fc32246f81d6546198 Mon Sep 17 00:00:00 2001 From: slontis Date: Thu, 13 Jul 2023 14:32:02 +1000 Subject: [PATCH 07/54] Add design notes for XOF API. Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/21443) Signed-off-by: fly2x --- doc/designs/xof.md | 268 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 doc/designs/xof.md diff --git a/doc/designs/xof.md b/doc/designs/xof.md new file mode 100644 index 0000000000..d7fe7ade87 --- /dev/null +++ b/doc/designs/xof.md @@ -0,0 +1,268 @@ +XOF Design +========== + +XOF Definition +-------------- + +An extendable output function (XOF) is defined as a variable-length hash +function on a message in which the output can be extended to any desired length. + +At a minimum an XOF needs to support the following pseudo-code + +```text +xof = xof.new(); +xof.absorb(bytes1); +xof.absorb(bytes2); +xof.finalize(); +out1 = xof.squeeze(10); +out2 = xof.squeeze(1000); +``` + +### Rules + +- absorb can be called multiple times +- finalize ends the absorb process (by adding padding bytes and doing a final + absorb). absorb must not be called once the finalize is done unless a reset + happens. +- finalize may be done as part of the first squeeze operation +- squeeze can be called multiple times. + +OpenSSL XOF Requirements +------------------------ + +The current OpenSSL implementation of XOF only supports a single call to squeeze. +The assumption exists in both the high level call to EVP_DigestFinalXOF() as +well as in the lower level SHA3_squeeze() operation (Of which there is a generic +c version, as well as assembler code for different platforms). + +A decision has to be made as to whether a new API is required, as well as +considering how the change may affect existing applications. +The changes introduced should have a minimal affect on other related functions +that share the same code (e.g SHAKE and SHA3 share functionality). +Older providers that have not been updated to support this change should produce +an error if a newer core is used that supports multiple squeeze operations. + +API Discussion of Squeeze +------------------------- + +### Squeeze + +Currently EVP_DigestFinalXOF() uses a flag to check that it is only invoked once. +It returns an error if called more than once. When initially written it also did +a reset, but that code was removed as it was deemed to be incorrect. + +If we remove the flag check, then the core code will potentially call low level +squeeze code in a older provider that does not handle returning correct data for +multiple calls. To counter this the provider needs a mechanism to indicate that +multiple calls are allowed. This could just be a new gettable flag (having a +separate provider function should not be necessary). + +#### Proposal 1 + +Change EVP_DigestFinalXOF(ctx, out, outlen) to handle multiple calls. +Possibly have EVP_DigestSqueeze() just as an alias method? +Changing the code at this level should be a simple matter of removing the +flag check. + +##### Pros + + - New API is not required + +##### Cons + + - Final seems like a strange name to call multiple times. + +#### Proposal 2 (Proposed Solution) + +Keep EVP_DigestFinalXOF() as a one shot function and create a new API to handle +the multi squeeze case e.g. + +```text +EVP_DigestSqueeze(ctx, out, outlen). +``` + +##### Pros + + - Seems like a better name. + - The existing function does not change, so it is not affected by logic that + needs to run for the multi squeeze case. + - The behaviour of the existing API is the same. + - At least one other toolkit uses this approach. + +##### Cons + + - Adds an extra API. + - The interaction between the 2 API's needs to be clearly documented. + - A call to EVP_DigestSqueeze() after EVP_DigestFinalXOF() would fail since + EVP_DigestFinalXOF() indicates no more output can be retrieved. + - A call to EVP_DigestFinalXOF() after the EVP_DigestSqueeze() would fail. + +#### Proposal 3 + +Create a completely new type e.g. EVP_XOF_MD to implement XOF digests + +##### Pros + + - This would separate the XOF operations so that the interface consisted + mainly of Init, Absorb and Squeeze API's + - DigestXOF could then be deprecated. + +##### Cons + + - XOF operations are required for Post Quantum signatures which currently use + an EVP_MD object. This would then complicate the Signature API also. + - Duplication of the EVP_MD code (although all legacy/engine code would be + removed). + +Choosing a name for the API that allows multiple output calls +------------------------------------------------------------- + +Currently OpenSSL only uses XOF's which use a sponge construction (which uses +the terms absorb and squeeze). +There will be other XOF's that do not use the sponge construction such as Blake2. + +The proposed API name to use is EVP_DigestSqueeze. +The alternate name suggested was EVP_DigestExtract. +The terms extract and expand are used by HKDF so I think this name would be +confusing. + +API Discussion of other XOF API'S +--------------------------------- + +### Init + +The digest can be initialized as normal using: + +```text +md = EVP_MD_fetch(libctx, "SHAKE256", propq); +ctx = EVP_MD_CTX_new(); +EVP_DigestInit_ex2(ctx, md, NULL); +``` + +### Absorb + +Absorb can be done by multiple calls to: + +```text +EVP_DigestUpdate(ctx, in, inlen); +``` + +#### Proposal: + +Do we want to have an Alias function? + +```text +EVP_DigestAbsorb(ctx, in, inlen); +``` + +(The consensus was that this is not required). + +### Finalize + +The finalize is just done as part of the squeeze operation. + +### Reset + +A reset can be done by calling: + +```text +EVP_DigestInit_ex2(ctx, NULL, NULL); +``` + +### State Copy + +The internal state can be copied by calling: + +```text +EVP_MD_CTX_copy_ex(ctx, newctx); +``` + +Low Level squeeze changes +-------------------------- + +### Description + +The existing one shot squeeze method is: + +```text +SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t outlen, size_t r) +``` + +It contains an opaque object for storing the state B, that can be used to +output to B. After every B bits, the state B is updated internally +by calling KeccakF1600(). + +Unless you are using a multiple of B as the B, the function has no +way of knowing where to start from if another call to SHA_squeeze() was +attempted. The method also avoids doing a final call to KeccakF1600() currently +since it was assumed that it was not required for a one shot operation. + +### Solution 1 + +Modify the SHA3_squeeze code to accept a input/output parameter to track the +position within the state B. +See + +#### Pros + + - Change in C code is minimal. it just needs to pass this additional parameter. + - There are no additional memory copies of buffered results. + +#### Cons + + - The logic in the c reference has many if clauses. + - This C code also needs to be written in assembler, the logic would also be + different in different assembler routines due to the internal format of the + state A being different. + - The general SHA3 case would be slower unless code was duplicated. + +### Solution 2 + +Leave SHA3_squeeze() as it is and buffer calls to the SHA3_squeeze() function +inside the final. See + +#### Pros + + - Change is mainly in C code. + +#### Cons + + - Because of the one shot nature of the SHA3_squeeze() it still needs to call + the KeccakF1600() function directly. + - The Assembler function for KeccakF1600() needs to be exposed. This function + was not intended to be exposed since the internal format of the state B + can be different on different platform architectures. + - When should this internal buffer state be cleared? + +### Solution 3 + +Perform a one-shot squeeze on the original absorbed data and throw away the +first part of the output buffer, + +#### Pros + + - Very simple. + +#### Cons + + - Incredibly slow. + - More of a hack than a real solution. + +### Solution 4 (Proposed Solution) + +An alternative approach to solution 2 is to modify the SHA3_squeeze() slightly +so that it can pass in a boolean that handles the call to KeccakF1600() +correctly for multiple calls. + +#### Pros + + - C code is fairly simple to implement. + - The state data remains as an opaque blob. + - For larger values of outlen SHA3_squeeze() may use the out buffer directly. + +#### Cons + + - Requires small assembler change to pass the boolean and handle the call to + KeccakF1600(). + - Uses memcpy to store partial results for a single blob of squeezed data of + size 'r' bytes. -- Gitee From 2d03e4b913005e8bdad6c96ce913aeb2d9bdcb23 Mon Sep 17 00:00:00 2001 From: Stephen Farrell Date: Mon, 16 Oct 2023 21:04:06 +0100 Subject: [PATCH 08/54] Add additional internal HPKE hardening checks resulting from code audit. Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22493) Signed-off-by: fly2x --- crypto/hpke/hpke.c | 100 +++++++++++++++++++++------------ crypto/hpke/hpke_util.c | 32 ++++++----- doc/man3/OSSL_HPKE_CTX_new.pod | 4 ++ include/openssl/evp.h | 1 + include/openssl/hpke.h | 1 + test/hpke_test.c | 13 ++++- 6 files changed, 97 insertions(+), 54 deletions(-) diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c index e2cbd17915..5e976d6150 100644 --- a/crypto/hpke/hpke.c +++ b/crypto/hpke/hpke.c @@ -19,8 +19,9 @@ #include #include "internal/hpke_util.h" #include "internal/nelem.h" +#include "internal/common.h" -/** default buffer size for keys and internal buffers we use */ +/* default buffer size for keys and internal buffers we use */ #define OSSL_HPKE_MAXSIZE 512 /* Define HPKE labels from RFC9180 in hex for EBCDIC compatibility */ @@ -38,8 +39,6 @@ static const char OSSL_HPKE_EXP_LABEL[] = "\x65\x78\x70"; static const char OSSL_HPKE_EXP_SEC_LABEL[] = "\x73\x65\x63"; /* "key" - label for use when generating key from shared secret */ static const char OSSL_HPKE_KEY_LABEL[] = "\x6b\x65\x79"; -/* "psk_hash" - for hashing PSK */ -static const char OSSL_HPKE_PSK_HASH_LABEL[] = "\x70\x73\x6b\x5f\x68\x61\x73\x68"; /* "secret" - for generating shared secret */ static const char OSSL_HPKE_SECRET_LABEL[] = "\x73\x65\x63\x72\x65\x74"; @@ -158,7 +157,6 @@ static int hpke_aead_dec(OSSL_HPKE_CTX *hctx, const unsigned char *iv, /* Create and initialise the context */ if ((ctx = EVP_CIPHER_CTX_new()) == NULL) return 0; - /* Initialise the decryption operation. */ if (EVP_DecryptInit_ex(ctx, hctx->aead_ciph, NULL, NULL, NULL) != 1) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); @@ -226,17 +224,20 @@ static int hpke_aead_enc(OSSL_HPKE_CTX *hctx, const unsigned char *iv, EVP_CIPHER_CTX *ctx = NULL; int len; size_t taglen = 0; - unsigned char tag[16]; + unsigned char tag[EVP_MAX_AEAD_TAG_LENGTH]; taglen = hctx->aead_info->taglen; if (*ctlen <= taglen || ptlen > *ctlen - taglen) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } + if (!ossl_assert(taglen <= sizeof(tag))) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } /* Create and initialise the context */ if ((ctx = EVP_CIPHER_CTX_new()) == NULL) return 0; - /* Initialise the encryption operation. */ if (EVP_EncryptInit_ex(ctx, hctx->aead_ciph, NULL, NULL, NULL) != 1) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); @@ -396,7 +397,7 @@ static int hpke_expansion(OSSL_HPKE_SUITE suite, const OSSL_HPKE_KEM_INFO *kem_info = NULL; if (cipherlen == NULL || enclen == NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (hpke_suite_check(suite, &kem_info, NULL, &aead_info) != 1) { @@ -448,14 +449,14 @@ static int hpke_encap(OSSL_HPKE_CTX *ctx, unsigned char *enc, size_t *enclen, { int erv = 0; OSSL_PARAM params[3], *p = params; - size_t lsslen = 0; + size_t lsslen = 0, lenclen = 0; EVP_PKEY_CTX *pctx = NULL; EVP_PKEY *pkR = NULL; const OSSL_HPKE_KEM_INFO *kem_info = NULL; if (ctx == NULL || enc == NULL || enclen == NULL || *enclen == 0 || pub == NULL || publen == 0) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (ctx->shared_secret != NULL) { @@ -507,10 +508,15 @@ static int hpke_encap(OSSL_HPKE_CTX *ctx, unsigned char *enc, size_t *enclen, goto err; } } - if (EVP_PKEY_encapsulate(pctx, NULL, enclen, NULL, &lsslen) != 1) { + lenclen = *enclen; + if (EVP_PKEY_encapsulate(pctx, NULL, &lenclen, NULL, &lsslen) != 1) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); goto err; } + if (lenclen > *enclen) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + goto err; + } ctx->shared_secret = OPENSSL_malloc(lsslen); if (ctx->shared_secret == NULL) goto err; @@ -550,7 +556,7 @@ static int hpke_decap(OSSL_HPKE_CTX *ctx, size_t lsslen = 0; if (ctx == NULL || enc == NULL || enclen == 0 || priv == NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (ctx->shared_secret != NULL) { @@ -647,8 +653,6 @@ static int hpke_do_middle(OSSL_HPKE_CTX *ctx, unsigned char ks_context[OSSL_HPKE_MAXSIZE]; size_t halflen = 0; size_t pskidlen = 0; - size_t psk_hashlen = OSSL_HPKE_MAXSIZE; - unsigned char psk_hash[OSSL_HPKE_MAXSIZE]; const OSSL_HPKE_AEAD_INFO *aead_info = NULL; const OSSL_HPKE_KDF_INFO *kdf_info = NULL; size_t secretlen = OSSL_HPKE_MAXSIZE; @@ -659,7 +663,7 @@ static int hpke_do_middle(OSSL_HPKE_CTX *ctx, /* only let this be done once */ if (ctx->exportersec != NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return 0; } if (ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id) == NULL) { @@ -690,7 +694,7 @@ static int hpke_do_middle(OSSL_HPKE_CTX *ctx, if (ctx->mode == OSSL_HPKE_MODE_PSK || ctx->mode == OSSL_HPKE_MODE_PSKAUTH) { if (ctx->psk == NULL || ctx->psklen == 0 || ctx->pskid == NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } } @@ -707,6 +711,7 @@ static int hpke_do_middle(OSSL_HPKE_CTX *ctx, suitebuf[3] = ctx->suite.kdf_id % 256; suitebuf[4] = ctx->suite.aead_id / 256; suitebuf[5] = ctx->suite.aead_id % 256; + /* Extract and Expand variously... */ if (ossl_hpke_labeled_extract(kctx, ks_context + 1, halflen, NULL, 0, OSSL_HPKE_SEC51LABEL, suitebuf, sizeof(suitebuf), @@ -724,16 +729,6 @@ static int hpke_do_middle(OSSL_HPKE_CTX *ctx, goto err; } ks_contextlen = 1 + 2 * halflen; - /* Extract and Expand variously... */ - psk_hashlen = halflen; - if (ossl_hpke_labeled_extract(kctx, psk_hash, psk_hashlen, - NULL, 0, OSSL_HPKE_SEC51LABEL, - suitebuf, sizeof(suitebuf), - OSSL_HPKE_PSK_HASH_LABEL, - ctx->psk, ctx->psklen) != 1) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); - goto err; - } secretlen = kdf_info->Nh; if (secretlen > OSSL_HPKE_MAXSIZE) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR); @@ -791,7 +786,6 @@ static int hpke_do_middle(OSSL_HPKE_CTX *ctx, err: OPENSSL_cleanse(ks_context, OSSL_HPKE_MAXSIZE); - OPENSSL_cleanse(psk_hash, OSSL_HPKE_MAXSIZE); OPENSSL_cleanse(secret, OSSL_HPKE_MAXSIZE); EVP_KDF_CTX_free(kctx); return erv; @@ -877,17 +871,25 @@ int OSSL_HPKE_CTX_set1_psk(OSSL_HPKE_CTX *ctx, const unsigned char *psk, size_t psklen) { if (ctx == NULL || pskid == NULL || psk == NULL || psklen == 0) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (psklen > OSSL_HPKE_MAX_PARMLEN) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } + if (psklen < OSSL_HPKE_MIN_PSKLEN) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } if (strlen(pskid) > OSSL_HPKE_MAX_PARMLEN) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } + if (strlen(pskid) == 0) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } if (ctx->mode != OSSL_HPKE_MODE_PSK && ctx->mode != OSSL_HPKE_MODE_PSKAUTH) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); @@ -1057,10 +1059,11 @@ int OSSL_HPKE_encap(OSSL_HPKE_CTX *ctx, const unsigned char *info, size_t infolen) { int erv = 1; + size_t minenc = 0; if (ctx == NULL || enc == NULL || enclen == NULL || *enclen == 0 || pub == NULL || publen == 0) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (ctx->role != OSSL_HPKE_ROLE_SENDER) { @@ -1071,6 +1074,15 @@ int OSSL_HPKE_encap(OSSL_HPKE_CTX *ctx, ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } + if (infolen > 0 && info == NULL) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + minenc = OSSL_HPKE_get_public_encap_size(ctx->suite); + if (minenc == 0 || minenc > *enclen) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } if (ctx->shared_secret != NULL) { /* only allow one encap per OSSL_HPKE_CTX */ ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); @@ -1095,9 +1107,10 @@ int OSSL_HPKE_decap(OSSL_HPKE_CTX *ctx, const unsigned char *info, size_t infolen) { int erv = 1; + size_t minenc = 0; if (ctx == NULL || enc == NULL || enclen == 0 || recippriv == NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) { @@ -1108,6 +1121,15 @@ int OSSL_HPKE_decap(OSSL_HPKE_CTX *ctx, ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } + if (infolen > 0 && info == NULL) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + minenc = OSSL_HPKE_get_public_encap_size(ctx->suite); + if (minenc == 0 || minenc > enclen) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } if (ctx->shared_secret != NULL) { /* only allow one encap per OSSL_HPKE_CTX */ ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); @@ -1137,7 +1159,7 @@ int OSSL_HPKE_seal(OSSL_HPKE_CTX *ctx, if (ctx == NULL || ct == NULL || ctlen == NULL || *ctlen == 0 || pt == NULL || ptlen == 0) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (ctx->role != OSSL_HPKE_ROLE_SENDER) { @@ -1150,7 +1172,7 @@ int OSSL_HPKE_seal(OSSL_HPKE_CTX *ctx, } if (ctx->key == NULL || ctx->nonce == NULL) { /* need to have done an encap first, info can be NULL */ - ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } seqlen = hpke_seqnonce2buf(ctx, seqbuf, sizeof(seqbuf)); @@ -1179,7 +1201,7 @@ int OSSL_HPKE_open(OSSL_HPKE_CTX *ctx, if (ctx == NULL || pt == NULL || ptlen == NULL || *ptlen == 0 || ct == NULL || ctlen == 0) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) { @@ -1192,7 +1214,7 @@ int OSSL_HPKE_open(OSSL_HPKE_CTX *ctx, } if (ctx->key == NULL || ctx->nonce == NULL) { /* need to have done an encap first, info can be NULL */ - ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } seqlen = hpke_seqnonce2buf(ctx, seqbuf, sizeof(seqbuf)); @@ -1220,14 +1242,18 @@ int OSSL_HPKE_export(OSSL_HPKE_CTX *ctx, const char *mdname = NULL; const OSSL_HPKE_KDF_INFO *kdf_info = NULL; - if (ctx == NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + if (ctx == NULL || secret == NULL || secretlen == 0) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (labellen > OSSL_HPKE_MAX_PARMLEN) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } + if (labellen > 0 && label == NULL) { + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } if (ctx->exportersec == NULL) { ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return 0; @@ -1274,7 +1300,7 @@ int OSSL_HPKE_keygen(OSSL_HPKE_SUITE suite, OSSL_PARAM params[3], *p = params; if (pub == NULL || publen == NULL || *publen == 0 || priv == NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (hpke_suite_check(suite, &kem_info, NULL, NULL) != 1) { @@ -1348,7 +1374,7 @@ int OSSL_HPKE_get_grease_value(const OSSL_HPKE_SUITE *suite_in, if (enc == NULL || enclen == 0 || ct == NULL || ctlen == 0 || suite == NULL) { - ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER); + ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT); return 0; } if (suite_in == NULL) { diff --git a/crypto/hpke/hpke_util.c b/crypto/hpke/hpke_util.c index 0d1cc602f7..a9d86a9355 100644 --- a/crypto/hpke/hpke_util.c +++ b/crypto/hpke/hpke_util.c @@ -17,9 +17,11 @@ #include #include #include "crypto/ecx.h" +#include "crypto/rand.h" #include "internal/hpke_util.h" #include "internal/packet.h" #include "internal/nelem.h" +#include "internal/common.h" /* * Delimiter used in OSSL_HPKE_str2suite @@ -189,12 +191,12 @@ const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_id(uint16_t kemid) const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_random(OSSL_LIB_CTX *ctx) { - unsigned char rval = 0; - int sz = OSSL_NELEM(hpke_kem_tab); + uint32_t rval = 0; + int err = 0; + size_t sz = OSSL_NELEM(hpke_kem_tab); - if (RAND_bytes_ex(ctx, &rval, sizeof(rval), 0) <= 0) - return NULL; - return &hpke_kem_tab[rval % sz]; + rval = ossl_rand_uniform_uint32(ctx, sz, &err); + return (err == 1 ? NULL : &hpke_kem_tab[rval]); } const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_id(uint16_t kdfid) @@ -211,12 +213,12 @@ const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_id(uint16_t kdfid) const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_random(OSSL_LIB_CTX *ctx) { - unsigned char rval = 0; - int sz = OSSL_NELEM(hpke_kdf_tab); + uint32_t rval = 0; + int err = 0; + size_t sz = OSSL_NELEM(hpke_kdf_tab); - if (RAND_bytes_ex(ctx, &rval, sizeof(rval), 0) <= 0) - return NULL; - return &hpke_kdf_tab[rval % sz]; + rval = ossl_rand_uniform_uint32(ctx, sz, &err); + return (err == 1 ? NULL : &hpke_kdf_tab[rval]); } const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_id(uint16_t aeadid) @@ -233,13 +235,13 @@ const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_id(uint16_t aeadid) const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_random(OSSL_LIB_CTX *ctx) { - unsigned char rval = 0; + uint32_t rval = 0; + int err = 0; /* the minus 1 below is so we don't pick the EXPORTONLY codepoint */ - int sz = OSSL_NELEM(hpke_aead_tab) - 1; + size_t sz = OSSL_NELEM(hpke_aead_tab) - 1; - if (RAND_bytes_ex(ctx, &rval, sizeof(rval), 0) <= 0) - return NULL; - return &hpke_aead_tab[rval % sz]; + rval = ossl_rand_uniform_uint32(ctx, sz, &err); + return (err == 1 ? NULL : &hpke_aead_tab[rval]); } static int kdf_derive(EVP_KDF_CTX *kctx, diff --git a/doc/man3/OSSL_HPKE_CTX_new.pod b/doc/man3/OSSL_HPKE_CTX_new.pod index df951d7120..4ec647983e 100644 --- a/doc/man3/OSSL_HPKE_CTX_new.pod +++ b/doc/man3/OSSL_HPKE_CTX_new.pod @@ -222,6 +222,10 @@ functions below. The constant I is defined as the limit of this value. (We chose 66 octets so that we can validate all the test vectors present in RFC9180, Appendix A.) +In accordance with RFC9180, section 9.5, we define a constant +I with a value of 32 for the minimum length of a +pre-shared key, passed in I. + While RFC9180 also RECOMMENDS a 64 octet limit for the I parameter, that is not sufficient for TLS Encrypted ClientHello (ECH) processing, so we enforce a limit of I with a value of 1024 as the limit diff --git a/include/openssl/evp.h b/include/openssl/evp.h index dbe6c72969..ea7620d631 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -35,6 +35,7 @@ # define EVP_MAX_KEY_LENGTH 64 # define EVP_MAX_IV_LENGTH 16 # define EVP_MAX_BLOCK_LENGTH 32 +# define EVP_MAX_AEAD_TAG_LENGTH 16 # define PKCS5_SALT_LEN 8 /* Default PKCS#5 iteration count */ diff --git a/include/openssl/hpke.h b/include/openssl/hpke.h index 1bb9ada3c4..af637ac61a 100644 --- a/include/openssl/hpke.h +++ b/include/openssl/hpke.h @@ -26,6 +26,7 @@ * Appendix A.6.1 with a 66 octet IKM so we'll allow that. */ # define OSSL_HPKE_MAX_PARMLEN 66 +# define OSSL_HPKE_MIN_PSKLEN 32 # define OSSL_HPKE_MAX_INFOLEN 1024 /* diff --git a/test/hpke_test.c b/test/hpke_test.c index 4ca67682a3..a8bd1f8f64 100644 --- a/test/hpke_test.c +++ b/test/hpke_test.c @@ -1319,8 +1319,8 @@ static int test_hpke_oddcalls(void) OSSL_HPKE_CTX *rctx = NULL; unsigned char plain[] = "quick brown fox"; size_t plainlen = sizeof(plain); - unsigned char enc[OSSL_HPKE_TSTSIZE]; - size_t enclen = sizeof(enc); + unsigned char enc[OSSL_HPKE_TSTSIZE], smallenc[10]; + size_t enclen = sizeof(enc), smallenclen = sizeof(smallenc); unsigned char cipher[OSSL_HPKE_TSTSIZE]; size_t cipherlen = sizeof(cipher); unsigned char clear[OSSL_HPKE_TSTSIZE]; @@ -1471,6 +1471,15 @@ static int test_hpke_oddcalls(void) /* encap with too big info */ if (!TEST_false(OSSL_HPKE_encap(ctx, enc, &enclen, pub, 1, info, -1))) goto end; + /* encap with NULL info & non-zero infolen */ + if (!TEST_false(OSSL_HPKE_encap(ctx, enc, &enclen, pub, 1, NULL, 1))) + goto end; + /* encap with non-NULL info & zero infolen */ + if (!TEST_false(OSSL_HPKE_encap(ctx, enc, &enclen, pub, 1, info, 0))) + goto end; + /* encap with too small enc */ + if (!TEST_false(OSSL_HPKE_encap(ctx, smallenc, &smallenclen, pub, 1, NULL, 0))) + goto end; /* good encap */ if (!TEST_true(OSSL_HPKE_encap(ctx, enc, &enclen, pub, publen, NULL, 0))) goto end; -- Gitee From 4dea7173ce6ecb403e7a8cfd47950e9f41737064 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 1 Nov 2023 15:25:24 +0000 Subject: [PATCH 09/54] Call SSL_write() in the quic-client-fuzzer Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22592) Signed-off-by: fly2x --- fuzz/quic-client.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/fuzz/quic-client.c b/fuzz/quic-client.c index 2dc2b3c9b2..17cfef113b 100644 --- a/fuzz/quic-client.c +++ b/fuzz/quic-client.c @@ -43,6 +43,10 @@ int FuzzerInitialize(int *argc, char ***argv) return 1; } +#define HANDSHAKING 0 +#define READING 1 +#define WRITING 2 + int FuzzerTestOneInput(const uint8_t *buf, size_t len) { SSL *client = NULL; @@ -52,6 +56,7 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) BIO_ADDR *peer_addr = NULL; struct in_addr ina = {0}; struct timeval tv; + int state = HANDSHAKING; if (len == 0) return 0; @@ -113,14 +118,29 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } for (;;) { - if ((ret = SSL_do_handshake(client)) == 1) { - /* - * Keep reading application data until there are no more - * datagrams to inject or a fatal error occurs - */ - uint8_t tmp[1024]; + uint8_t tmp[1024]; + int writelen = 0; + + switch (state) { + case HANDSHAKING: + ret = SSL_do_handshake(client); + if (ret == 1) + state = READING; + break; + case READING: ret = SSL_read(client, tmp, sizeof(tmp)); + if (ret > 0) { + state = WRITING; + writelen = ret; + assert(writelen <= sizeof(tmp)); + } + break; + case WRITING: + ret = SSL_write(client, tmp, writelen); + if (ret > 0) + state = READING; + break; } if (ret <= 0) { switch (SSL_get_error(client, ret)) { -- Gitee From a202821f737091b273ef3925a5fa8e60ce33dd90 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 1 Nov 2023 16:15:24 +0000 Subject: [PATCH 10/54] Add support for streams to the quic-client fuzzer Enable the quic-client fuzzer to accept and create new streams Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22592) Signed-off-by: fly2x --- fuzz/quic-client.c | 101 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 14 deletions(-) diff --git a/fuzz/quic-client.c b/fuzz/quic-client.c index 17cfef113b..9550a6e86b 100644 --- a/fuzz/quic-client.c +++ b/fuzz/quic-client.c @@ -43,13 +43,18 @@ int FuzzerInitialize(int *argc, char ***argv) return 1; } -#define HANDSHAKING 0 -#define READING 1 -#define WRITING 2 +#define HANDSHAKING 0 +#define READING 1 +#define WRITING 2 +#define ACCEPTING_STREAM 3 +#define CREATING_STREAM 4 +#define SWAPPING_STREAM 5 int FuzzerTestOneInput(const uint8_t *buf, size_t len) { - SSL *client = NULL; + SSL *client = NULL, *stream = NULL; + SSL *allstreams[] = {NULL, NULL, NULL, NULL}; + size_t i, thisstream = 0, numstreams = 1; BIO *in; BIO *out; SSL_CTX *ctx; @@ -57,6 +62,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) struct in_addr ina = {0}; struct timeval tv; int state = HANDSHAKING; + uint8_t tmp[1024]; + int writelen = 0; if (len == 0) return 0; @@ -104,13 +111,40 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) goto end; SSL_set_connect_state(client); + if (!SSL_set_incoming_stream_policy(client, + SSL_INCOMING_STREAM_POLICY_ACCEPT, + 0)) + goto end; + + allstreams[0] = stream = client; for (;;) { size_t size; uint64_t nxtpktms = 0; OSSL_TIME nxtpkt = ossl_time_zero(), nxttimeout; - int isinf, ret; + int isinf, ret = 0; if (len >= 2) { + if (len >= 5 && buf[0] == 0xff && buf[1] == 0xff) { + switch (buf[2]) { + case 0x00: + if (state == READING) + state = ACCEPTING_STREAM; + break; + case 0x01: + if (state == READING) + state = CREATING_STREAM; + break; + case 0x02: + if (state == READING) + state = SWAPPING_STREAM; + break; + default: + /*ignore*/ + break; + } + len -= 3; + buf += 3; + } nxtpktms = buf[0] + (buf[1] << 8); nxtpkt = ossl_time_add(fake_now, ossl_ms2time(nxtpktms)); len -= 2; @@ -118,32 +152,70 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) } for (;;) { - uint8_t tmp[1024]; - int writelen = 0; - switch (state) { case HANDSHAKING: - ret = SSL_do_handshake(client); + ret = SSL_do_handshake(stream); if (ret == 1) state = READING; break; case READING: - ret = SSL_read(client, tmp, sizeof(tmp)); + ret = SSL_read(stream, tmp, sizeof(tmp)); if (ret > 0) { state = WRITING; writelen = ret; - assert(writelen <= sizeof(tmp)); + assert(writelen <= (int)sizeof(tmp)); } break; + case WRITING: - ret = SSL_write(client, tmp, writelen); + ret = SSL_write(stream, tmp, writelen); if (ret > 0) state = READING; break; + + case ACCEPTING_STREAM: + state = READING; + ret = 1; + if (numstreams == OSSL_NELEM(allstreams) + || SSL_get_accept_stream_queue_len(client) == 0) + break; + thisstream = numstreams; + stream = allstreams[numstreams++] + = SSL_accept_stream(client, 0); + if (stream == NULL) + goto end; + break; + + case CREATING_STREAM: + state = READING; + ret = 1; + if (numstreams == OSSL_NELEM(allstreams)) + break; + stream = SSL_new_stream(client, 0); + if (stream == NULL) { + /* Ignore, and go back to the previous stream */ + stream = allstreams[thisstream]; + break; + } + thisstream = numstreams; + allstreams[numstreams++] = stream; + break; + + case SWAPPING_STREAM: + state = READING; + ret = 1; + if (numstreams == 1) + break; + if (++thisstream == numstreams) + thisstream = 0; + stream = allstreams[thisstream]; + break; } + assert(stream != NULL); + assert(thisstream < numstreams); if (ret <= 0) { - switch (SSL_get_error(client, ret)) { + switch (SSL_get_error(stream, ret)) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: break; @@ -182,7 +254,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len) buf += size + 2; } end: - SSL_free(client); + for (i = 0; i < numstreams; i++) + SSL_free(allstreams[i]); ERR_clear_error(); SSL_CTX_free(ctx); BIO_ADDR_free(peer_addr); -- Gitee From de6acee0ecbd212b5a636546f890b4bb2bf06d43 Mon Sep 17 00:00:00 2001 From: Pauli Date: Thu, 2 Nov 2023 12:26:50 +1100 Subject: [PATCH 11/54] rand uniform: fix likely usage @mspncp noted that the condition should have been likely not unlikely. Reviewed-by: Matthias St. Pierre Reviewed-by: Tom Cosgrove Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22593) Signed-off-by: fly2x --- crypto/rand/rand_uniform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/rand/rand_uniform.c b/crypto/rand/rand_uniform.c index 26ce3ee560..b9e2635859 100644 --- a/crypto/rand/rand_uniform.c +++ b/crypto/rand/rand_uniform.c @@ -85,7 +85,7 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err) if (f < f2) return i + 1; /* For not all 1 bits, there is no carry so return the result */ - if (unlikely(f != 0xffffffff)) + if (likely(f != 0xffffffff)) return i; /* setup for the next word of randomness */ f = prod & 0xffffffff; -- Gitee From 299ccef86e21922c9abf6a242dbde00b294aa31d Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 1 Nov 2023 14:00:22 +0100 Subject: [PATCH 12/54] When changing IV length invalidate previously set IV Reviewed-by: Paul Dale Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22590) Signed-off-by: fly2x --- providers/implementations/ciphers/cipher_aes_ocb.c | 5 ++++- providers/implementations/ciphers/ciphercommon_ccm.c | 5 ++++- providers/implementations/ciphers/ciphercommon_gcm.c | 7 ++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/providers/implementations/ciphers/cipher_aes_ocb.c b/providers/implementations/ciphers/cipher_aes_ocb.c index 3f3cc6efbb..aec988e44e 100644 --- a/providers/implementations/ciphers/cipher_aes_ocb.c +++ b/providers/implementations/ciphers/cipher_aes_ocb.c @@ -385,7 +385,10 @@ static int aes_ocb_set_ctx_params(void *vctx, const OSSL_PARAM params[]) /* IV len must be 1 to 15 */ if (sz < OCB_MIN_IV_LEN || sz > OCB_MAX_IV_LEN) return 0; - ctx->base.ivlen = sz; + if (ctx->base.ivlen != sz) { + ctx->base.ivlen = sz; + ctx->iv_state = IV_STATE_UNINITIALISED; + } } p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); if (p != NULL) { diff --git a/providers/implementations/ciphers/ciphercommon_ccm.c b/providers/implementations/ciphers/ciphercommon_ccm.c index ce3f7527f3..33105911e3 100644 --- a/providers/implementations/ciphers/ciphercommon_ccm.c +++ b/providers/implementations/ciphers/ciphercommon_ccm.c @@ -109,7 +109,10 @@ int ossl_ccm_set_ctx_params(void *vctx, const OSSL_PARAM params[]) ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); return 0; } - ctx->l = ivlen; + if (ctx->l != ivlen) { + ctx->l = ivlen; + ctx->iv_set = 0; + } } p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD); diff --git a/providers/implementations/ciphers/ciphercommon_gcm.c b/providers/implementations/ciphers/ciphercommon_gcm.c index cd7852a547..fe24b450a5 100644 --- a/providers/implementations/ciphers/ciphercommon_gcm.c +++ b/providers/implementations/ciphers/ciphercommon_gcm.c @@ -280,7 +280,12 @@ int ossl_gcm_set_ctx_params(void *vctx, const OSSL_PARAM params[]) ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); return 0; } - ctx->ivlen = sz; + if (ctx->ivlen != sz) { + /* If the iv was already set or autogenerated, it is invalid. */ + if (ctx->iv_state != IV_STATE_UNINITIALISED) + ctx->iv_state = IV_STATE_FINISHED; + ctx->ivlen = sz; + } break; case PIDX_CIPHER_PARAM_AEAD_TLS1_AAD: -- Gitee From ff47a6d932cc24946f6c7b3fba2cb4dede706c05 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 1 Nov 2023 16:54:58 +0100 Subject: [PATCH 13/54] update/final: Return error if key is not set Also make sure the key is not set if the key length is changed on the context after the key was set previously. Reviewed-by: Paul Dale Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22590) Signed-off-by: fly2x --- .../implementations/ciphers/cipher_des.c | 1 + .../ciphers/cipher_tdes_common.c | 1 + .../implementations/ciphers/ciphercommon.c | 33 ++++++++++++++++++- .../include/prov/ciphercommon.h | 1 + 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/providers/implementations/ciphers/cipher_des.c b/providers/implementations/ciphers/cipher_des.c index ca2a924a91..e2c890979e 100644 --- a/providers/implementations/ciphers/cipher_des.c +++ b/providers/implementations/ciphers/cipher_des.c @@ -96,6 +96,7 @@ static int des_init(void *vctx, const unsigned char *key, size_t keylen, } if (!ctx->hw->init(ctx, key, keylen)) return 0; + ctx->key_set = 1; } return ossl_cipher_generic_set_ctx_params(ctx, params); } diff --git a/providers/implementations/ciphers/cipher_tdes_common.c b/providers/implementations/ciphers/cipher_tdes_common.c index ceaa0f9821..c80d9f16b1 100644 --- a/providers/implementations/ciphers/cipher_tdes_common.c +++ b/providers/implementations/ciphers/cipher_tdes_common.c @@ -90,6 +90,7 @@ static int tdes_init(void *vctx, const unsigned char *key, size_t keylen, } if (!ctx->hw->init(ctx, key, ctx->keylen)) return 0; + ctx->key_set = 1; } return ossl_cipher_generic_set_ctx_params(ctx, params); } diff --git a/providers/implementations/ciphers/ciphercommon.c b/providers/implementations/ciphers/ciphercommon.c index fa383165d8..7ad3eb0a1f 100644 --- a/providers/implementations/ciphers/ciphercommon.c +++ b/providers/implementations/ciphers/ciphercommon.c @@ -128,7 +128,10 @@ int ossl_cipher_var_keylen_set_ctx_params(void *vctx, const OSSL_PARAM params[]) ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); return 0; } - ctx->keylen = keylen; + if (ctx->keylen != keylen) { + ctx->keylen = keylen; + ctx->key_set = 0; + } } return 1; } @@ -217,6 +220,7 @@ static int cipher_generic_init_internal(PROV_CIPHER_CTX *ctx, } if (!ctx->hw->init(ctx, key, ctx->keylen)) return 0; + ctx->key_set = 1; } return ossl_cipher_generic_set_ctx_params(ctx, params); } @@ -249,6 +253,11 @@ int ossl_cipher_generic_block_update(void *vctx, unsigned char *out, size_t blksz = ctx->blocksize; size_t nextblocks; + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + if (ctx->tlsversion > 0) { /* * Each update call corresponds to a TLS record and is individually @@ -390,6 +399,11 @@ int ossl_cipher_generic_block_final(void *vctx, unsigned char *out, if (!ossl_prov_is_running()) return 0; + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + if (ctx->tlsversion > 0) { /* We never finalize TLS, so this is an error */ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); @@ -456,6 +470,11 @@ int ossl_cipher_generic_stream_update(void *vctx, unsigned char *out, { PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + if (inl == 0) { *outl = 0; return 1; @@ -510,9 +529,16 @@ int ossl_cipher_generic_stream_update(void *vctx, unsigned char *out, int ossl_cipher_generic_stream_final(void *vctx, unsigned char *out, size_t *outl, size_t outsize) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + if (!ossl_prov_is_running()) return 0; + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + *outl = 0; return 1; } @@ -526,6 +552,11 @@ int ossl_cipher_generic_cipher(void *vctx, unsigned char *out, size_t *outl, if (!ossl_prov_is_running()) return 0; + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + if (outsize < inl) { ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); return 0; diff --git a/providers/implementations/include/prov/ciphercommon.h b/providers/implementations/include/prov/ciphercommon.h index 2a7a059086..45002ad594 100644 --- a/providers/implementations/include/prov/ciphercommon.h +++ b/providers/implementations/include/prov/ciphercommon.h @@ -69,6 +69,7 @@ struct prov_cipher_ctx_st { unsigned int pad : 1; /* Whether padding should be used or not */ unsigned int enc : 1; /* Set to 1 for encrypt, or 0 otherwise */ unsigned int iv_set : 1; /* Set when the iv is copied to the iv/oiv buffers */ + unsigned int key_set : 1; /* Set when key is set on the context */ unsigned int updated : 1; /* Set to 1 during update for one shot ciphers */ unsigned int variable_keylength : 1; unsigned int inverse_cipher : 1; /* set to 1 to use inverse cipher */ -- Gitee From 95d396e12adecf0b9ef9ca471ebad4ea3654728b Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 1 Nov 2023 18:14:09 +0100 Subject: [PATCH 14/54] Add negative test for iv length change Reviewed-by: Paul Dale Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22590) Signed-off-by: fly2x --- test/evp_extra_test.c | 61 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index 0ece891159..c558cfb27c 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -4061,7 +4061,7 @@ static int test_evp_reset(int idx) TEST_info("test_evp_reset %d: %s", idx, errmsg); EVP_CIPHER_CTX_free(ctx); EVP_CIPHER_free(type); - return testresult; + return testresult; } typedef struct { @@ -4298,6 +4298,64 @@ static int test_gcm_reinit(int idx) return testresult; } +static const char *ivlen_change_ciphers[] = { + "AES-256-GCM", +#ifndef OPENSSL_NO_OCB + "AES-256-OCB", +#endif + "AES-256-CCM" +}; + +/* Negative test for ivlen change after iv being set */ +static int test_ivlen_change(int idx) +{ + int outlen; + int res = 0; + unsigned char outbuf[1024]; + + static const unsigned char iv[] = { + 0x57, 0x71, 0x7d, 0xad, 0xdb, 0x9b, 0x98, 0x82, + 0x5a, 0x55, 0x91, 0x81, 0x42, 0xa8, 0x89, 0x34 + }; + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *ciph = NULL; + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; + size_t ivlen = 13; /* non-default IV length */ + + if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())) + goto err; + + if (!TEST_ptr(ciph = EVP_CIPHER_fetch(testctx, ivlen_change_ciphers[idx], + testpropq))) + goto err; + + if (!TEST_true(EVP_CipherInit_ex(ctx, ciph, NULL, kGCMDefaultKey, iv, 1))) + goto err; + + if (!TEST_true(EVP_CipherUpdate(ctx, outbuf, &outlen, gcmDefaultPlaintext, + sizeof(gcmDefaultPlaintext)))) + goto err; + + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, + &ivlen); + if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx, params))) + goto err; + + ERR_set_mark(); + if (!TEST_false(EVP_CipherUpdate(ctx, outbuf, &outlen, gcmDefaultPlaintext, + sizeof(gcmDefaultPlaintext)))) { + ERR_clear_last_mark(); + goto err; + } + ERR_pop_to_mark(); + + res = 1; + err: + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(ciph); + return res; +} + #ifndef OPENSSL_NO_DEPRECATED_3_0 static EVP_PKEY_METHOD *custom_pmeth = NULL; static const EVP_PKEY_METHOD *orig_pmeth = NULL; @@ -5419,6 +5477,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_evp_reset, OSSL_NELEM(evp_reset_tests)); ADD_ALL_TESTS(test_gcm_reinit, OSSL_NELEM(gcm_reinit_tests)); ADD_ALL_TESTS(test_evp_updated_iv, OSSL_NELEM(evp_updated_iv_tests)); + ADD_ALL_TESTS(test_ivlen_change, OSSL_NELEM(ivlen_change_ciphers)); #ifndef OPENSSL_NO_DEPRECATED_3_0 ADD_ALL_TESTS(test_custom_pmeth, 12); -- Gitee From 94f17eb9eea612e9c28fdcd070779ee2dded0657 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 1 Nov 2023 18:39:32 +0100 Subject: [PATCH 15/54] Add negative test for key length change Reviewed-by: Paul Dale Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22590) Signed-off-by: fly2x --- test/evp_extra_test.c | 74 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c index c558cfb27c..4250630327 100644 --- a/test/evp_extra_test.c +++ b/test/evp_extra_test.c @@ -4312,7 +4312,6 @@ static int test_ivlen_change(int idx) int outlen; int res = 0; unsigned char outbuf[1024]; - static const unsigned char iv[] = { 0x57, 0x71, 0x7d, 0xad, 0xdb, 0x9b, 0x98, 0x82, 0x5a, 0x55, 0x91, 0x81, 0x42, 0xa8, 0x89, 0x34 @@ -4356,6 +4355,77 @@ static int test_ivlen_change(int idx) return res; } +static const char *keylen_change_ciphers[] = { +#ifndef OPENSSL_NO_BF + "BF-ECB", +#endif +#ifndef OPENSSL_NO_CAST + "CAST5-ECB", +#endif +#ifndef OPENSSL_NO_RC2 + "RC2-ECB", +#endif +#ifndef OPENSSL_NO_RC4 + "RC4", +#endif +#ifndef OPENSSL_NO_RC5 + "RC5-ECB", +#endif + NULL +}; + +/* Negative test for keylen change after key was set */ +static int test_keylen_change(int idx) +{ + int outlen; + int res = 0; + unsigned char outbuf[1024]; + static const unsigned char key[] = { + 0x57, 0x71, 0x7d, 0xad, 0xdb, 0x9b, 0x98, 0x82, + 0x5a, 0x55, 0x91, 0x81, 0x42, 0xa8, 0x89, 0x34 + }; + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *ciph = NULL; + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; + size_t keylen = 12; /* non-default key length */ + + if (lgcyprov == NULL) + return TEST_skip("Test requires legacy provider to be loaded"); + + if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())) + goto err; + + if (!TEST_ptr(ciph = EVP_CIPHER_fetch(testctx, keylen_change_ciphers[idx], + testpropq))) + goto err; + + if (!TEST_true(EVP_CipherInit_ex(ctx, ciph, NULL, key, NULL, 1))) + goto err; + + if (!TEST_true(EVP_CipherUpdate(ctx, outbuf, &outlen, gcmDefaultPlaintext, + sizeof(gcmDefaultPlaintext)))) + goto err; + + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, + &keylen); + if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx, params))) + goto err; + + ERR_set_mark(); + if (!TEST_false(EVP_CipherUpdate(ctx, outbuf, &outlen, gcmDefaultPlaintext, + sizeof(gcmDefaultPlaintext)))) { + ERR_clear_last_mark(); + goto err; + } + ERR_pop_to_mark(); + + res = 1; + err: + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(ciph); + return res; +} + #ifndef OPENSSL_NO_DEPRECATED_3_0 static EVP_PKEY_METHOD *custom_pmeth = NULL; static const EVP_PKEY_METHOD *orig_pmeth = NULL; @@ -5478,6 +5548,8 @@ int setup_tests(void) ADD_ALL_TESTS(test_gcm_reinit, OSSL_NELEM(gcm_reinit_tests)); ADD_ALL_TESTS(test_evp_updated_iv, OSSL_NELEM(evp_updated_iv_tests)); ADD_ALL_TESTS(test_ivlen_change, OSSL_NELEM(ivlen_change_ciphers)); + if (OSSL_NELEM(keylen_change_ciphers) - 1 > 0) + ADD_ALL_TESTS(test_keylen_change, OSSL_NELEM(keylen_change_ciphers) - 1); #ifndef OPENSSL_NO_DEPRECATED_3_0 ADD_ALL_TESTS(test_custom_pmeth, 12); -- Gitee From f0d520bdffb95f98abdee194aa9141686abf6706 Mon Sep 17 00:00:00 2001 From: "Matthias St. Pierre" Date: Thu, 2 Nov 2023 20:51:52 +0100 Subject: [PATCH 16/54] internal/common.h: rename macro `(un)likely` to `ossl_(un)likely` The macro was introduced in commit ed6dfd1e3694 without an openssl-specific prefix as mandated by the coding style. Reviewed-by: Tim Hudson Reviewed-by: Tom Cosgrove (Merged from https://github.com/openssl/openssl/pull/22603) Signed-off-by: fly2x --- crypto/evp/evp_enc.c | 24 ++++++++++++------------ crypto/rand/rand_uniform.c | 6 +++--- include/internal/common.h | 10 +++++----- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c index e9faf31057..a199529712 100644 --- a/crypto/evp/evp_enc.c +++ b/crypto/evp/evp_enc.c @@ -662,7 +662,7 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, size_t soutl, inl_ = (size_t)inl; int blocksize; - if (likely(outl != NULL)) { + if (ossl_likely(outl != NULL)) { *outl = 0; } else { ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER); @@ -670,22 +670,22 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, } /* Prevent accidental use of decryption context when encrypting */ - if (unlikely(!ctx->encrypt)) { + if (ossl_unlikely(!ctx->encrypt)) { ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_OPERATION); return 0; } - if (unlikely(ctx->cipher == NULL)) { + if (ossl_unlikely(ctx->cipher == NULL)) { ERR_raise(ERR_LIB_EVP, EVP_R_NO_CIPHER_SET); return 0; } - if (unlikely(ctx->cipher->prov == NULL)) + if (ossl_unlikely(ctx->cipher->prov == NULL)) goto legacy; blocksize = ctx->cipher->block_size; - if (unlikely(ctx->cipher->cupdate == NULL || blocksize < 1)) { + if (ossl_unlikely(ctx->cipher->cupdate == NULL || blocksize < 1)) { ERR_raise(ERR_LIB_EVP, EVP_R_UPDATE_ERROR); return 0; } @@ -694,7 +694,7 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, inl_ + (size_t)(blocksize == 1 ? 0 : blocksize), in, inl_); - if (likely(ret)) { + if (ossl_likely(ret)) { if (soutl > INT_MAX) { ERR_raise(ERR_LIB_EVP, EVP_R_UPDATE_ERROR); return 0; @@ -811,7 +811,7 @@ int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, size_t soutl, inl_ = (size_t)inl; int blocksize; - if (likely(outl != NULL)) { + if (ossl_likely(outl != NULL)) { *outl = 0; } else { ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER); @@ -819,21 +819,21 @@ int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, } /* Prevent accidental use of encryption context when decrypting */ - if (unlikely(ctx->encrypt)) { + if (ossl_unlikely(ctx->encrypt)) { ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_OPERATION); return 0; } - if (unlikely(ctx->cipher == NULL)) { + if (ossl_unlikely(ctx->cipher == NULL)) { ERR_raise(ERR_LIB_EVP, EVP_R_NO_CIPHER_SET); return 0; } - if (unlikely(ctx->cipher->prov == NULL)) + if (ossl_unlikely(ctx->cipher->prov == NULL)) goto legacy; blocksize = EVP_CIPHER_CTX_get_block_size(ctx); - if (unlikely(ctx->cipher->cupdate == NULL || blocksize < 1)) { + if (ossl_unlikely(ctx->cipher->cupdate == NULL || blocksize < 1)) { ERR_raise(ERR_LIB_EVP, EVP_R_UPDATE_ERROR); return 0; } @@ -841,7 +841,7 @@ int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, inl_ + (size_t)(blocksize == 1 ? 0 : blocksize), in, inl_); - if (likely(ret)) { + if (ossl_likely(ret)) { if (soutl > INT_MAX) { ERR_raise(ERR_LIB_EVP, EVP_R_UPDATE_ERROR); return 0; diff --git a/crypto/rand/rand_uniform.c b/crypto/rand/rand_uniform.c index b9e2635859..f0b199b95a 100644 --- a/crypto/rand/rand_uniform.c +++ b/crypto/rand/rand_uniform.c @@ -34,7 +34,7 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err) *err = 0; return 0; } - if (unlikely(upper == 1)) + if (ossl_unlikely(upper == 1)) return 0; /* Get 32 bits of entropy */ @@ -56,7 +56,7 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err) prod = (uint64_t)upper * rand; i = prod >> 32; f = prod & 0xffffffff; - if (likely(f <= 1 + ~upper)) /* 1+~upper == -upper but compilers whine */ + if (ossl_likely(f <= 1 + ~upper)) /* 1+~upper == -upper but compilers whine */ return i; /* @@ -85,7 +85,7 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err) if (f < f2) return i + 1; /* For not all 1 bits, there is no carry so return the result */ - if (likely(f != 0xffffffff)) + if (ossl_likely(f != 0xffffffff)) return i; /* setup for the next word of randomness */ f = prod & 0xffffffff; diff --git a/include/internal/common.h b/include/internal/common.h index a9e3b7a729..47cb6631f5 100644 --- a/include/internal/common.h +++ b/include/internal/common.h @@ -19,11 +19,11 @@ # include "internal/nelem.h" # if defined(__GNUC__) || defined(__clang__) -# define likely(x) __builtin_expect(!!(x), 1) -# define unlikely(x) __builtin_expect(!!(x), 0) +# define ossl_likely(x) __builtin_expect(!!(x), 1) +# define ossl_unlikely(x) __builtin_expect(!!(x), 0) # else -# define likely(x) x -# define unlikely(x) x +# define ossl_likely(x) x +# define ossl_unlikely(x) x # endif # if defined(__GNUC__) || defined(__clang__) @@ -38,7 +38,7 @@ # endif # ifdef NDEBUG -# define ossl_assert(x) likely((x) != 0) +# define ossl_assert(x) ossl_likely((x) != 0) # else __owur static ossl_inline int ossl_assert_int(int expr, const char *exprstr, const char *file, int line) -- Gitee From 0ca7d1ed5045ada340cff8f020f01c240f3828bf Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Thu, 2 Nov 2023 17:19:38 +0100 Subject: [PATCH 17/54] ossl_qrl_enc_level_set_provide_secret(): Avoid leaking keyslot in error condition Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22600) Signed-off-by: fly2x --- ssl/quic/quic_record_shared.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ssl/quic/quic_record_shared.c b/ssl/quic/quic_record_shared.c index 36807c7dd5..a3fd51db6d 100644 --- a/ssl/quic/quic_record_shared.c +++ b/ssl/quic/quic_record_shared.c @@ -185,7 +185,7 @@ static int el_setup_keyslot(OSSL_QRL_ENC_LEVEL_SET *els, EVP_CIPHER_free(cipher); return 1; -err: + err: EVP_CIPHER_CTX_free(cctx); EVP_CIPHER_free(cipher); OPENSSL_cleanse(el->iv[keyslot], sizeof(el->iv[keyslot])); @@ -327,16 +327,16 @@ int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els, el->state = QRL_EL_STATE_PROV_NORMAL; return 1; -err: + err: el->suite_id = 0; el->md = NULL; OPENSSL_cleanse(hpr_key, sizeof(hpr_key)); OPENSSL_cleanse(ku_key, sizeof(ku_key)); OPENSSL_cleanse(el->ku, sizeof(el->ku)); if (have_ks0) - el_teardown_keyslot(els, enc_level, 0); + el_teardown_keyslot(els, enc_level, init_keyslot); if (have_ks1) - el_teardown_keyslot(els, enc_level, 1); + el_teardown_keyslot(els, enc_level, !init_keyslot); if (own_md) EVP_MD_free(md); return 0; -- Gitee From 8d02da98c1eea7b476c4350e783504a283fb4cc2 Mon Sep 17 00:00:00 2001 From: Michael Hinz Date: Fri, 3 Nov 2023 14:17:39 +0100 Subject: [PATCH 18/54] Fix documentation regarding KMAC sizes As per recommendation by jfinkhaeuser, this documents the defaults for KMAC-128 as 32 and for KMAC-256 as 64. The code already accomodates for these values, so no changes are needed there. Fixes #22381 CLA: trivial Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Shane Lontis Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22614) Signed-off-by: fly2x --- doc/man7/EVP_MAC-KMAC.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man7/EVP_MAC-KMAC.pod b/doc/man7/EVP_MAC-KMAC.pod index 1065c166db..dc67c66336 100644 --- a/doc/man7/EVP_MAC-KMAC.pod +++ b/doc/man7/EVP_MAC-KMAC.pod @@ -51,7 +51,7 @@ It is an optional value with a length of at most 512 bytes, and is empty by defa =item "size" (B) Sets the MAC size. -By default, it is 16 for C and 32 for C. +By default, it is 32 for C and 64 for C. =item "block-size" (B) -- Gitee From 76d9469e6fa34b461b243574aac97a2964577fdc Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 3 Nov 2023 08:52:43 +0100 Subject: [PATCH 19/54] ossl_quic_new(): Avoid dereferencing NULL qc during cleanup Fixes Coverity 1548383 Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/22606) Signed-off-by: fly2x --- ssl/quic/quic_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index dd689865e4..399d1d2afd 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -382,7 +382,7 @@ SSL *ossl_quic_new(SSL_CTX *ctx) qc = OPENSSL_zalloc(sizeof(*qc)); if (qc == NULL) { QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); - goto err; + return NULL; } #if defined(OPENSSL_THREADS) if ((qc->mutex = ossl_crypto_mutex_new()) == NULL) { -- Gitee From 718ffd8c1e3064a15c52cc7b1e114c8c4d520e8a Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 3 Nov 2023 08:57:21 +0100 Subject: [PATCH 20/54] tparam_on_enc_ext(): Remove dead code in cleanup Fixes Coverity 1548382 Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/22606) Signed-off-by: fly2x --- test/quicapitest.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/quicapitest.c b/test/quicapitest.c index 77c9598c69..41cf0fc7a8 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -1994,12 +1994,8 @@ static int tparam_on_enc_ext(QTEST_FAULT *qtf, QTEST_ENCRYPTED_EXTENSIONS *ee, rc = 1; err: - if (have_wpkt) { - if (rc) - WPACKET_finish(&wpkt); - else - WPACKET_cleanup(&wpkt); - } + if (have_wpkt) + WPACKET_cleanup(&wpkt); BUF_MEM_free(old_bufm); BUF_MEM_free(new_bufm); return rc; -- Gitee From cf5a6060d9a44824bec1ea0aa32c21b900e3a7d9 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Thu, 2 Nov 2023 16:25:27 +0000 Subject: [PATCH 21/54] Correctly track the original length when generating a stream frame txp_generate_stream_frames() plans chunks of data to send via the function txp_plan_stream_chunk(). That function may clamp the amount in the chunk due to flow control, even though there is more available to send. We should take this into account when deciding whether or not to try serializing the next chunk. Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22601) Signed-off-by: fly2x --- ssl/quic/quic_txp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index 2af385af0f..1f93638bf6 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -2073,6 +2073,7 @@ static int txp_generate_crypto_frames(OSSL_QUIC_TX_PACKETISER *txp, struct chunk_info { OSSL_QUIC_FRAME_STREAM shdr; + uint64_t orig_len; OSSL_QTX_IOVEC iov[2]; size_t num_stream_iovec; int valid; @@ -2099,6 +2100,8 @@ static int txp_plan_stream_chunk(OSSL_QUIC_TX_PACKETISER *txp, /* Should only have 0-length chunk if FIN */ return 0; + chunk->orig_len = chunk->shdr.len; + /* Clamp according to connection and stream-level TXFC. */ fc_credit = ossl_quic_txfc_get_credit(stream_txfc); fc_swm = ossl_quic_txfc_get_swm(stream_txfc); @@ -2199,7 +2202,7 @@ static int txp_generate_stream_frames(OSSL_QUIC_TX_PACKETISER *txp, goto err; shdr = &chunks[i % 2].shdr; - orig_len = shdr->len; + orig_len = chunks[i % 2].orig_len; if (i > 0) /* Load next chunk for lookahead. */ if (!txp_plan_stream_chunk(txp, h, sstream, stream_txfc, i + 1, @@ -2331,8 +2334,7 @@ static int txp_generate_stream_frames(OSSL_QUIC_TX_PACKETISER *txp, if (shdr->len < orig_len) { /* * If we did not serialize all of this chunk we definitely do not - * want to try the next chunk (and we must not mark the stream - * as drained). + * want to try the next chunk */ rc = 1; goto err; -- Gitee From cfd4efa8edeaee00ccde188e41240dc3b814a556 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 1 Nov 2023 08:53:18 -0400 Subject: [PATCH 22/54] Introduce libabigail checking in ci It would be nice if we could monitor the consistency of our ABI from PR to PR, to ensure there are no inadvertent changes to the library ABI. Introduce a new CI job that runs the libabigail tools to build an ABI representation of the PR-built library and compares it to a stored/expected representation, reporting any discrepancies it finds. Fixes #22571 Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22589) Signed-off-by: fly2x --- .github/workflows/ci.yml | 12 +- .github/workflows/libcrypto-abi.xml | 5523 +++++++++++++++++++++++++++ .github/workflows/libssl-abi.xml | 587 +++ 3 files changed, 6121 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/libcrypto-abi.xml create mode 100644 .github/workflows/libssl-abi.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 815f7c61b5..72c0167b85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -272,14 +272,24 @@ jobs: run: git submodule update --init --depth 1 fuzz/corpora - name: modprobe tls run: sudo modprobe tls + - name: Enable sctp + run: sudo modprobe sctp + - name: Enable auth in sctp + run: sudo sysctl -w net.sctp.auth_enable=1 + - name: install extra config support + run: sudo apt-get -y install libsctp-dev abigail-tools libzstd-dev zstd - name: config - run: ./config --banner=Configured --strict-warnings enable-ktls enable-fips && perl configdata.pm --dump + run: ./config --banner=Configured --strict-warnings enable-ktls enable-fips enable-egd enable-ec_nistp_64_gcc_128 enable-md2 enable-rc5 enable-sctp enable-ssl3 enable-ssl3-method enable-trace enable-zlib enable-zstd && perl configdata.pm --dump - name: make run: make -s -j4 - name: get cpu info run: | cat /proc/cpuinfo ./util/opensslwrap.sh version -c + - name: Check ABI compatibility for libcrypto + run: abidiff ./.github/workflows/libcrypto-abi.xml ./libcrypto.so + - name: Check ABI compatibility for libssl + run: abidiff ./.github/workflows/libssl-abi.xml ./libssl.so - name: make test run: make test HARNESS_JOBS=${HARNESS_JOBS:-4} diff --git a/.github/workflows/libcrypto-abi.xml b/.github/workflows/libcrypto-abi.xml new file mode 100644 index 0000000000..d82bc8b320 --- /dev/null +++ b/.github/workflows/libcrypto-abi.xml @@ -0,0 +1,5523 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/workflows/libssl-abi.xml b/.github/workflows/libssl-abi.xml new file mode 100644 index 0000000000..cb66e04b3d --- /dev/null +++ b/.github/workflows/libssl-abi.xml @@ -0,0 +1,587 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- Gitee From e8eb56d3f7ff6ebf3ed06fc699c5483248262367 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 20 Oct 2023 09:18:19 +0200 Subject: [PATCH 23/54] Make DH_check_pub_key() and DH_generate_key() safer yet We already check for an excessively large P in DH_generate_key(), but not in DH_check_pub_key(), and none of them check for an excessively large Q. This change adds all the missing excessive size checks of P and Q. It's to be noted that behaviours surrounding excessively sized P and Q differ. DH_check() raises an error on the excessively sized P, but only sets a flag for the excessively sized Q. This behaviour is mimicked in DH_check_pub_key(). Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22453) Signed-off-by: fly2x --- crypto/dh/dh_check.c | 12 ++++++++++++ crypto/dh/dh_err.c | 3 ++- crypto/dh/dh_key.c | 12 ++++++++++++ crypto/err/openssl.txt | 1 + include/crypto/dherr.h | 2 +- include/openssl/dh.h | 6 +++--- include/openssl/dherr.h | 3 ++- 7 files changed, 33 insertions(+), 6 deletions(-) diff --git a/crypto/dh/dh_check.c b/crypto/dh/dh_check.c index 7ba2beae7f..e20eb62081 100644 --- a/crypto/dh/dh_check.c +++ b/crypto/dh/dh_check.c @@ -249,6 +249,18 @@ int DH_check_pub_key_ex(const DH *dh, const BIGNUM *pub_key) */ int DH_check_pub_key(const DH *dh, const BIGNUM *pub_key, int *ret) { + /* Don't do any checks at all with an excessively large modulus */ + if (BN_num_bits(dh->params.p) > OPENSSL_DH_CHECK_MAX_MODULUS_BITS) { + ERR_raise(ERR_LIB_DH, DH_R_MODULUS_TOO_LARGE); + *ret = DH_MODULUS_TOO_LARGE | DH_CHECK_PUBKEY_INVALID; + return 0; + } + + if (dh->params.q != NULL && BN_ucmp(dh->params.p, dh->params.q) < 0) { + *ret |= DH_CHECK_INVALID_Q_VALUE | DH_CHECK_PUBKEY_INVALID; + return 1; + } + return ossl_ffc_validate_public_key(&dh->params, pub_key, ret); } diff --git a/crypto/dh/dh_err.c b/crypto/dh/dh_err.c index f1f4fb6a37..4d6d2acd98 100644 --- a/crypto/dh/dh_err.c +++ b/crypto/dh/dh_err.c @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -55,6 +55,7 @@ static const ERR_STRING_DATA DH_str_reasons[] = { {ERR_PACK(ERR_LIB_DH, 0, DH_R_PARAMETER_ENCODING_ERROR), "parameter encoding error"}, {ERR_PACK(ERR_LIB_DH, 0, DH_R_PEER_KEY_ERROR), "peer key error"}, + {ERR_PACK(ERR_LIB_DH, 0, DH_R_Q_TOO_LARGE), "q too large"}, {ERR_PACK(ERR_LIB_DH, 0, DH_R_SHARED_INFO_ERROR), "shared info error"}, {ERR_PACK(ERR_LIB_DH, 0, DH_R_UNABLE_TO_CHECK_GENERATOR), "unable to check generator"}, diff --git a/crypto/dh/dh_key.c b/crypto/dh/dh_key.c index 8ab75e06d7..7132b9b68e 100644 --- a/crypto/dh/dh_key.c +++ b/crypto/dh/dh_key.c @@ -49,6 +49,12 @@ int ossl_dh_compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh) goto err; } + if (dh->params.q != NULL + && BN_num_bits(dh->params.q) > OPENSSL_DH_MAX_MODULUS_BITS) { + ERR_raise(ERR_LIB_DH, DH_R_Q_TOO_LARGE); + goto err; + } + if (BN_num_bits(dh->params.p) < DH_MIN_MODULUS_BITS) { ERR_raise(ERR_LIB_DH, DH_R_MODULUS_TOO_SMALL); return 0; @@ -271,6 +277,12 @@ static int generate_key(DH *dh) return 0; } + if (dh->params.q != NULL + && BN_num_bits(dh->params.q) > OPENSSL_DH_MAX_MODULUS_BITS) { + ERR_raise(ERR_LIB_DH, DH_R_Q_TOO_LARGE); + return 0; + } + if (BN_num_bits(dh->params.p) < DH_MIN_MODULUS_BITS) { ERR_raise(ERR_LIB_DH, DH_R_MODULUS_TOO_SMALL); return 0; diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index fc30a2205a..5f60bd52d2 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -535,6 +535,7 @@ DH_R_NO_PARAMETERS_SET:107:no parameters set DH_R_NO_PRIVATE_VALUE:100:no private value DH_R_PARAMETER_ENCODING_ERROR:105:parameter encoding error DH_R_PEER_KEY_ERROR:111:peer key error +DH_R_Q_TOO_LARGE:130:q too large DH_R_SHARED_INFO_ERROR:113:shared info error DH_R_UNABLE_TO_CHECK_GENERATOR:121:unable to check generator DSA_R_BAD_FFC_PARAMETERS:114:bad ffc parameters diff --git a/include/crypto/dherr.h b/include/crypto/dherr.h index af2c158144..519327f795 100644 --- a/include/crypto/dherr.h +++ b/include/crypto/dherr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/include/openssl/dh.h b/include/openssl/dh.h index 8bc17448a0..f1c0ed06b3 100644 --- a/include/openssl/dh.h +++ b/include/openssl/dh.h @@ -144,7 +144,7 @@ DECLARE_ASN1_ITEM(DHparams) # define DH_GENERATOR_3 3 # define DH_GENERATOR_5 5 -/* DH_check error codes */ +/* DH_check error codes, some of them shared with DH_check_pub_key */ /* * NB: These values must align with the equivalently named macros in * internal/ffc.h. @@ -154,10 +154,10 @@ DECLARE_ASN1_ITEM(DHparams) # define DH_UNABLE_TO_CHECK_GENERATOR 0x04 # define DH_NOT_SUITABLE_GENERATOR 0x08 # define DH_CHECK_Q_NOT_PRIME 0x10 -# define DH_CHECK_INVALID_Q_VALUE 0x20 +# define DH_CHECK_INVALID_Q_VALUE 0x20 /* +DH_check_pub_key */ # define DH_CHECK_INVALID_J_VALUE 0x40 # define DH_MODULUS_TOO_SMALL 0x80 -# define DH_MODULUS_TOO_LARGE 0x100 +# define DH_MODULUS_TOO_LARGE 0x100 /* +DH_check_pub_key */ /* DH_check_pub_key error codes */ # define DH_CHECK_PUBKEY_TOO_SMALL 0x01 diff --git a/include/openssl/dherr.h b/include/openssl/dherr.h index eda3b1c9a5..2997d7d4c2 100644 --- a/include/openssl/dherr.h +++ b/include/openssl/dherr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -51,6 +51,7 @@ # define DH_R_NO_PRIVATE_VALUE 100 # define DH_R_PARAMETER_ENCODING_ERROR 105 # define DH_R_PEER_KEY_ERROR 111 +# define DH_R_Q_TOO_LARGE 130 # define DH_R_SHARED_INFO_ERROR 113 # define DH_R_UNABLE_TO_CHECK_GENERATOR 121 -- Gitee From fd094d40fdd693865fb4ce77773ac06c09567ec9 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 20 Oct 2023 09:24:01 +0200 Subject: [PATCH 24/54] Fix conflicts between DH check flags and FFC check flags There are comments in include/openssl/dh.h and include/internal/ffc.h that they must be aligned with each other, and yet, clashes have been introduced. The simplest fix is to move the offending FFC flags out of the way, as they are indeed internal and shouldn't affect any public interface, apart from those that are aligned with the DH flags, which are public. Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22453) Signed-off-by: fly2x --- include/internal/ffc.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/internal/ffc.h b/include/internal/ffc.h index 01b8a4f9d3..edd8381e8d 100644 --- a/include/internal/ffc.h +++ b/include/internal/ffc.h @@ -58,8 +58,11 @@ # define FFC_CHECK_INVALID_Q_VALUE 0x00020 # define FFC_CHECK_INVALID_J_VALUE 0x00040 -# define FFC_CHECK_BAD_LN_PAIR 0x00080 -# define FFC_CHECK_INVALID_SEED_SIZE 0x00100 +/* + * 0x80, 0x100 reserved by include/openssl/dh.h with check bits that are not + * relevant for FFC. + */ + # define FFC_CHECK_MISSING_SEED_OR_COUNTER 0x00200 # define FFC_CHECK_INVALID_G 0x00400 # define FFC_CHECK_INVALID_PQ 0x00800 @@ -68,6 +71,8 @@ # define FFC_CHECK_Q_MISMATCH 0x04000 # define FFC_CHECK_G_MISMATCH 0x08000 # define FFC_CHECK_COUNTER_MISMATCH 0x10000 +# define FFC_CHECK_BAD_LN_PAIR 0x20000 +# define FFC_CHECK_INVALID_SEED_SIZE 0x40000 /* Validation Return codes */ # define FFC_ERROR_PUBKEY_TOO_SMALL 0x01 -- Gitee From 2356ffa886e7db875d9b5e87b126b02d024acfa4 Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Mon, 6 Nov 2023 10:53:46 +0100 Subject: [PATCH 25/54] Use proper KDF SS parameter name Reviewed-by: Matt Caswell Reviewed-by: Paul Dale Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22636) Signed-off-by: fly2x --- doc/man7/EVP_KDF-SS.pod | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/man7/EVP_KDF-SS.pod b/doc/man7/EVP_KDF-SS.pod index 7f158e4216..c8d19691a7 100644 --- a/doc/man7/EVP_KDF-SS.pod +++ b/doc/man7/EVP_KDF-SS.pod @@ -53,7 +53,7 @@ This parameter is ignored for KMAC. These parameters work as described in L. -=item "key" (B) +=item "key" (B) This parameter set the shared secret that is used for key derivation. @@ -116,7 +116,7 @@ fixedinfo value "label" and salt "salt": SN_hmac, strlen(SN_hmac)); *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha256, strlen(SN_sha256)); - *p++ = OSSL_PARAM_construct_octet_string(EVP_KDF_CTRL_SET_KEY, + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET, "secret", (size_t)6); *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, "label", (size_t)5); @@ -143,7 +143,7 @@ fixedinfo value "label", salt of "salt" and KMAC outlen of 20: *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC, SN_kmac128, strlen(SN_kmac128)); - *p++ = OSSL_PARAM_construct_octet_string(EVP_KDF_CTRL_SET_KEY, + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET, "secret", (size_t)6); *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, "label", (size_t)5); -- Gitee From 10d49b684cf74da17370867de24f8bd5c1f2c093 Mon Sep 17 00:00:00 2001 From: James Muir Date: Sun, 29 Oct 2023 15:24:08 -0400 Subject: [PATCH 26/54] quic http3 demo: minor updates -update run command to include LD_LIBRARY_PATH -suggest installing libnghttp3-dev on Ubuntu -drop "-f" from clean recipe (it is already included in $(RM)) Part of https://github.com/openssl/project/issues/253 Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/22623) Signed-off-by: fly2x --- demos/http3/Makefile | 12 +++++++++--- demos/http3/README.md | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/demos/http3/Makefile b/demos/http3/Makefile index 89bac3743e..9d8212ff0a 100644 --- a/demos/http3/Makefile +++ b/demos/http3/Makefile @@ -1,11 +1,17 @@ -CFLAGS = -I../../include -g -Wall +# +# To run the demo when linked with a shared library (default) ensure that +# libcrypto and libssl are on the library path. For example: +# +# LD_LIBRARY_PATH=../.. ./ossl-nghttp3-demo www.example.com:443 + +CFLAGS = -I../../include -g -Wall -Wsign-compare LDFLAGS = -L../.. -LDLIBS = -lcrypto -lssl -lnghttp3 +LDLIBS = -lcrypto -lssl -lnghttp3 all: ossl-nghttp3-demo clean: - $(RM) -f ossl-nghttp3-demo *.o + $(RM) ossl-nghttp3-demo *.o ossl-nghttp3-demo: ossl-nghttp3-demo.o ossl-nghttp3.o $(CC) $(CFLAGS) -o "$@" $^ $(LDFLAGS) $(LDLIBS) diff --git a/demos/http3/README.md b/demos/http3/README.md index e087aba1e1..e193aa13c1 100644 --- a/demos/http3/README.md +++ b/demos/http3/README.md @@ -12,7 +12,8 @@ The demo is structured into two parts: layer (`ossl-nghttp3-demo.c`). The Makefile in this directory can be used to build the demo on \*nix-style -systems. You will need to have the `nghttp3` library available. +systems. You will need the `nghttp3` library and header file. On +Ubuntu, these can be obtained by installing the package `libnghttp3-dev`. Running the Demo ---------------- @@ -26,7 +27,7 @@ port as the sole argument: ```shell $ make -$ ./ossl-nghttp3-demo www.google.com:443 +$ LD_LIBRARY_PATH=../.. ./ossl-nghttp3-demo www.google.com:443 ``` The demo produces the HTTP response headers in textual form as output followed -- Gitee From 6b1bcdfeafc57fe0054fcb9b40ad8ee33ade9d40 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Mon, 6 Nov 2023 11:03:05 +0100 Subject: [PATCH 27/54] Fix a possible memory leak in custom_ext_add Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/22638) Signed-off-by: fly2x --- ssl/statem/extensions_cust.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c index 8b296f1f59..7c049d2970 100644 --- a/ssl/statem/extensions_cust.c +++ b/ssl/statem/extensions_cust.c @@ -225,6 +225,9 @@ int custom_ext_add(SSL_CONNECTION *s, int context, WPACKET *pkt, X509 *x, || !WPACKET_start_sub_packet_u16(pkt) || (outlen > 0 && !WPACKET_memcpy(pkt, out, outlen)) || !WPACKET_close(pkt)) { + if (meth->free_cb != NULL) + meth->free_cb(SSL_CONNECTION_GET_SSL(s), meth->ext_type, context, + out, meth->add_arg); if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; @@ -234,6 +237,9 @@ int custom_ext_add(SSL_CONNECTION *s, int context, WPACKET *pkt, X509 *x, * We can't send duplicates: code logic should prevent this. */ if (!ossl_assert((meth->ext_flags & SSL_EXT_FLAG_SENT) == 0)) { + if (meth->free_cb != NULL) + meth->free_cb(SSL_CONNECTION_GET_SSL(s), meth->ext_type, + context, out, meth->add_arg); if (!for_comp) SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; -- Gitee From b202070f76a7bc55f7679dd00bab4b159327b3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Wed, 18 Oct 2023 18:40:07 +0200 Subject: [PATCH 28/54] Lift BLAKE2B provider definition into a macro Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22444) Signed-off-by: fly2x --- .../implementations/digests/blake2_prov.c | 269 +++++++++++------- .../implementations/digests/blake2b_prov.c | 71 ----- 2 files changed, 171 insertions(+), 169 deletions(-) diff --git a/providers/implementations/digests/blake2_prov.c b/providers/implementations/digests/blake2_prov.c index d31627b92e..f80d307321 100644 --- a/providers/implementations/digests/blake2_prov.c +++ b/providers/implementations/digests/blake2_prov.c @@ -8,7 +8,9 @@ */ #include +#include #include +#include #include "prov/blake2.h" #include "prov/digestcommon.h" #include "prov/implementations.h" @@ -21,17 +23,6 @@ static int ossl_blake2s256_init(void *ctx) return ossl_blake2s_init((BLAKE2S_CTX *)ctx, &P); } -static int ossl_blake2b512_init(void *ctx) -{ - struct blake2b_md_data_st *mdctx = ctx; - uint8_t digest_length = mdctx->params.digest_length; - - ossl_blake2b_param_init(&mdctx->params); - if (digest_length != 0) - mdctx->params.digest_length = digest_length; - return ossl_blake2b_init(&mdctx->ctx, &mdctx->params); -} - /* ossl_blake2s256_functions */ IMPLEMENT_digest_functions(blake2s256, BLAKE2S_CTX, BLAKE2S_BLOCKBYTES, BLAKE2S_DIGEST_LENGTH, 0, @@ -40,91 +31,173 @@ IMPLEMENT_digest_functions(blake2s256, BLAKE2S_CTX, /* ossl_blake2b512_functions */ -static OSSL_FUNC_digest_init_fn blake2b512_internal_init; -static OSSL_FUNC_digest_newctx_fn blake2b512_newctx; -static OSSL_FUNC_digest_freectx_fn blake2b512_freectx; -static OSSL_FUNC_digest_dupctx_fn blake2b512_dupctx; -static OSSL_FUNC_digest_final_fn blake2b512_internal_final; -static OSSL_FUNC_digest_get_params_fn blake2b512_get_params; - -static int blake2b512_internal_init(void *ctx, const OSSL_PARAM params[]) -{ - return ossl_prov_is_running() && ossl_blake2b_set_ctx_params(ctx, params) - && ossl_blake2b512_init(ctx); -} - -static void *blake2b512_newctx(void *prov_ctx) -{ - struct blake2b_md_data_st *ctx; - - ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) : NULL; - return ctx; -} - -static void blake2b512_freectx(void *vctx) -{ - struct blake2b_md_data_st *ctx; - - ctx = (struct blake2b_md_data_st *)vctx; - OPENSSL_clear_free(ctx, sizeof(*ctx)); -} - -static void *blake2b512_dupctx(void *ctx) -{ - struct blake2b_md_data_st *in, *ret; - - in = (struct blake2b_md_data_st *)ctx; - ret = ossl_prov_is_running()? OPENSSL_malloc(sizeof(*ret)) : NULL; - if (ret != NULL) - *ret = *in; - return ret; -} - -static int blake2b512_internal_final(void *ctx, unsigned char *out, - size_t *outl, size_t outsz) -{ - struct blake2b_md_data_st *b_ctx; - - b_ctx = (struct blake2b_md_data_st *)ctx; - - if (!ossl_prov_is_running()) - return 0; - - *outl = b_ctx->ctx.outlen; - - if (outsz == 0) - return 1; - - if (outsz < *outl) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); - return 0; - } - - return ossl_blake2b_final(out, ctx); -} - -static int blake2b512_get_params(OSSL_PARAM params[]) -{ - return ossl_digest_default_get_params(params, BLAKE2B_BLOCKBYTES, 64, 0); -} - -const OSSL_DISPATCH ossl_blake2b512_functions[] = { - {OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))blake2b512_newctx}, - {OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))ossl_blake2b_update}, - {OSSL_FUNC_DIGEST_FINAL, (void (*)(void))blake2b512_internal_final}, - {OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))blake2b512_freectx}, - {OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))blake2b512_dupctx}, - {OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))blake2b512_get_params}, - {OSSL_FUNC_DIGEST_GETTABLE_PARAMS, - (void (*)(void))ossl_digest_default_gettable_params}, - {OSSL_FUNC_DIGEST_INIT, (void (*)(void))blake2b512_internal_init}, - {OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS, - (void (*)(void))ossl_blake2b_gettable_ctx_params}, - {OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, - (void (*)(void))ossl_blake2b_settable_ctx_params}, - {OSSL_FUNC_DIGEST_GET_CTX_PARAMS, - (void (*)(void))ossl_blake2b_get_ctx_params}, - {OSSL_FUNC_DIGEST_SET_CTX_PARAMS, - (void (*)(void))ossl_blake2b_set_ctx_params}, - {0, NULL} +#define IMPLEMENT_BLAKE_functions(variant, VARIANT, variantsize) \ +static const OSSL_PARAM known_blake##variant##_ctx_params[] = { \ + {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, \ + OSSL_PARAM_END \ +}; \ + \ +const OSSL_PARAM *ossl_blake##variant##_gettable_ctx_params(ossl_unused void *ctx, \ + ossl_unused void *pctx) \ +{ \ + return known_blake##variant##_ctx_params; \ +} \ + \ +const OSSL_PARAM *ossl_blake##variant##_settable_ctx_params(ossl_unused void *ctx, \ + ossl_unused void *pctx) \ +{ \ + return known_blake##variant##_ctx_params; \ +} \ + \ +int ossl_blake##variant##_get_ctx_params(void *vctx, OSSL_PARAM params[]) \ +{ \ + struct blake##variant##_md_data_st *mdctx = vctx; \ + OSSL_PARAM *p; \ + \ + BLAKE##VARIANT##_CTX *ctx = &mdctx->ctx; \ + \ + if (ctx == NULL) \ + return 0; \ + if (params == NULL) \ + return 1; \ + \ + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE); \ + if (p != NULL \ + && !OSSL_PARAM_set_uint(p, (unsigned int)mdctx->params.digest_length)) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); \ + return 0; \ + } \ + \ + return 1; \ +} \ + \ +int ossl_blake##variant##_set_ctx_params(void *vctx, const OSSL_PARAM params[]) \ +{ \ + size_t size; \ + struct blake##variant##_md_data_st *mdctx = vctx; \ + const OSSL_PARAM *p; \ + \ + BLAKE##VARIANT##_CTX *ctx = &mdctx->ctx; \ + \ + if (ctx == NULL) \ + return 0; \ + if (params == NULL) \ + return 1; \ + \ + p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SIZE); \ + if (p != NULL) { \ + if (!OSSL_PARAM_get_size_t(p, &size)) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); \ + return 0; \ + } \ + if (size < 1 || size > UINT8_MAX) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); \ + return 0; \ + } \ + ossl_blake##variant##_param_set_digest_length(&mdctx->params, (uint8_t)size); \ + } \ + \ + return 1; \ +} \ + \ +static int ossl_blake##variantsize##_init(void *ctx) \ +{ \ + struct blake##variant##_md_data_st *mdctx = ctx; \ + uint8_t digest_length = mdctx->params.digest_length; \ + \ + ossl_blake##variant##_param_init(&mdctx->params); \ + if (digest_length != 0) \ + mdctx->params.digest_length = digest_length; \ + return ossl_blake##variant##_init(&mdctx->ctx, &mdctx->params); \ +} \ + \ +static OSSL_FUNC_digest_init_fn blake##variantsize##_internal_init; \ +static OSSL_FUNC_digest_newctx_fn blake##variantsize##_newctx; \ +static OSSL_FUNC_digest_freectx_fn blake##variantsize##_freectx; \ +static OSSL_FUNC_digest_dupctx_fn blake##variantsize##_dupctx; \ +static OSSL_FUNC_digest_final_fn blake##variantsize##_internal_final; \ +static OSSL_FUNC_digest_get_params_fn blake##variantsize##_get_params; \ + \ +static int blake##variantsize##_internal_init(void *ctx, const OSSL_PARAM params[]) \ +{ \ + return ossl_prov_is_running() && ossl_blake##variant##_set_ctx_params(ctx, params) \ + && ossl_blake##variantsize##_init(ctx); \ +} \ + \ +static void *blake##variantsize##_newctx(void *prov_ctx) \ +{ \ + struct blake##variant##_md_data_st *ctx; \ + \ + ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) : NULL; \ + return ctx; \ +} \ + \ +static void blake##variantsize##_freectx(void *vctx) \ +{ \ + struct blake##variant##_md_data_st *ctx; \ + \ + ctx = (struct blake##variant##_md_data_st *)vctx; \ + OPENSSL_clear_free(ctx, sizeof(*ctx)); \ +} \ + \ +static void *blake##variantsize##_dupctx(void *ctx) \ +{ \ + struct blake##variant##_md_data_st *in, *ret; \ + \ + in = (struct blake##variant##_md_data_st *)ctx; \ + ret = ossl_prov_is_running()? OPENSSL_malloc(sizeof(*ret)) : NULL; \ + if (ret != NULL) \ + *ret = *in; \ + return ret; \ +} \ + \ +static int blake##variantsize##_internal_final(void *ctx, unsigned char *out, \ + size_t *outl, size_t outsz) \ +{ \ + struct blake##variant##_md_data_st *b_ctx; \ + \ + b_ctx = (struct blake##variant##_md_data_st *)ctx; \ + \ + if (!ossl_prov_is_running()) \ + return 0; \ + \ + *outl = b_ctx->ctx.outlen; \ + \ + if (outsz == 0) \ + return 1; \ + \ + if (outsz < *outl) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); \ + return 0; \ + } \ + \ + return ossl_blake##variant##_final(out, ctx); \ +} \ + \ +static int blake##variantsize##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_digest_default_get_params(params, BLAKE##VARIANT##_BLOCKBYTES, 64, 0); \ +} \ + \ +const OSSL_DISPATCH ossl_blake##variantsize##_functions[] = { \ + {OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))blake##variantsize##_newctx}, \ + {OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))ossl_blake##variant##_update}, \ + {OSSL_FUNC_DIGEST_FINAL, (void (*)(void))blake##variantsize##_internal_final}, \ + {OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))blake##variantsize##_freectx}, \ + {OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))blake##variantsize##_dupctx}, \ + {OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))blake##variantsize##_get_params}, \ + {OSSL_FUNC_DIGEST_GETTABLE_PARAMS, \ + (void (*)(void))ossl_digest_default_gettable_params}, \ + {OSSL_FUNC_DIGEST_INIT, (void (*)(void))blake##variantsize##_internal_init}, \ + {OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_gettable_ctx_params}, \ + {OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_settable_ctx_params}, \ + {OSSL_FUNC_DIGEST_GET_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_get_ctx_params}, \ + {OSSL_FUNC_DIGEST_SET_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_set_ctx_params}, \ + {0, NULL} \ }; + +IMPLEMENT_BLAKE_functions(2b, 2B, 2b512) diff --git a/providers/implementations/digests/blake2b_prov.c b/providers/implementations/digests/blake2b_prov.c index ee61de8a72..970549ed0c 100644 --- a/providers/implementations/digests/blake2b_prov.c +++ b/providers/implementations/digests/blake2b_prov.c @@ -17,81 +17,10 @@ #include #include #include -#include -#include -#include #include "internal/numbers.h" #include "blake2_impl.h" #include "prov/blake2.h" -static const OSSL_PARAM known_blake2b_ctx_params[] = { - {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, - OSSL_PARAM_END -}; - -const OSSL_PARAM *ossl_blake2b_gettable_ctx_params(ossl_unused void *ctx, - ossl_unused void *pctx) -{ - return known_blake2b_ctx_params; -} - -const OSSL_PARAM *ossl_blake2b_settable_ctx_params(ossl_unused void *ctx, - ossl_unused void *pctx) -{ - return known_blake2b_ctx_params; -} - -int ossl_blake2b_get_ctx_params(void *vctx, OSSL_PARAM params[]) -{ - struct blake2b_md_data_st *mdctx = vctx; - OSSL_PARAM *p; - - BLAKE2B_CTX *ctx = &mdctx->ctx; - - if (ctx == NULL) - return 0; - if (params == NULL) - return 1; - - p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE); - if (p != NULL - && !OSSL_PARAM_set_uint(p, (unsigned int)mdctx->params.digest_length)) { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); - return 0; - } - - return 1; -} - -int ossl_blake2b_set_ctx_params(void *vctx, const OSSL_PARAM params[]) -{ - size_t size; - struct blake2b_md_data_st *mdctx = vctx; - const OSSL_PARAM *p; - - BLAKE2B_CTX *ctx = &mdctx->ctx; - - if (ctx == NULL) - return 0; - if (params == NULL) - return 1; - - p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SIZE); - if (p != NULL) { - if (!OSSL_PARAM_get_size_t(p, &size)) { - ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); - return 0; - } - if (size < 1 || size > UINT8_MAX) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); - return 0; - } - ossl_blake2b_param_set_digest_length(&mdctx->params, (uint8_t)size); - } - - return 1; -} - static const uint64_t blake2b_IV[8] = { 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, -- Gitee From 39b10f3847db45764c9f199578fbe2bb1e4ab610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Thu, 19 Oct 2023 18:31:33 +0200 Subject: [PATCH 29/54] Implement BLAKE2s with the same macro as BLAKE2b This avoids code duplication and provides variable-size support for BLAKE2s like 786b9a8 Test data obtained with libb2 with the following programs: ==> b2.c <== #include #include int main() { char buf[16] = {}; blake2s(buf, 0, 0, 16, 0, 0); write(1, buf, 16); } ==> b3.c <== #include #include int main() { char buf[10] = {}; blake2s(buf, "\x61", 0, 10, 1, 0); write(1, buf, 10); } Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22444) Signed-off-by: fly2x --- providers/implementations/digests/blake2_prov.c | 17 +---------------- providers/implementations/include/prov/blake2.h | 10 ++++++++++ test/recipes/30-test_evp_data/evpmd_blake.txt | 10 ++++++++++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/providers/implementations/digests/blake2_prov.c b/providers/implementations/digests/blake2_prov.c index f80d307321..4178d0554d 100644 --- a/providers/implementations/digests/blake2_prov.c +++ b/providers/implementations/digests/blake2_prov.c @@ -15,22 +15,6 @@ #include "prov/digestcommon.h" #include "prov/implementations.h" -static int ossl_blake2s256_init(void *ctx) -{ - BLAKE2S_PARAM P; - - ossl_blake2s_param_init(&P); - return ossl_blake2s_init((BLAKE2S_CTX *)ctx, &P); -} - -/* ossl_blake2s256_functions */ -IMPLEMENT_digest_functions(blake2s256, BLAKE2S_CTX, - BLAKE2S_BLOCKBYTES, BLAKE2S_DIGEST_LENGTH, 0, - ossl_blake2s256_init, ossl_blake2s_update, - ossl_blake2s_final) - -/* ossl_blake2b512_functions */ - #define IMPLEMENT_BLAKE_functions(variant, VARIANT, variantsize) \ static const OSSL_PARAM known_blake##variant##_ctx_params[] = { \ {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, \ @@ -200,4 +184,5 @@ const OSSL_DISPATCH ossl_blake##variantsize##_functions[] = { \ {0, NULL} \ }; +IMPLEMENT_BLAKE_functions(2s, 2S, 2s256) IMPLEMENT_BLAKE_functions(2b, 2B, 2b512) diff --git a/providers/implementations/include/prov/blake2.h b/providers/implementations/include/prov/blake2.h index 445fd89aa2..42229e2d74 100644 --- a/providers/implementations/include/prov/blake2.h +++ b/providers/implementations/include/prov/blake2.h @@ -88,6 +88,11 @@ struct blake2b_md_data_st { BLAKE2B_PARAM params; }; +struct blake2s_md_data_st { + BLAKE2S_CTX ctx; + BLAKE2S_PARAM params; +}; + int ossl_blake2b_init(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P); int ossl_blake2b_init_key(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P, const void *key); @@ -125,4 +130,9 @@ void ossl_blake2s_param_set_personal(BLAKE2S_PARAM *P, const uint8_t *personal, void ossl_blake2s_param_set_salt(BLAKE2S_PARAM *P, const uint8_t *salt, size_t length); +OSSL_FUNC_digest_get_ctx_params_fn ossl_blake2s_get_ctx_params; +OSSL_FUNC_digest_set_ctx_params_fn ossl_blake2s_set_ctx_params; +OSSL_FUNC_digest_gettable_ctx_params_fn ossl_blake2s_gettable_ctx_params; +OSSL_FUNC_digest_settable_ctx_params_fn ossl_blake2s_settable_ctx_params; + #endif /* OSSL_PROV_BLAKE2_H */ diff --git a/test/recipes/30-test_evp_data/evpmd_blake.txt b/test/recipes/30-test_evp_data/evpmd_blake.txt index 474e659142..949de2f783 100644 --- a/test/recipes/30-test_evp_data/evpmd_blake.txt +++ b/test/recipes/30-test_evp_data/evpmd_blake.txt @@ -54,6 +54,16 @@ Digest = BLAKE2s256 Input = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081 Output = C80ABEEBB669AD5DEEB5F5EC8EA6B7A05DDF7D31EC4C0A2EE20B0B98CAEC6746 +Digest = BLAKE2s256 +Input = +OutputSize = 16 +Output = 64550d6ffe2c0a01a14aba1eade0200c + +Digest = BLAKE2s256 +Input = 61 +OutputSize = 10 +Output = b60d322755eebca92b5e + Digest = BLAKE2b512 Input = Output = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce -- Gitee From cf9888eb80a2f15ffef6d2702f724fbb4c8c1392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Fri, 20 Oct 2023 00:01:28 +0200 Subject: [PATCH 30/54] Document BLAKE2's variable size Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22444) Signed-off-by: fly2x --- doc/man3/EVP_blake2b512.pod | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/man3/EVP_blake2b512.pod b/doc/man3/EVP_blake2b512.pod index 55bd9f3bce..7bf08f6495 100644 --- a/doc/man3/EVP_blake2b512.pod +++ b/doc/man3/EVP_blake2b512.pod @@ -38,9 +38,8 @@ calling these functions multiple times and should consider using L with L instead. See L for further information. -While the BLAKE2b and BLAKE2s algorithms supports a variable length digest, -this implementation outputs a digest of a fixed length (the maximum length -supported), which is 512-bits for BLAKE2b and 256-bits for BLAKE2s. +Both algorithms support a variable-length digest, +but this is only available through L. =head1 RETURN VALUES -- Gitee From 40dc5ffffbabe51695cad2b209909a63aa4f9e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Tue, 31 Oct 2023 20:36:31 +0100 Subject: [PATCH 31/54] CHANGES.md: note BLAKE2s supports BLAKE2b-like "size" setting Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22444) Signed-off-by: fly2x --- CHANGES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 9ce5ceb8a3..029589c7d3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -26,7 +26,10 @@ OpenSSL 3.3 ### Changes between 3.2 and 3.3 [xx XXX xxxx] - * none yet + * The BLAKE2s hash algorithm matches BLAKE2b's support + for configurable output length. + + *Ahelenia Ziemiańska* OpenSSL 3.2 ----------- -- Gitee From e1ba19ad0c905e9db8b460a88112597301ce5bff Mon Sep 17 00:00:00 2001 From: "Matthias St. Pierre" Date: Wed, 25 Oct 2023 11:05:58 +0200 Subject: [PATCH 32/54] apps/rehash.c: avoid printf format warning [-Wformat] The `aarch64-linux-android33-clang` cross-compiler (v14.0.6) complains twice about an unsupported '%n' format specifier, preventing a successful `--strict-warnings` build: error: '%n' specifier not supported on this platform [-Werror,-Wformat] BIO_snprintf(buf, buflen, "%s%s%n%08x.%s%d", This is a false positive, because BIO_snprintf() implements its own format parsing (which is implemented in the _dopr() function). This commit fixes the problem by rewriting the code to dispense with the dubious '%n' format specifier. As a side-effect, the code becomes a little bit more comprehensible and self-explaining. Reviewed-by: David von Oheimb Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22511) Signed-off-by: fly2x --- apps/rehash.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/rehash.c b/apps/rehash.c index dbd1389acd..1e5cf38a7f 100644 --- a/apps/rehash.c +++ b/apps/rehash.c @@ -355,9 +355,9 @@ static int do_dir(const char *dirname, enum Hash h) OPENSSL_DIR_CTX *d = NULL; struct stat st; unsigned char idmask[MAX_COLLISIONS / 8]; - int n, numfiles, nextid, buflen, errs = 0; + int n, numfiles, nextid, dirlen, buflen, errs = 0; size_t i; - const char *pathsep; + const char *pathsep = ""; const char *filename; char *buf, *copy = NULL; STACK_OF(OPENSSL_STRING) *files = NULL; @@ -366,9 +366,12 @@ static int do_dir(const char *dirname, enum Hash h) BIO_printf(bio_err, "Skipping %s, can't write\n", dirname); return 1; } - buflen = strlen(dirname); - pathsep = (buflen && !ends_with_dirsep(dirname)) ? "/": ""; - buflen += NAME_MAX + 1 + 1; + dirlen = strlen(dirname); + if (dirlen != 0 && !ends_with_dirsep(dirname)) { + pathsep = "/"; + dirlen++; + } + buflen = dirlen + NAME_MAX + 1; buf = app_malloc(buflen, "filename buffer"); if (verbose) @@ -427,12 +430,12 @@ static int do_dir(const char *dirname, enum Hash h) while (bit_isset(idmask, nextid)) nextid++; - BIO_snprintf(buf, buflen, "%s%s%n%08x.%s%d", - dirname, pathsep, &n, bp->hash, + BIO_snprintf(buf, buflen, "%s%s%08x.%s%d", + dirname, pathsep, bp->hash, suffixes[bp->type], nextid); if (verbose) BIO_printf(bio_out, "link %s -> %s\n", - ep->filename, &buf[n]); + ep->filename, &buf[dirlen]); if (unlink(buf) < 0 && errno != ENOENT) { BIO_printf(bio_err, "%s: Can't unlink %s, %s\n", @@ -449,12 +452,12 @@ static int do_dir(const char *dirname, enum Hash h) bit_set(idmask, nextid); } else if (remove_links) { /* Link to be deleted */ - BIO_snprintf(buf, buflen, "%s%s%n%08x.%s%d", - dirname, pathsep, &n, bp->hash, + BIO_snprintf(buf, buflen, "%s%s%08x.%s%d", + dirname, pathsep, bp->hash, suffixes[bp->type], ep->old_id); if (verbose) BIO_printf(bio_out, "unlink %s\n", - &buf[n]); + &buf[dirlen]); if (unlink(buf) < 0 && errno != ENOENT) { BIO_printf(bio_err, "%s: Can't unlink %s, %s\n", -- Gitee From 2923fbd053ad0eb7ff171fb35d0ac435b0d31215 Mon Sep 17 00:00:00 2001 From: Pauli Date: Tue, 7 Nov 2023 07:32:18 +1100 Subject: [PATCH 33/54] Fix bug in priority queue remove function The short circuit in the remove function when the element is the last in the heap, failed to add the removed slot back to the freelist. Fixes #22644 Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22646) Signed-off-by: fly2x --- ssl/priority_queue.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ssl/priority_queue.c b/ssl/priority_queue.c index ab2442aeae..5393c532a7 100644 --- a/ssl/priority_queue.c +++ b/ssl/priority_queue.c @@ -265,8 +265,14 @@ void *ossl_pqueue_remove(OSSL_PQUEUE *pq, size_t elem) ASSERT_USED(pq, n); - if (n == pq->htop - 1) + if (n == pq->htop - 1) { + pq->elements[elem].posn = pq->freelist; + pq->freelist = elem; +#ifndef NDEBUG + pq->elements[elem].used = 0; +#endif return pq->heap[--pq->htop].data; + } if (n > 0) pqueue_force_bottom(pq, n); return ossl_pqueue_pop(pq); -- Gitee From 7e22f1310f1115c013c0d151afef4106022121ca Mon Sep 17 00:00:00 2001 From: Pauli Date: Tue, 7 Nov 2023 07:44:49 +1100 Subject: [PATCH 34/54] Add unit test for #22644 Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22646) Signed-off-by: fly2x --- test/priority_queue_test.c | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/test/priority_queue_test.c b/test/priority_queue_test.c index 7ff618a398..d8cafc943c 100644 --- a/test/priority_queue_test.c +++ b/test/priority_queue_test.c @@ -157,6 +157,94 @@ static int test_large_priority_queue(void) 1, 1); } +typedef struct info_st { + uint64_t seq_num, sub_seq; + size_t idx; +} INFO; + +DEFINE_PRIORITY_QUEUE_OF(INFO); + +static int cmp(const INFO *a, const INFO *b) +{ + if (a->seq_num < b->seq_num) + return -1; + if (a->seq_num > b->seq_num) + return 1; + if (a->sub_seq < b->sub_seq) + return -1; + if (a->sub_seq > b->sub_seq) + return 1; + return 0; +} + +static int test_22644(void) +{ + size_t i; + INFO infos[32]; + int res = 0; + PRIORITY_QUEUE_OF(INFO) *pq = ossl_pqueue_INFO_new(cmp); + + memset(infos, 0, sizeof(infos)); + for (i = 0; i < 32; ++i) + infos[i].sub_seq = i; + + infos[0].seq_num = 70650219160667140; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[0], &infos[0].idx)) + || !TEST_size_t_eq(infos[0].idx, 7) + || !TEST_ptr(ossl_pqueue_INFO_remove(pq, infos[0].idx))) + goto err; + + infos[1].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[1], &infos[1].idx)) + || !TEST_size_t_eq(infos[1].idx, 7) + || !TEST_ptr(ossl_pqueue_INFO_remove(pq, infos[1].idx))) + goto err; + + infos[2].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[2], &infos[2].idx)) + || !TEST_size_t_eq(infos[2].idx, 7)) + goto err; + + infos[3].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[3], &infos[3].idx)) + || !TEST_size_t_eq(infos[3].idx, 6)) + goto err; + + infos[4].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[4], &infos[4].idx)) + || !TEST_size_t_eq(infos[4].idx, 5)) + goto err; + + infos[5].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[5], &infos[5].idx)) + || !TEST_size_t_eq(infos[5].idx, 4)) + goto err; + + infos[6].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[6], &infos[6].idx)) + || !TEST_size_t_eq(infos[6].idx, 3)) + goto err; + + infos[7].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[7], &infos[7].idx)) + || !TEST_size_t_eq(infos[7].idx, 2)) + goto err; + + infos[8].seq_num = 289360691352306692; + if (!TEST_true(ossl_pqueue_INFO_push(pq, &infos[8], &infos[8].idx)) + || !TEST_size_t_eq(infos[8].idx, 1)) + goto err; + + if (!TEST_ptr(ossl_pqueue_INFO_pop(pq)) + || !TEST_ptr(ossl_pqueue_INFO_pop(pq))) /* crash if bug present */ + goto err; + res = 1; + + err: + ossl_pqueue_INFO_free(pq); + return res; +} + int setup_tests(void) { ADD_ALL_TESTS(test_size_t_priority_queue, @@ -167,5 +255,6 @@ int setup_tests(void) * 6 /* remove */ * 2); /* pop & free */ ADD_TEST(test_large_priority_queue); + ADD_TEST(test_22644); return 1; } -- Gitee From aef23dc103aec581b5a9c9d6cb754c21ac9753b0 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 3 Nov 2023 12:12:38 -0400 Subject: [PATCH 35/54] Correct order of ossl_condvar_signal in quic_multistream_test quic_multistream test was issuing a signal on a condvar after dropping the corresponding mutex, not before, leading to potential race conditions in the reading of the associated data Fixes #22588 Reviewed-by: Hugo Landau Reviewed-by: Paul Dale Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22616) Signed-off-by: fly2x --- test/quic_multistream_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c index 8d513d7f08..22a753ad67 100644 --- a/test/quic_multistream_test.c +++ b/test/quic_multistream_test.c @@ -497,8 +497,8 @@ static int join_server_thread(struct helper *h) ossl_crypto_mutex_lock(h->server_thread.m); h->server_thread.stop = 1; - ossl_crypto_mutex_unlock(h->server_thread.m); ossl_crypto_condvar_signal(h->server_thread.c); + ossl_crypto_mutex_unlock(h->server_thread.m); ossl_crypto_thread_native_join(h->server_thread.t, &rv); ossl_crypto_thread_native_clean(h->server_thread.t); @@ -1079,8 +1079,8 @@ static int run_script_worker(struct helper *h, const struct script_op *script, else if (h->blocking && !h->server_thread.ready) { ossl_crypto_mutex_lock(h->server_thread.m); h->server_thread.ready = 1; - ossl_crypto_mutex_unlock(h->server_thread.m); ossl_crypto_condvar_signal(h->server_thread.c); + ossl_crypto_mutex_unlock(h->server_thread.m); } if (h->blocking) assert(h->s == NULL); @@ -2658,8 +2658,8 @@ static int script_20_trigger(struct helper *h, volatile uint64_t *counter) #if defined(OPENSSL_THREADS) ossl_crypto_mutex_lock(h->misc_m); ++*counter; - ossl_crypto_mutex_unlock(h->misc_m); ossl_crypto_condvar_broadcast(h->misc_cv); + ossl_crypto_mutex_unlock(h->misc_m); #endif return 1; } -- Gitee From cf54d7dc03f296e72759a75ecfdeae49168733f2 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 3 Nov 2023 12:56:40 -0400 Subject: [PATCH 36/54] add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau Reviewed-by: Paul Dale Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22616) Signed-off-by: fly2x --- test/helpers/quictestlib.c | 45 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/test/helpers/quictestlib.c b/test/helpers/quictestlib.c index 2c74b26252..0348729b72 100644 --- a/test/helpers/quictestlib.c +++ b/test/helpers/quictestlib.c @@ -74,13 +74,16 @@ struct qtest_fault { static void packet_plain_finish(void *arg); static void handshake_finish(void *arg); +static OSSL_TIME qtest_get_time(void); +static void qtest_reset_time(void); static int using_fake_time = 0; static OSSL_TIME fake_now; +static CRYPTO_RWLOCK *fake_now_lock = NULL; static OSSL_TIME fake_now_cb(void *arg) { - return fake_now; + return qtest_get_time(); } static void noise_msg_callback(int write_p, int version, int content_type, @@ -282,11 +285,14 @@ int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx, if (serverctx != NULL && !TEST_true(SSL_CTX_up_ref(serverctx))) goto err; tserver_args.ctx = serverctx; + if (fake_now_lock == NULL) { + fake_now_lock = CRYPTO_THREAD_lock_new(); + if (fake_now_lock == NULL) + goto err; + } if ((flags & QTEST_FLAG_FAKE_TIME) != 0) { using_fake_time = 1; - fake_now = ossl_time_zero(); - /* zero time can have a special meaning, bump it */ - qtest_add_time(1); + qtest_reset_time(); tserver_args.now_cb = fake_now_cb; (void)ossl_quic_conn_set_override_now_cb(*cssl, fake_now_cb, NULL); } else { @@ -331,7 +337,31 @@ int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx, void qtest_add_time(uint64_t millis) { + if (!CRYPTO_THREAD_write_lock(fake_now_lock)) + return; fake_now = ossl_time_add(fake_now, ossl_ms2time(millis)); + CRYPTO_THREAD_unlock(fake_now_lock); +} + +static OSSL_TIME qtest_get_time(void) +{ + OSSL_TIME ret; + + if (!CRYPTO_THREAD_read_lock(fake_now_lock)) + return ossl_time_zero(); + ret = fake_now; + CRYPTO_THREAD_unlock(fake_now_lock); + return ret; +} + +static void qtest_reset_time(void) +{ + if (!CRYPTO_THREAD_write_lock(fake_now_lock)) + return; + fake_now = ossl_time_zero(); + CRYPTO_THREAD_unlock(fake_now_lock); + /* zero time can have a special meaning, bump it */ + qtest_add_time(1); } QTEST_FAULT *qtest_create_injector(QUIC_TSERVER *ts) @@ -399,17 +429,20 @@ int qtest_wait_for_timeout(SSL *s, QUIC_TSERVER *qtserv) */ if (!SSL_get_event_timeout(s, &tv, &cinf)) return 0; + if (using_fake_time) - now = fake_now; + now = qtest_get_time(); else now = ossl_time_now(); + ctimeout = cinf ? ossl_time_infinite() : ossl_time_from_timeval(tv); stimeout = ossl_time_subtract(ossl_quic_tserver_get_deadline(qtserv), now); mintimeout = ossl_time_min(ctimeout, stimeout); if (ossl_time_is_infinite(mintimeout)) return 0; + if (using_fake_time) - fake_now = ossl_time_add(now, mintimeout); + qtest_add_time(ossl_time2ms(mintimeout)); else OSSL_sleep(ossl_time2ms(mintimeout)); -- Gitee From 7b362be77b4b90d1e99e5b4eb2c0cca84afa3926 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 14:53:10 +0000 Subject: [PATCH 37/54] QUIC TXP: Fix use of implicit-length STREAM frames in presence of PATH_REPSONSE frames Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22615) Signed-off-by: fly2x --- ssl/quic/quic_txp.c | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index 1f93638bf6..f607d6964b 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -846,8 +846,17 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp, && total_dgram_size < min_dpl) { size_t deficit = min_dpl - total_dgram_size; + if (!ossl_assert(!pkt[first_el].h.done_implicit)) + goto out; + if (!txp_pkt_append_padding(&pkt[first_el], txp, deficit)) goto out; + + /* + * Padding frames make a packet ineligible for being a non-inflight + * packet. + */ + pkt[first_el].tpkt->ackm_pkt.is_inflight = 1; } } @@ -2137,7 +2146,6 @@ static int txp_generate_stream_frames(OSSL_QUIC_TX_PACKETISER *txp, QUIC_SSTREAM *sstream, QUIC_TXFC *stream_txfc, QUIC_STREAM *next_stream, - size_t min_ppl, int *have_ack_eliciting, int *packet_full, uint64_t *new_credit_consumed) @@ -2151,7 +2159,7 @@ static int txp_generate_stream_frames(OSSL_QUIC_TX_PACKETISER *txp, WPACKET *wpkt; QUIC_TXPIM_CHUNK chunk; size_t i, j, space_left; - int needs_padding_if_implicit, can_fill_payload, use_explicit_len; + int can_fill_payload, use_explicit_len; int could_have_following_chunk; uint64_t orig_len; uint64_t hdr_len_implicit, payload_len_implicit; @@ -2222,13 +2230,6 @@ static int txp_generate_stream_frames(OSSL_QUIC_TX_PACKETISER *txp, goto err; /* can't fit anything */ } - /* - * If using the implicit-length representation would need padding, we - * can't use it. - */ - needs_padding_if_implicit = (h->bytes_appended + hdr_len_implicit - + payload_len_implicit < min_ppl); - /* * If there is a next stream, we don't use the implicit length so we can * add more STREAM frames after this one, unless there is enough data @@ -2246,7 +2247,7 @@ static int txp_generate_stream_frames(OSSL_QUIC_TX_PACKETISER *txp, /* Choose between explicit or implicit length representations. */ use_explicit_len = !((can_fill_payload || !could_have_following_chunk) - && !needs_padding_if_implicit); + && !pkt->force_pad); if (use_explicit_len) { /* @@ -2354,7 +2355,6 @@ static void txp_enlink_tmp(QUIC_STREAM **tmp_head, QUIC_STREAM *stream) static int txp_generate_stream_related(OSSL_QUIC_TX_PACKETISER *txp, struct txp_pkt *pkt, - size_t min_ppl, int *have_ack_eliciting, QUIC_STREAM **tmp_head) { @@ -2495,7 +2495,7 @@ static int txp_generate_stream_related(OSSL_QUIC_TX_PACKETISER *txp, if (!txp_generate_stream_frames(txp, pkt, stream->id, stream->sstream, &stream->txfc, - snext, min_ppl, + snext, have_ack_eliciting, &packet_full, &stream->txp_txfc_new_credit_consumed)) { @@ -2537,7 +2537,6 @@ static int txp_generate_for_el(OSSL_QUIC_TX_PACKETISER *txp, QUIC_CFQ_ITEM *cfq_item; QUIC_TXPIM_PKT *tpkt = NULL; struct tx_helper *h = &pkt->h; - size_t min_ppl = 0; /* Maximum PN reached? */ if (!ossl_quic_pn_valid(txp->next_pn[pn_space])) @@ -2740,7 +2739,7 @@ static int txp_generate_for_el(OSSL_QUIC_TX_PACKETISER *txp, /* Stream-specific frames */ if (a.allow_stream_rel && txp->handshake_complete) - if (!txp_generate_stream_related(txp, pkt, min_ppl, + if (!txp_generate_stream_related(txp, pkt, &have_ack_eliciting, &pkt->stream_head)) goto fatal_err; @@ -2768,18 +2767,7 @@ static int txp_generate_for_el(OSSL_QUIC_TX_PACKETISER *txp, have_ack_eliciting = 1; } - /* PADDING */ - if (a.allow_padding && h->bytes_appended < min_ppl) { - WPACKET *wpkt = tx_helper_begin(h); - if (wpkt == NULL) - goto fatal_err; - - if (!ossl_quic_wire_encode_padding(wpkt, min_ppl - h->bytes_appended) - || !tx_helper_commit(h)) - goto fatal_err; - - can_be_non_inflight = 0; - } + /* PADDING is added by ossl_quic_tx_packetiser_generate(). */ /* * ACKM Data -- Gitee From b5d208fb33b3482c9c059b02affaaa60c1fcc539 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 15:13:51 +0000 Subject: [PATCH 38/54] QUIC TXP: Handle padding correctly for ACK_ONLY archetype Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22615) Signed-off-by: fly2x --- ssl/quic/quic_txp.c | 46 +++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index f607d6964b..e13501f1e9 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -828,35 +828,51 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp, if (need_padding) { size_t total_dgram_size = 0; const size_t min_dpl = QUIC_MIN_INITIAL_DGRAM_LEN; - uint32_t first_el = QUIC_ENC_LEVEL_NUM; + uint32_t pad_el = QUIC_ENC_LEVEL_NUM; for (enc_level = QUIC_ENC_LEVEL_INITIAL; enc_level < QUIC_ENC_LEVEL_NUM; ++enc_level) if (pkt[enc_level].h_valid && pkt[enc_level].h.bytes_appended > 0) { - if (first_el == QUIC_ENC_LEVEL_NUM) - first_el = enc_level; + if (pad_el == QUIC_ENC_LEVEL_NUM + /* + * We might not be able to add padding, for example if we + * are using the ACK_ONLY archetype. + */ + && pkt[enc_level].geom.adata.allow_padding + && !pkt[enc_level].h.done_implicit) + pad_el = enc_level; txp_pkt_postgen_update_pkt_overhead(&pkt[enc_level], txp); total_dgram_size += pkt[enc_level].geom.pkt_overhead + pkt[enc_level].h.bytes_appended; } - if (first_el != QUIC_ENC_LEVEL_NUM - && total_dgram_size < min_dpl) { + if (pad_el != QUIC_ENC_LEVEL_NUM && total_dgram_size < min_dpl) { size_t deficit = min_dpl - total_dgram_size; - if (!ossl_assert(!pkt[first_el].h.done_implicit)) + if (!txp_pkt_append_padding(&pkt[pad_el], txp, deficit)) goto out; - if (!txp_pkt_append_padding(&pkt[first_el], txp, deficit)) - goto out; + total_dgram_size += deficit; /* * Padding frames make a packet ineligible for being a non-inflight * packet. */ - pkt[first_el].tpkt->ackm_pkt.is_inflight = 1; + pkt[pad_el].tpkt->ackm_pkt.is_inflight = 1; + } + + /* + * If we have failed to make a datagram of adequate size, for example + * because we have a padding requirement but are using the ACK_ONLY + * archetype (because we are CC limited), which precludes us from + * sending padding, give up on generating the datagram - there is + * nothing we can do. + */ + if (total_dgram_size < min_dpl) { + res = 1; + goto out; } } @@ -875,11 +891,17 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp, rc = txp_pkt_commit(txp, &pkt[enc_level], archetype, &txpim_pkt_reffed); - if (rc) + if (rc) { status->sent_ack_eliciting = status->sent_ack_eliciting || pkt[enc_level].tpkt->ackm_pkt.is_ack_eliciting; + if (enc_level == QUIC_ENC_LEVEL_HANDSHAKE) + status->sent_handshake + = (pkt[enc_level].h_valid + && pkt[enc_level].h.bytes_appended > 0); + } + if (txpim_pkt_reffed) pkt[enc_level].tpkt = NULL; /* don't free */ @@ -889,10 +911,6 @@ int ossl_quic_tx_packetiser_generate(OSSL_QUIC_TX_PACKETISER *txp, ++pkts_done; } - status->sent_handshake - = (pkt[QUIC_ENC_LEVEL_HANDSHAKE].h_valid - && pkt[QUIC_ENC_LEVEL_HANDSHAKE].h.bytes_appended > 0); - /* Flush & Cleanup */ res = 1; out: -- Gitee From baf31c961471275464a1840369553f57f76fb57d Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 7 Nov 2023 15:14:34 +0100 Subject: [PATCH 39/54] Sync CHANGES.md and NEWS.md with 3.1 branch Reviewed-by: Richard Levitte Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22647) (cherry picked from commit 2d0d3edb04ab0fa53e30e3cbdd114de9933d5361) Signed-off-by: fly2x --- CHANGES.md | 8 ++++++-- NEWS.md | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 029589c7d3..98ac35500f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -486,9 +486,13 @@ OpenSSL 3.2 OpenSSL 3.1 ----------- -### Changes between 3.1.3 and 3.1.4 [xx XXX xxxx] +### Changes between 3.1.4 and 3.1.5 [xx XXX xxxx] -* Fix incorrect key and IV resizing issues when calling EVP_EncryptInit_ex2(), + * none yet + +### Changes between 3.1.3 and 3.1.4 [24 Oct 2023] + + * Fix incorrect key and IV resizing issues when calling EVP_EncryptInit_ex2(), EVP_DecryptInit_ex2() or EVP_CipherInit_ex2() with OSSL_PARAM parameters that alter the key or IV length ([CVE-2023-5363]). diff --git a/NEWS.md b/NEWS.md index b19d8e3d2c..5808f805a0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -60,7 +60,11 @@ OpenSSL 3.2 OpenSSL 3.1 ----------- -### Major changes between OpenSSL 3.1.3 and OpenSSL 3.1.4 [under development] +### Major changes between OpenSSL 3.1.4 and OpenSSL 3.1.5 [under development] + + * none + +### Major changes between OpenSSL 3.1.3 and OpenSSL 3.1.4 [24 Oct 2023] * Mitigate incorrect resize handling for symmetric cipher keys and IVs. ([CVE-2023-5363]) -- Gitee From 84b4b63c79857f70112f99b607b34c0b1e2dbcba Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 7 Nov 2023 15:22:00 +0100 Subject: [PATCH 40/54] Add CHANGES.md and NEWS.md entry for CVE-2023-5678 Reviewed-by: Richard Levitte Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22647) (cherry picked from commit 4ee71b4c302a06c24b46a5def1cff2096bd57f0b) Signed-off-by: fly2x --- CHANGES.md | 15 ++++++++++++++- NEWS.md | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 98ac35500f..fd7f37fb03 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -488,7 +488,19 @@ OpenSSL 3.1 ### Changes between 3.1.4 and 3.1.5 [xx XXX xxxx] - * none yet + * Fix excessive time spent in DH check / generation with large Q parameter + value. + + Applications that use the functions DH_generate_key() to generate an + X9.42 DH key may experience long delays. Likewise, applications that use + DH_check_pub_key(), DH_check_pub_key_ex() or EVP_PKEY_public_check() + to check an X9.42 DH key or X9.42 DH parameters may experience long delays. + Where the key or parameters that are being checked have been obtained from + an untrusted source this may lead to a Denial of Service. + + ([CVE-2023-5678]) + + *Richard Levitte* ### Changes between 3.1.3 and 3.1.4 [24 Oct 2023] @@ -20316,6 +20328,7 @@ ndif +[CVE-2023-5678]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-5678 [CVE-2023-5363]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-5363 [CVE-2023-4807]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-4807 [CVE-2023-3817]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-3817 diff --git a/NEWS.md b/NEWS.md index 5808f805a0..a654335049 100644 --- a/NEWS.md +++ b/NEWS.md @@ -62,7 +62,8 @@ OpenSSL 3.1 ### Major changes between OpenSSL 3.1.4 and OpenSSL 3.1.5 [under development] - * none + * Fix excessive time spent in DH check / generation with large Q parameter + value ([CVE-2023-5678]) ### Major changes between OpenSSL 3.1.3 and OpenSSL 3.1.4 [24 Oct 2023] @@ -1519,6 +1520,7 @@ OpenSSL 0.9.x +[CVE-2023-5678]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-5678 [CVE-2023-5363]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-5363 [CVE-2023-4807]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-4807 [CVE-2023-3817]: https://www.openssl.org/news/vulnerabilities.html#CVE-2023-3817 -- Gitee From 3adde0831693ec8850fb813dada21dda978ccec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Veronika=20Hanul=C3=ADkov=C3=A1?= Date: Thu, 12 Oct 2023 14:38:06 +0200 Subject: [PATCH 41/54] Add config tests for including provider config files Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22598) Signed-off-by: fly2x --- test/conf_include_test.c | 75 +++++++++++++++++-- test/recipes/90-test_includes.t | 14 +++- .../conf-includes-prov/inc-default.cnf | 5 ++ .../conf-includes-prov/inc-legacy.cnf | 5 ++ .../includes-prov-dir.cnf | 17 +++++ 5 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 test/recipes/90-test_includes_data/conf-includes-prov/inc-default.cnf create mode 100644 test/recipes/90-test_includes_data/conf-includes-prov/inc-legacy.cnf create mode 100644 test/recipes/90-test_includes_data/includes-prov-dir.cnf diff --git a/test/conf_include_test.c b/test/conf_include_test.c index 2481a2380b..facf960360 100644 --- a/test/conf_include_test.c +++ b/test/conf_include_test.c @@ -37,28 +37,32 @@ #endif /* changes path to that of the filename */ -static int change_path(const char *file) +static char *change_path(const char *file) { char *s = OPENSSL_strdup(file); char *p = s; char *last = NULL; int ret = 0; + char *new_config_name = NULL; if (s == NULL) - return -1; + return NULL; while ((p = strpbrk(p, DIRSEP)) != NULL) { last = p++; } if (last == NULL) goto err; - last[DIRSEP_PRESERVE] = 0; + last[DIRSEP_PRESERVE] = 0; TEST_note("changing path to %s", s); + ret = chdir(s); + if (ret == 0) + new_config_name = strdup(last + DIRSEP_PRESERVE + 1); err: OPENSSL_free(s); - return ret; + return new_config_name; } /* @@ -68,6 +72,9 @@ static int change_path(const char *file) static CONF *conf; static BIO *in; static int expect_failure = 0; +static int test_providers = 0; +static OSSL_LIB_CTX *libctx = NULL; +static char *rel_conf_file = NULL; static int test_load_config(void) { @@ -116,6 +123,27 @@ static int test_load_config(void) return 0; } + if (test_providers != 0) { + /* test for `active` directive in configuration file */ + val = 0; + if (!TEST_int_eq(NCONF_get_number(conf, "null_sect", "activate", &val), 1) + || !TEST_int_eq(val, 1)) { + TEST_note("null provider not activated"); + return 0; + } + val = 0; + if (!TEST_int_eq(NCONF_get_number(conf, "default_sect", "activate", &val), 1) + || !TEST_int_eq(val, 1)) { + TEST_note("default provider not activated"); + return 0; + } + val = 0; + if (!TEST_int_eq(NCONF_get_number(conf, "legacy_sect", "activate", &val), 1) + || !TEST_int_eq(val, 1)) { + TEST_note("legacy provider not activated"); + return 0; + } + } return 1; } @@ -174,10 +202,33 @@ static int test_check_overflow(void) return 1; } +static int test_available_providers(void) +{ + libctx = OSSL_LIB_CTX_new(); + if (!TEST_ptr(libctx)) + return 0; + + if (!TEST_ptr(rel_conf_file) || !OSSL_LIB_CTX_load_config(libctx, rel_conf_file)) { + TEST_note("Failed to load config"); + return 0; + } + + if (OSSL_PROVIDER_available(libctx, "default") != 1) { + TEST_note("Default provider is missing"); + return 0; + } + if (OSSL_PROVIDER_available(libctx, "legacy") != 1) { + TEST_note("Legacy provider is missing"); + return 0; + } + return 1; +} + typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_FAIL, + OPT_TEST_PROV, OPT_TEST_ENUM } OPTION_CHOICE; @@ -186,6 +237,8 @@ const OPTIONS *test_get_options(void) static const OPTIONS test_options[] = { OPT_TEST_OPTIONS_WITH_EXTRA_USAGE("conf_file\n"), { "f", OPT_FAIL, '-', "A failure is expected" }, + { "providers", OPT_TEST_PROV, '-', + "Test for activated default and legacy providers"}, { NULL } }; return test_options; @@ -193,7 +246,7 @@ const OPTIONS *test_get_options(void) int setup_tests(void) { - const char *conf_file; + char *conf_file = NULL; OPTION_CHOICE o; if (!TEST_ptr(conf = NCONF_new(NULL))) @@ -204,6 +257,8 @@ int setup_tests(void) case OPT_FAIL: expect_failure = 1; break; + case OPT_TEST_PROV: + test_providers = 1; case OPT_TEST_CASES: break; default: @@ -222,16 +277,24 @@ int setup_tests(void) * For this test we need to chdir as we use relative * path names in the config files. */ - change_path(conf_file); + rel_conf_file = change_path(conf_file); + if (!TEST_ptr(rel_conf_file)) { + TEST_note("Unable to change path"); + return 0; + } ADD_TEST(test_load_config); ADD_TEST(test_check_null_numbers); ADD_TEST(test_check_overflow); + if (test_providers != 0) + ADD_TEST(test_available_providers); + return 1; } void cleanup_tests(void) { + OPENSSL_free(rel_conf_file); BIO_vfree(in); NCONF_free(conf); CONF_modules_unload(1); diff --git a/test/recipes/90-test_includes.t b/test/recipes/90-test_includes.t index b931d4ec4a..5ff61910c5 100644 --- a/test/recipes/90-test_includes.t +++ b/test/recipes/90-test_includes.t @@ -2,8 +2,9 @@ use strict; use warnings; -use OpenSSL::Test qw/:DEFAULT data_file/; +use OpenSSL::Test qw/:DEFAULT bldtop_dir data_file/; use OpenSSL::Test::Utils; +use Cwd qw(abs_path); setup("test_includes"); @@ -13,9 +14,11 @@ plan skip_all => "test_includes doesn't work without posix-io" delete $ENV{OPENSSL_CONF_INCLUDE}; plan tests => # The number of tests being performed - 6 + 7 + ($^O eq "VMS" ? 2 : 0); +$ENV{OPENSSL_MODULES} = abs_path(bldtop_dir("providers")); + ok(run(test(["conf_include_test", data_file("includes.cnf")])), "test directory includes"); ok(run(test(["conf_include_test", data_file("includes-file.cnf")])), "test file includes"); ok(run(test(["conf_include_test", data_file("includes-eq.cnf")])), "test includes with equal character"); @@ -28,3 +31,10 @@ if ($^O eq "VMS") { } ok(run(test(["conf_include_test", "-f", data_file("includes-broken.cnf")])), "test broken includes"); ok(run(test(["conf_include_test", "-f", data_file("incdir.cnf")])), "test includedir"); + +SKIP: { + skip "Skipping legacy test", 1 + if disabled("legacy"); + ok(run(test(["conf_include_test", "-providers", data_file("includes-prov-dir.cnf")])), + "test directory includes with provider configs"); +} diff --git a/test/recipes/90-test_includes_data/conf-includes-prov/inc-default.cnf b/test/recipes/90-test_includes_data/conf-includes-prov/inc-default.cnf new file mode 100644 index 0000000000..dc272ed292 --- /dev/null +++ b/test/recipes/90-test_includes_data/conf-includes-prov/inc-default.cnf @@ -0,0 +1,5 @@ +[provider_sect] +default = default_sect + +[default_sect] +activate = 1 diff --git a/test/recipes/90-test_includes_data/conf-includes-prov/inc-legacy.cnf b/test/recipes/90-test_includes_data/conf-includes-prov/inc-legacy.cnf new file mode 100644 index 0000000000..1987d66bce --- /dev/null +++ b/test/recipes/90-test_includes_data/conf-includes-prov/inc-legacy.cnf @@ -0,0 +1,5 @@ +[provider_sect] +legacy = legacy_sect + +[legacy_sect] +activate = 1 diff --git a/test/recipes/90-test_includes_data/includes-prov-dir.cnf b/test/recipes/90-test_includes_data/includes-prov-dir.cnf new file mode 100644 index 0000000000..c7ad216083 --- /dev/null +++ b/test/recipes/90-test_includes_data/includes-prov-dir.cnf @@ -0,0 +1,17 @@ +# +# Example configuration file using includes to load providers. +# + +openssl_conf = openssl_init + +[openssl_init] +providers = provider_sect + +[provider_sect] +null = null_sect + +[null_sect] +activate = 1 + +.include conf-includes +.include conf-includes-prov -- Gitee From 2ffd2f02dacb67ca69a64ea2b757e762ed4adae2 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 8 Nov 2023 10:15:25 +0000 Subject: [PATCH 42/54] After a stream has implicit length don't add more stream related frames Once we have decided that a stream has an implicit length then we should treat the packet as full and not try to add any more stream related frames to the packet. Fixes #22658 Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22662) Signed-off-by: fly2x --- ssl/quic/quic_txp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c index e13501f1e9..5500c9b3f6 100644 --- a/ssl/quic/quic_txp.c +++ b/ssl/quic/quic_txp.c @@ -2283,6 +2283,7 @@ static int txp_generate_stream_frames(OSSL_QUIC_TX_PACKETISER *txp, shdr->len = payload_len_explicit; } else { + *packet_full = 1; shdr->has_explicit_len = 0; shdr->len = payload_len_implicit; } -- Gitee From 2dc7e0cf280490ac81040e6b1f4a531bc5cde191 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Mon, 6 Nov 2023 10:44:27 +0100 Subject: [PATCH 43/54] Fix a possible memory leak of ssl->s3.tmp.psk Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/22637) Signed-off-by: fly2x --- ssl/s3_lib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 1f778c3423..e8ec98c221 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -3371,6 +3371,10 @@ void ssl3_free(SSL *s) OPENSSL_free(sc->s3.alpn_selected); OPENSSL_free(sc->s3.alpn_proposed); +#ifndef OPENSSL_NO_PSK + OPENSSL_free(sc->s3.tmp.psk); +#endif + #ifndef OPENSSL_NO_SRP ssl_srp_ctx_free_intern(sc); #endif -- Gitee From 604970e53d6c2698ea947ee46da365824b91b40c Mon Sep 17 00:00:00 2001 From: Alexey Fofanov Date: Wed, 18 Oct 2023 14:23:22 +0300 Subject: [PATCH 44/54] apps/list.c: Check the result of inserting a provider into provider's stack Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22492) (cherry picked from commit 15b83e04a5e125ab873ace1e474790a4a5b44647) Signed-off-by: fly2x --- apps/list.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/list.c b/apps/list.c index 7cbef78719..8649598df7 100644 --- a/apps/list.c +++ b/apps/list.c @@ -1209,9 +1209,11 @@ static int provider_cmp(const OSSL_PROVIDER * const *a, static int collect_providers(OSSL_PROVIDER *provider, void *stack) { STACK_OF(OSSL_PROVIDER) *provider_stack = stack; - - sk_OSSL_PROVIDER_push(provider_stack, provider); - return 1; + /* + * If OK - result is the index of inserted data + * Error - result is -1 or 0 + */ + return sk_OSSL_PROVIDER_push(provider_stack, provider) > 0 ? 1 : 0; } static void list_provider_info(void) @@ -1226,8 +1228,13 @@ static void list_provider_info(void) BIO_printf(bio_err, "ERROR: Memory allocation\n"); return; } + + if (OSSL_PROVIDER_do_all(NULL, &collect_providers, providers) != 1) { + BIO_printf(bio_err, "ERROR: Memory allocation\n"); + return; + } + BIO_printf(bio_out, "Providers:\n"); - OSSL_PROVIDER_do_all(NULL, &collect_providers, providers); sk_OSSL_PROVIDER_sort(providers); for (i = 0; i < sk_OSSL_PROVIDER_num(providers); i++) { const OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(providers, i); -- Gitee From dc2eb8012d6573c61cd48b68280add32c957fe32 Mon Sep 17 00:00:00 2001 From: James Muir Date: Fri, 3 Nov 2023 13:15:04 -0400 Subject: [PATCH 45/54] cms demos: print signingTime attributes Add a makefile for the cms demos, and add a routine to cms_ver.c to print any signingTime attributes from the CMS_ContentInfo object. This provides an example that could be extended if an application wants to examine the purported signing times. Part of #8026 Testing: $ cd demos/cms $ make test Reviewed-by: Shane Lontis Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22618) Signed-off-by: fly2x --- demos/cms/Makefile | 35 +++++++++++++++++++++++++++++++ demos/cms/cms_dec.c | 2 ++ demos/cms/cms_enc.c | 2 ++ demos/cms/cms_sign2.c | 2 ++ demos/cms/cms_ver.c | 48 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 demos/cms/Makefile diff --git a/demos/cms/Makefile b/demos/cms/Makefile new file mode 100644 index 0000000000..7c8f30d632 --- /dev/null +++ b/demos/cms/Makefile @@ -0,0 +1,35 @@ +# +# To run the demos when linked with a shared library (default) ensure that +# libcrypto is on the library path. For example, to run the +# cms_enc demo: +# +# LD_LIBRARY_PATH=../.. ./cms_enc + +TESTS = cms_comp \ + cms_ddec \ + cms_dec \ + cms_denc \ + cms_enc \ + cms_sign \ + cms_sign2 \ + cms_uncomp \ + cms_ver + +CFLAGS = -I../../include -g +LDFLAGS = -L../.. +LDLIBS = -lcrypto + +all: $(TESTS) + +clean: + $(RM) $(TESTS) *.o + +cms_%: cms_%.c + $(CC) $(CFLAGS) $(LDFLAGS) -o "$@" "$<" $(LDLIBS) + +test: all + @echo "\nCMS tests:" + LD_LIBRARY_PATH=../.. ./cms_enc + LD_LIBRARY_PATH=../.. ./cms_dec + LD_LIBRARY_PATH=../.. ./cms_sign2 + LD_LIBRARY_PATH=../.. ./cms_ver diff --git a/demos/cms/cms_dec.c b/demos/cms/cms_dec.c index ebc34a5f94..f64a68ab42 100644 --- a/demos/cms/cms_dec.c +++ b/demos/cms/cms_dec.c @@ -59,6 +59,8 @@ int main(int argc, char **argv) if (!CMS_decrypt(cms, rkey, rcert, NULL, out, 0)) goto err; + printf("Decryption Successful\n"); + ret = EXIT_SUCCESS; err: diff --git a/demos/cms/cms_enc.c b/demos/cms/cms_enc.c index a0af2c4774..1f69571a17 100644 --- a/demos/cms/cms_enc.c +++ b/demos/cms/cms_enc.c @@ -73,6 +73,8 @@ int main(int argc, char **argv) if (!SMIME_write_CMS(out, cms, in, flags)) goto err; + printf("Encryption Successful\n"); + ret = EXIT_SUCCESS; err: if (ret != EXIT_SUCCESS) { diff --git a/demos/cms/cms_sign2.c b/demos/cms/cms_sign2.c index b10043f921..61d9f8bbe8 100644 --- a/demos/cms/cms_sign2.c +++ b/demos/cms/cms_sign2.c @@ -77,6 +77,8 @@ int main(int argc, char **argv) if (!SMIME_write_CMS(out, cms, in, CMS_STREAM)) goto err; + printf("Signing Successful\n"); + ret = EXIT_SUCCESS; err: if (ret != EXIT_SUCCESS) { diff --git a/demos/cms/cms_ver.c b/demos/cms/cms_ver.c index f7d3a9bc85..43e9d09854 100644 --- a/demos/cms/cms_ver.c +++ b/demos/cms/cms_ver.c @@ -12,6 +12,49 @@ #include #include +/* + * print any signingTime attributes. + * signingTime is when each party purportedly signed the message. + */ +static void print_signingTime(CMS_ContentInfo *cms) +{ + STACK_OF(CMS_SignerInfo) *sis; + CMS_SignerInfo *si; + X509_ATTRIBUTE *attr; + ASN1_TYPE *t; + ASN1_UTCTIME *utctime; + ASN1_GENERALIZEDTIME *gtime; + BIO *b; + int i, loc; + + b = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); + sis = CMS_get0_SignerInfos(cms); + for (i = 0; i < sk_CMS_SignerInfo_num(sis); i++) { + si = sk_CMS_SignerInfo_value(sis, i); + loc = CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1); + attr = CMS_signed_get_attr(si, loc); + t = X509_ATTRIBUTE_get0_type(attr, 0); + if (t == NULL) + continue; + switch (t->type) { + case V_ASN1_UTCTIME: + utctime = t->value.utctime; + ASN1_UTCTIME_print(b, utctime); + break; + case V_ASN1_GENERALIZEDTIME: + gtime = t->value.generalizedtime; + ASN1_GENERALIZEDTIME_print(b, gtime); + break; + default: + fprintf(stderr, "unrecognized signingTime type\n"); + break; + } + BIO_printf(b, ": signingTime from SignerInfo %i\n", i); + } + BIO_free(b); + return; +} + int main(int argc, char **argv) { BIO *in = NULL, *out = NULL, *tbio = NULL, *cont = NULL; @@ -56,6 +99,8 @@ int main(int argc, char **argv) if (cms == NULL) goto err; + print_signingTime(cms); + /* File to output verified content to */ out = BIO_new_file("smver.txt", "w"); if (out == NULL) @@ -66,9 +111,10 @@ int main(int argc, char **argv) goto err; } - fprintf(stderr, "Verification Successful\n"); + printf("Verification Successful\n"); ret = EXIT_SUCCESS; + err: if (ret != EXIT_SUCCESS) { fprintf(stderr, "Error Verifying Data\n"); -- Gitee From 89da1f660cb1bb921d34efcae521f9302e3cdf19 Mon Sep 17 00:00:00 2001 From: slontis Date: Fri, 21 Jul 2023 15:05:38 +1000 Subject: [PATCH 46/54] Add EVP_DigestSqueeze() API. Fixes #7894 This allows SHAKE to squeeze multiple times with different output sizes. The existing EVP_DigestFinalXOF() API has been left as a one shot operation. A similar interface is used by another toolkit. The low level SHA3_Squeeze() function needed to change slightly so that it can handle multiple squeezes. This involves changing the assembler code so that it passes a boolean to indicate whether the Keccak function should be called on entry. At the provider level, the squeeze is buffered, so that it only requests a multiple of the blocksize when SHA3_Squeeze() is called. On the first call the value is zero, on subsequent calls the value passed is 1. This PR is derived from the excellent work done by @nmathewson in https://github.com/openssl/openssl/pull/7921 Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/21511) Signed-off-by: fly2x --- crypto/evp/digest.c | 37 +- crypto/evp/legacy_sha.c | 3 +- crypto/sha/asm/keccak1600-armv4.pl | 4 +- crypto/sha/asm/keccak1600-armv8.pl | 4 +- crypto/sha/asm/keccak1600-ppc64.pl | 3 + crypto/sha/asm/keccak1600-x86_64.pl | 18 +- crypto/sha/keccak1600.c | 19 +- crypto/sha/sha3.c | 100 +++- doc/life-cycles/digest.dot | 24 +- doc/man3/EVP_DigestInit.pod | 21 +- doc/man7/EVP_MD-BLAKE2.pod | 11 + doc/man7/EVP_MD-SHAKE.pod | 15 +- doc/man7/img/digest.png | Bin 56894 -> 84676 bytes doc/man7/life_cycle-digest.pod | 130 +++-- doc/man7/provider-digest.pod | 3 +- include/crypto/evp.h | 1 + include/internal/sha3.h | 17 +- include/openssl/core_dispatch.h | 4 + include/openssl/evp.h | 6 +- providers/implementations/digests/sha3_prov.c | 114 +++- test/build.info | 7 +- test/evp_xof_test.c | 492 ++++++++++++++++++ test/recipes/30-test_evp_xof.t | 12 + util/libcrypto.num | 1 + 24 files changed, 938 insertions(+), 108 deletions(-) create mode 100644 test/evp_xof_test.c create mode 100644 test/recipes/30-test_evp_xof.t diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 42331703da..ab670a8f49 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -502,6 +502,7 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *isize) return ret; } +/* This is a one shot operation */ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size) { int ret = 0; @@ -526,10 +527,15 @@ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size) return 0; } + /* + * For backward compatibility we pass the XOFLEN via a param here so that + * older providers can use the supplied value. Ideally we should have just + * used the size passed into ctx->digest->dfinal(). + */ params[i++] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &size); params[i++] = OSSL_PARAM_construct_end(); - if (EVP_MD_CTX_set_params(ctx, params) > 0) + if (EVP_MD_CTX_set_params(ctx, params) >= 0) ret = ctx->digest->dfinal(ctx->algctx, md, &size, size); ctx->flags |= EVP_MD_CTX_FLAG_FINALISED; @@ -553,6 +559,27 @@ legacy: return ret; } +/* EVP_DigestSqueeze() can be called multiple times */ +int EVP_DigestSqueeze(EVP_MD_CTX *ctx, unsigned char *md, size_t size) +{ + if (ctx->digest == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_NULL_ALGORITHM); + return 0; + } + + if (ctx->digest->prov == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_OPERATION); + return 0; + } + + if (ctx->digest->dsqueeze == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_METHOD_NOT_SUPPORTED); + return 0; + } + + return ctx->digest->dsqueeze(ctx->algctx, md, &size, size); +} + EVP_MD_CTX *EVP_MD_CTX_dup(const EVP_MD_CTX *in) { EVP_MD_CTX *out = EVP_MD_CTX_new(); @@ -1032,6 +1059,12 @@ static void *evp_md_from_algorithm(int name_id, fncnt++; } break; + case OSSL_FUNC_DIGEST_SQUEEZE: + if (md->dsqueeze == NULL) { + md->dsqueeze = OSSL_FUNC_digest_squeeze(fns); + fncnt++; + } + break; case OSSL_FUNC_DIGEST_DIGEST: if (md->digest == NULL) md->digest = OSSL_FUNC_digest_digest(fns); @@ -1075,7 +1108,7 @@ static void *evp_md_from_algorithm(int name_id, break; } } - if ((fncnt != 0 && fncnt != 5) + if ((fncnt != 0 && fncnt != 5 && fncnt != 6) || (fncnt == 0 && md->digest == NULL)) { /* * In order to be a consistent set of functions we either need the diff --git a/crypto/evp/legacy_sha.c b/crypto/evp/legacy_sha.c index 0c2afc2900..38423ff540 100644 --- a/crypto/evp/legacy_sha.c +++ b/crypto/evp/legacy_sha.c @@ -37,7 +37,8 @@ static int nm##_update(EVP_MD_CTX *ctx, const void *data, size_t count) \ } \ static int nm##_final(EVP_MD_CTX *ctx, unsigned char *md) \ { \ - return fn##_final(md, EVP_MD_CTX_get0_md_data(ctx)); \ + KECCAK1600_CTX *kctx = EVP_MD_CTX_get0_md_data(ctx); \ + return fn##_final(kctx, md, kctx->md_size); \ } #define IMPLEMENT_LEGACY_EVP_MD_METH_SHAKE(nm, fn, tag) \ static int nm##_init(EVP_MD_CTX *ctx) \ diff --git a/crypto/sha/asm/keccak1600-armv4.pl b/crypto/sha/asm/keccak1600-armv4.pl index eaad86d39d..18948fd7c0 100755 --- a/crypto/sha/asm/keccak1600-armv4.pl +++ b/crypto/sha/asm/keccak1600-armv4.pl @@ -966,6 +966,8 @@ SHA3_squeeze: stmdb sp!,{r6-r9} mov r14,$A_flat + cmp r4, #0 @ r4 = 'next' argument + bne .Lnext_block b .Loop_squeeze .align 4 @@ -1037,7 +1039,7 @@ SHA3_squeeze: subs $bsz,$bsz,#8 @ bsz -= 8 bhi .Loop_squeeze - +.Lnext_block: mov r0,r14 @ original $A_flat bl KeccakF1600 diff --git a/crypto/sha/asm/keccak1600-armv8.pl b/crypto/sha/asm/keccak1600-armv8.pl index ab7aa713ac..72f8c3adb5 100755 --- a/crypto/sha/asm/keccak1600-armv8.pl +++ b/crypto/sha/asm/keccak1600-armv8.pl @@ -483,6 +483,8 @@ SHA3_squeeze: mov $out,x1 mov $len,x2 mov $bsz,x3 + cmp x4, #0 // x4 = 'next' argument + bne .Lnext_block .Loop_squeeze: ldr x4,[x0],#8 @@ -497,7 +499,7 @@ SHA3_squeeze: subs x3,x3,#8 bhi .Loop_squeeze - +.Lnext_block: mov x0,$A_flat bl KeccakF1600 mov x0,$A_flat diff --git a/crypto/sha/asm/keccak1600-ppc64.pl b/crypto/sha/asm/keccak1600-ppc64.pl index bff0d78585..3f8ba817f8 100755 --- a/crypto/sha/asm/keccak1600-ppc64.pl +++ b/crypto/sha/asm/keccak1600-ppc64.pl @@ -668,6 +668,8 @@ SHA3_squeeze: subi $out,r4,1 ; prepare for stbu mr $len,r5 mr $bsz,r6 + ${UCMP}i r7,1 ; r7 = 'next' argument + blt .Lnext_block b .Loop_squeeze .align 4 @@ -698,6 +700,7 @@ SHA3_squeeze: subic. r6,r6,8 bgt .Loop_squeeze +.Lnext_block: mr r3,$A_flat bl KeccakF1600 subi r3,$A_flat,8 ; prepare for ldu diff --git a/crypto/sha/asm/keccak1600-x86_64.pl b/crypto/sha/asm/keccak1600-x86_64.pl index 02f0116014..bddcaf8294 100755 --- a/crypto/sha/asm/keccak1600-x86_64.pl +++ b/crypto/sha/asm/keccak1600-x86_64.pl @@ -503,12 +503,12 @@ SHA3_absorb: .size SHA3_absorb,.-SHA3_absorb ___ } -{ my ($A_flat,$out,$len,$bsz) = ("%rdi","%rsi","%rdx","%rcx"); +{ my ($A_flat,$out,$len,$bsz,$next) = ("%rdi","%rsi","%rdx","%rcx","%r8"); ($out,$len,$bsz) = ("%r12","%r13","%r14"); $code.=<<___; .globl SHA3_squeeze -.type SHA3_squeeze,\@function,4 +.type SHA3_squeeze,\@function,5 .align 32 SHA3_squeeze: .cfi_startproc @@ -520,10 +520,12 @@ SHA3_squeeze: .cfi_push %r14 shr \$3,%rcx - mov $A_flat,%r8 + mov $A_flat,%r9 mov %rsi,$out mov %rdx,$len mov %rcx,$bsz + bt \$0,$next + jc .Lnext_block jmp .Loop_squeeze .align 32 @@ -531,8 +533,8 @@ SHA3_squeeze: cmp \$8,$len jb .Ltail_squeeze - mov (%r8),%rax - lea 8(%r8),%r8 + mov (%r9),%rax + lea 8(%r9),%r9 mov %rax,($out) lea 8($out),$out sub \$8,$len # len -= 8 @@ -540,14 +542,14 @@ SHA3_squeeze: sub \$1,%rcx # bsz-- jnz .Loop_squeeze - +.Lnext_block: call KeccakF1600 - mov $A_flat,%r8 + mov $A_flat,%r9 mov $bsz,%rcx jmp .Loop_squeeze .Ltail_squeeze: - mov %r8, %rsi + mov %r9, %rsi mov $out,%rdi mov $len,%rcx .byte 0xf3,0xa4 # rep movsb diff --git a/crypto/sha/keccak1600.c b/crypto/sha/keccak1600.c index c15bc42aaa..6682367be1 100644 --- a/crypto/sha/keccak1600.c +++ b/crypto/sha/keccak1600.c @@ -13,7 +13,7 @@ size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len, size_t r); -void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r); +void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next); #if !defined(KECCAK1600_ASM) || !defined(SELFTEST) @@ -1090,10 +1090,16 @@ size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len, } /* - * sha3_squeeze is called once at the end to generate |out| hash value - * of |len| bytes. + * SHA3_squeeze may be called after SHA3_absorb to generate |out| hash value of + * |len| bytes. + * If multiple SHA3_squeeze calls are required the output length |len| must be a + * multiple of the blocksize, with |next| being 0 on the first call and 1 on + * subsequent calls. It is the callers responsibility to buffer the results. + * When only a single call to SHA3_squeeze is required, |len| can be any size + * and |next| must be 0. */ -void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r) +void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, + int next) { uint64_t *A_flat = (uint64_t *)A; size_t i, w = r / 8; @@ -1101,6 +1107,9 @@ void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r) assert(r < (25 * sizeof(A[0][0])) && (r % 8) == 0); while (len != 0) { + if (next) + KeccakF1600(A); + next = 1; for (i = 0; i < w && len != 0; i++) { uint64_t Ai = BitDeinterleave(A_flat[i]); @@ -1123,8 +1132,6 @@ void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r) out += 8; len -= 8; } - if (len) - KeccakF1600(A); } } #endif diff --git a/crypto/sha/sha3.c b/crypto/sha/sha3.c index 633bc2e120..2411b3f1f8 100644 --- a/crypto/sha/sha3.c +++ b/crypto/sha/sha3.c @@ -10,12 +10,13 @@ #include #include "internal/sha3.h" -void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r); +void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next); void ossl_sha3_reset(KECCAK1600_CTX *ctx) { memset(ctx->A, 0, sizeof(ctx->A)); ctx->bufsz = 0; + ctx->xof_state = XOF_STATE_INIT; } int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen) @@ -51,6 +52,10 @@ int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len) if (len == 0) return 1; + if (ctx->xof_state == XOF_STATE_SQUEEZE + || ctx->xof_state == XOF_STATE_FINAL) + return 0; + if ((num = ctx->bufsz) != 0) { /* process intermediate buffer? */ rem = bsz - num; @@ -84,13 +89,21 @@ int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len) return 1; } -int ossl_sha3_final(unsigned char *md, KECCAK1600_CTX *ctx) +/* + * ossl_sha3_final()is a single shot method + * (Use ossl_sha3_squeeze for multiple calls). + * outlen is the variable size output. + */ +int ossl_sha3_final(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen) { size_t bsz = ctx->block_size; size_t num = ctx->bufsz; - if (ctx->md_size == 0) + if (outlen == 0) return 1; + if (ctx->xof_state == XOF_STATE_SQUEEZE + || ctx->xof_state == XOF_STATE_FINAL) + return 0; /* * Pad the data with 10*1. Note that |num| can be |bsz - 1| @@ -103,7 +116,86 @@ int ossl_sha3_final(unsigned char *md, KECCAK1600_CTX *ctx) (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz); - SHA3_squeeze(ctx->A, md, ctx->md_size, bsz); + ctx->xof_state = XOF_STATE_FINAL; + SHA3_squeeze(ctx->A, out, outlen, bsz, 0); + return 1; +} + +/* + * This method can be called multiple times. + * Rather than heavily modifying assembler for SHA3_squeeze(), + * we instead just use the limitations of the existing function. + * i.e. Only request multiples of the ctx->block_size when calling + * SHA3_squeeze(). For output length requests smaller than the + * ctx->block_size just request a single ctx->block_size bytes and + * buffer the results. The next request will use the buffer first + * to grab output bytes. + */ +int ossl_sha3_squeeze(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen) +{ + size_t bsz = ctx->block_size; + size_t num = ctx->bufsz; + size_t len; + int next = 1; + + if (outlen == 0) + return 1; + + if (ctx->xof_state == XOF_STATE_FINAL) + return 0; + + /* + * On the first squeeze call, finish the absorb process, + * by adding the trailing padding and then doing + * a final absorb. + */ + if (ctx->xof_state != XOF_STATE_SQUEEZE) { + /* + * Pad the data with 10*1. Note that |num| can be |bsz - 1| + * in which case both byte operations below are performed on + * same byte... + */ + memset(ctx->buf + num, 0, bsz - num); + ctx->buf[num] = ctx->pad; + ctx->buf[bsz - 1] |= 0x80; + (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz); + ctx->xof_state = XOF_STATE_SQUEEZE; + num = ctx->bufsz = 0; + next = 0; + } + + /* + * Step 1. Consume any bytes left over from a previous squeeze + * (See Step 4 below). + */ + if (num != 0) { + if (outlen > ctx->bufsz) + len = ctx->bufsz; + else + len = outlen; + memcpy(out, ctx->buf + bsz - ctx->bufsz, len); + out += len; + outlen -= len; + ctx->bufsz -= len; + } + if (outlen == 0) + return 1; + + /* Step 2. Copy full sized squeezed blocks to the output buffer directly */ + if (outlen >= bsz) { + len = bsz * (outlen / bsz); + SHA3_squeeze(ctx->A, out, len, bsz, next); + next = 1; + out += len; + outlen -= len; + } + if (outlen > 0) { + /* Step 3. Squeeze one more block into a buffer */ + SHA3_squeeze(ctx->A, ctx->buf, bsz, bsz, next); + memcpy(out, ctx->buf, outlen); + /* Step 4. Remember the leftover part of the squeezed block */ + ctx->bufsz = bsz - outlen; + } return 1; } diff --git a/doc/life-cycles/digest.dot b/doc/life-cycles/digest.dot index 8d4d72480c..2f22a0d5e6 100644 --- a/doc/life-cycles/digest.dot +++ b/doc/life-cycles/digest.dot @@ -6,28 +6,30 @@ digraph digest { initialised [label=initialised, fontcolor="#c94c4c"]; updated [label=updated, fontcolor="#c94c4c"]; finaled [label="finaled", fontcolor="#c94c4c"]; + squeezed [label="squeezed", fontcolor="#c94c4c"]; end [label="freed", color="#deeaee", style="filled"]; begin -> newed [label="EVP_MD_CTX_new"]; - newed -> initialised [label="EVP_DigestInit"]; - initialised -> updated [label="EVP_DigestUpdate", weight=3]; + newed -> initialised [label="EVP_DigestInit", weight=100]; + initialised -> updated [label="EVP_DigestUpdate", weight=100]; updated -> updated [label="EVP_DigestUpdate"]; - updated -> finaled [label="EVP_DigestFinal"]; + updated -> finaled [label="EVP_DigestFinal", weight=2]; updated -> finaled [label="EVP_DigestFinalXOF", fontcolor="#808080", color="#808080"]; - /* Once this works it should go back in: - finaled -> finaled [taillabel="EVP_DigestFinalXOF", - labeldistance=9, labelangle=345, - labelfontcolor="#808080", color="#808080"]; - */ + updated -> squeezed [label="EVP_DigestSqueeze", weight=3]; finaled -> end [label="EVP_MD_CTX_free"]; - finaled -> newed [label="EVP_MD_CTX_reset", style=dashed, weight=2, + finaled -> newed [label="EVP_MD_CTX_reset", style=dashed, color="#034f84", fontcolor="#034f84"]; updated -> newed [label="EVP_MD_CTX_reset", style=dashed, color="#034f84", fontcolor="#034f84"]; - updated -> initialised [label="EVP_DigestInit", weight=0, style=dashed, + updated -> initialised [label="EVP_DigestInit", style=dashed, color="#034f84", fontcolor="#034f84"]; finaled -> initialised [label="EVP_DigestInit", style=dashed, color="#034f84", fontcolor="#034f84"]; + squeezed -> squeezed [label="EVP_DigestSqueeze"]; + squeezed -> end [label="EVP_MD_CTX_free", weight=1]; + squeezed -> newed [label="EVP_MD_CTX_reset", style=dashed, + color="#034f84", fontcolor="#034f84"]; + squeezed -> initialised [label="EVP_DigestInit", style=dashed, + color="#034f84", fontcolor="#034f84"]; } - diff --git a/doc/man3/EVP_DigestInit.pod b/doc/man3/EVP_DigestInit.pod index 409630e5d4..492180def9 100644 --- a/doc/man3/EVP_DigestInit.pod +++ b/doc/man3/EVP_DigestInit.pod @@ -12,6 +12,7 @@ EVP_MD_CTX_settable_params, EVP_MD_CTX_gettable_params, EVP_MD_CTX_set_flags, EVP_MD_CTX_clear_flags, EVP_MD_CTX_test_flags, EVP_Q_digest, EVP_Digest, EVP_DigestInit_ex2, EVP_DigestInit_ex, EVP_DigestInit, EVP_DigestUpdate, EVP_DigestFinal_ex, EVP_DigestFinalXOF, EVP_DigestFinal, +EVP_DigestSqueeze, EVP_MD_is_a, EVP_MD_get0_name, EVP_MD_get0_description, EVP_MD_names_do_all, EVP_MD_get0_provider, EVP_MD_get_type, EVP_MD_get_pkey_type, EVP_MD_get_size, EVP_MD_get_block_size, EVP_MD_get_flags, @@ -61,7 +62,8 @@ EVP_MD_CTX_type, EVP_MD_CTX_pkey_ctx, EVP_MD_CTX_md_data int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s); - int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t len); + int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *out, size_t outlen); + int EVP_DigestSqueeze(EVP_MD_CTX *ctx, unsigned char *out, size_t outlen); EVP_MD_CTX *EVP_MD_CTX_dup(const EVP_MD_CTX *in); int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in); @@ -291,9 +293,16 @@ initialize a new digest operation. =item EVP_DigestFinalXOF() Interfaces to extendable-output functions, XOFs, such as SHAKE128 and SHAKE256. -It retrieves the digest value from I and places it in I-sized I. +It retrieves the digest value from I and places it in I-sized I. After calling this function no additional calls to EVP_DigestUpdate() can be made, but EVP_DigestInit_ex2() can be called to initialize a new operation. +EVP_DigestFinalXOF() may only be called once + +=item EVP_DigestSqueeze() + +Similar to EVP_DigestFinalXOF() but allows multiple calls to be made to +squeeze variable length output data. +EVP_DigestFinalXOF() should not be called after this. =item EVP_MD_CTX_dup() @@ -478,8 +487,9 @@ EVP_MD_CTX_set_params() can be used with the following OSSL_PARAM keys: =item "xoflen" (B) Sets the digest length for extendable output functions. -It is used by the SHAKE algorithm and should not exceed what can be given -using a B. +The value should not exceed what can be given using a B. +It may be used by BLAKE2B-512, SHAKE-128 and SHAKE-256 to set the +output length used by EVP_DigestFinal_ex() and EVP_DigestFinal(). =item "pad-type" (B) @@ -795,7 +805,8 @@ EVP_MD_CTX_get0_md() instead. EVP_MD_CTX_update_fn() and EVP_MD_CTX_set_update_fn() were deprecated in OpenSSL 3.0. -EVP_MD_CTX_dup() was added in OpenSSL 3.2. +The functions EVP_MD_CTX_dup() and EVP_DigestSqueeze() were added in +OpenSSL 3.2. =head1 COPYRIGHT diff --git a/doc/man7/EVP_MD-BLAKE2.pod b/doc/man7/EVP_MD-BLAKE2.pod index 205b0c59be..288a6dd735 100644 --- a/doc/man7/EVP_MD-BLAKE2.pod +++ b/doc/man7/EVP_MD-BLAKE2.pod @@ -25,6 +25,17 @@ Known names are "BLAKE2B-512" and "BLAKE2b512". =back +=head2 Settable Parameters + +"BLAKE2B-512" supports the following EVP_MD_CTX_set_params() key +described in L. + +=over 4 + +=item "xoflen" (B) + +=back + =head2 Gettable Parameters This implementation supports the common gettable parameters described diff --git a/doc/man7/EVP_MD-SHAKE.pod b/doc/man7/EVP_MD-SHAKE.pod index 8a31cd53a8..6f1fe9cae6 100644 --- a/doc/man7/EVP_MD-SHAKE.pod +++ b/doc/man7/EVP_MD-SHAKE.pod @@ -65,15 +65,28 @@ For backwards compatibility reasons the default xoflen length for SHAKE-256 is 32 (bytes) which results in a security strength of only 128 bits. To ensure the maximum security strength of 256 bits, the xoflen should be set to at least 64. +This parameter may be used when calling either EVP_DigestFinal_ex() or +EVP_DigestFinal(), since these functions were not designed to handle variable +length output. It is recommended to either use EVP_DigestSqueeze() or +EVP_DigestFinalXOF() instead. + =back +=head1 NOTES + +For SHAKE-128, to ensure the maximum security strength of 128 bits, the output +length passed to EVP_DigestFinalXOF() should be at least 32. + +For SHAKE-256, to ensure the maximum security strength of 256 bits, the output +length passed to EVP_DigestFinalXOF() should be at least 64. + =head1 SEE ALSO L, L, L =head1 COPYRIGHT -Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/doc/man7/img/digest.png b/doc/man7/img/digest.png index 9f35deb5dcc8c03c8631e7d60267e9bbcb1b6a9a..8a9f78a26b39b0e091095fa0b67a5b14d4bc153c 100644 GIT binary patch literal 84676 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%0D=i(7 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 L9;%nA!ovO^)z--C literal 56894 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 | initialised | <+ EVP_DigestInit | - | +-------------------+ | | - | | | EVP_DigestUpdate | - | | EVP_DigestUpdate | +------------------+ | - | v | v | | - | +------------------------------------------------+ | - EVP_DigestInit | | updated | --+ - | +------------------------------------------------+ | - | | | | - | | EVP_DigestFinal | EVP_DigestFinalXOF | - | v v | - | +------------------------------------------------+ | - +--- | finaled | --+ - +------------------------------------------------+ - | - | EVP_MD_CTX_free - v - +-------------------+ - | freed | - +-------------------+ + +--------------------+ + | start | + +--------------------+ + | EVP_MD_CTX_reset + | EVP_MD_CTX_new +-------------------------------------------------+ + v v | + EVP_MD_CTX_reset + - - - - - - - - - - - - - - - - - - - - - - + EVP_MD_CTX_reset | + +-------------------> ' newed ' <--------------------+ | + | + - - - - - - - - - - - - - - - - - - - - - - + | | + | | | | + | | EVP_DigestInit | | + | v | | + | EVP_DigestInit + - - - - - - - - - - - - - - - - - - - - - - + | | + +----+-------------------> ' initialised ' <+ EVP_DigestInit | | + | | + - - - - - - - - - - - - - - - - - - - - - - + | | | + | | | ^ | | | + | | | EVP_DigestUpdate | EVP_DigestInit | | | + | | v | | | | + | | +---------------------------------------------+ | | | + | +-------------------- | | | | | + | | | | | | + | EVP_DigestUpdate | | | | | + | +-------------------- | | | | | + | | | updated | | | | + | +-------------------> | | | | | + | | | | | | + | | | | | | + +----+------------------------- | | -+-------------------+----+ | + | | +---------------------------------------------+ | | | | + | | | | | | | + | | | EVP_DigestSqueeze +-------------------+ | | | + | | v | | | | + | | EVP_DigestSqueeze +---------------------------------------------+ | | | + | | +-------------------- | | | | | + | | | | squeezed | | | | + | | +-------------------> | | ---------------------+ | | + | | +---------------------------------------------+ | | + | | | | | + | | +---------------------------------------+ | | + | | | | | + | | +---------------------------------------------+ EVP_DigestFinalXOF | | | + | +------------------------- | finaled | <--------------------+----+ | + | +---------------------------------------------+ | | + | EVP_DigestFinal ^ | | | | + +---------------------------------+ | | EVP_MD_CTX_free | | + | v | | + | +------------------+ EVP_MD_CTX_free | | + | | freed | <--------------------+ | + | +------------------+ | + | | + +------------------------------------------------------+ =end man @@ -91,19 +117,21 @@ This is the canonical list. =begin man - Function Call --------------------- Current State ---------------------- - start newed initialised updated finaled freed + Function Call --------------------- Current State ----------------------------------- + start newed initialised updated finaled squeezed freed EVP_MD_CTX_new newed - EVP_DigestInit initialised initialised initialised initialised + EVP_DigestInit initialised initialised initialised initialised initialised EVP_DigestUpdate updated updated EVP_DigestFinal finaled EVP_DigestFinalXOF finaled + EVP_DigestSqueeze squeezed squeezed EVP_MD_CTX_free freed freed freed freed freed EVP_MD_CTX_reset newed newed newed newed EVP_MD_CTX_get_params newed initialised updated EVP_MD_CTX_set_params newed initialised updated EVP_MD_CTX_gettable_params newed initialised updated EVP_MD_CTX_settable_params newed initialised updated + EVP_MD_CTX_copy_ex newed initialised updated squeezed =end man @@ -118,6 +146,7 @@ This is the canonical list. initialised updated finaled + squeezed freed EVP_MD_CTX_new newed @@ -125,6 +154,7 @@ This is the canonical list. + EVP_DigestInit @@ -132,6 +162,7 @@ This is the canonical list. initialised initialised initialised + initialised EVP_DigestUpdate @@ -139,6 +170,7 @@ This is the canonical list. updated updated + EVP_DigestFinal @@ -146,6 +178,15 @@ This is the canonical list. finaled + + +EVP_DigestSqueeze + + + + squeezed + + squeezed EVP_DigestFinalXOF @@ -153,6 +194,7 @@ This is the canonical list. finaled + EVP_MD_CTX_free freed @@ -160,6 +202,7 @@ This is the canonical list. freed freed freed + EVP_MD_CTX_reset @@ -167,6 +210,7 @@ This is the canonical list. newed newed newed + EVP_MD_CTX_get_params @@ -174,6 +218,7 @@ This is the canonical list. initialised updated + EVP_MD_CTX_set_params @@ -181,6 +226,7 @@ This is the canonical list. initialised updated + EVP_MD_CTX_gettable_params @@ -188,6 +234,7 @@ This is the canonical list. initialised updated + EVP_MD_CTX_settable_params @@ -195,6 +242,15 @@ This is the canonical list. initialised updated + + +EVP_MD_CTX_copy_ex + + newed + initialised + updated + + squeezed @@ -211,7 +267,7 @@ L, L =head1 COPYRIGHT -Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2021-2023 The OpenSSL Project Authors. All Rights Reserved. Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/doc/man7/provider-digest.pod b/doc/man7/provider-digest.pod index 2c99b8b3fb..d23da59e1a 100644 --- a/doc/man7/provider-digest.pod +++ b/doc/man7/provider-digest.pod @@ -198,8 +198,7 @@ This digest method can only handle one block of input. =item B -This digest method is an extensible-output function (XOF) and supports -setting the B parameter. +This digest method is an extensible-output function (XOF). =item B diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 34cea2f9f4..96133bf7f5 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -282,6 +282,7 @@ struct evp_md_st { OSSL_FUNC_digest_init_fn *dinit; OSSL_FUNC_digest_update_fn *dupdate; OSSL_FUNC_digest_final_fn *dfinal; + OSSL_FUNC_digest_squeeze_fn *dsqueeze; OSSL_FUNC_digest_digest_fn *digest; OSSL_FUNC_digest_freectx_fn *freectx; OSSL_FUNC_digest_dupctx_fn *dupctx; diff --git a/include/internal/sha3.h b/include/internal/sha3.h index 80ad86e58e..332916aa54 100644 --- a/include/internal/sha3.h +++ b/include/internal/sha3.h @@ -22,23 +22,31 @@ typedef struct keccak_st KECCAK1600_CTX; -typedef size_t (sha3_absorb_fn)(void *vctx, const void *inp, size_t len); -typedef int (sha3_final_fn)(unsigned char *md, void *vctx); +typedef size_t (sha3_absorb_fn)(void *vctx, const void *in, size_t inlen); +typedef int (sha3_final_fn)(void *vctx, unsigned char *out, size_t outlen); +typedef int (sha3_squeeze_fn)(void *vctx, unsigned char *out, size_t outlen); typedef struct prov_sha3_meth_st { sha3_absorb_fn *absorb; sha3_final_fn *final; + sha3_squeeze_fn *squeeze; } PROV_SHA3_METHOD; +#define XOF_STATE_INIT 0 +#define XOF_STATE_ABSORB 1 +#define XOF_STATE_FINAL 2 +#define XOF_STATE_SQUEEZE 3 + struct keccak_st { uint64_t A[5][5]; + unsigned char buf[KECCAK1600_WIDTH / 8 - 32]; size_t block_size; /* cached ctx->digest->block_size */ size_t md_size; /* output length, variable in XOF */ size_t bufsz; /* used bytes in below buffer */ - unsigned char buf[KECCAK1600_WIDTH / 8 - 32]; unsigned char pad; PROV_SHA3_METHOD meth; + int xof_state; }; void ossl_sha3_reset(KECCAK1600_CTX *ctx); @@ -46,7 +54,8 @@ int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen); int ossl_keccak_kmac_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen); int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len); -int ossl_sha3_final(unsigned char *md, KECCAK1600_CTX *ctx); +int ossl_sha3_final(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen); +int ossl_sha3_squeeze(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen); size_t SHA3_absorb(uint64_t A[5][5], const unsigned char *inp, size_t len, size_t r); diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h index 9b03f20c3b..a5bc2cf75d 100644 --- a/include/openssl/core_dispatch.h +++ b/include/openssl/core_dispatch.h @@ -300,6 +300,7 @@ OSSL_CORE_MAKE_FUNC(int, provider_self_test, (void *provctx)) # define OSSL_FUNC_DIGEST_GETTABLE_PARAMS 11 # define OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS 12 # define OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS 13 +# define OSSL_FUNC_DIGEST_SQUEEZE 14 OSSL_CORE_MAKE_FUNC(void *, digest_newctx, (void *provctx)) OSSL_CORE_MAKE_FUNC(int, digest_init, (void *dctx, const OSSL_PARAM params[])) @@ -308,6 +309,9 @@ OSSL_CORE_MAKE_FUNC(int, digest_update, OSSL_CORE_MAKE_FUNC(int, digest_final, (void *dctx, unsigned char *out, size_t *outl, size_t outsz)) +OSSL_CORE_MAKE_FUNC(int, digest_squeeze, + (void *dctx, + unsigned char *out, size_t *outl, size_t outsz)) OSSL_CORE_MAKE_FUNC(int, digest_digest, (void *provctx, const unsigned char *in, size_t inl, unsigned char *out, size_t *outl, size_t outsz)) diff --git a/include/openssl/evp.h b/include/openssl/evp.h index ea7620d631..f70b9d744d 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -729,8 +729,10 @@ __owur int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in); __owur int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type); __owur int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s); -__owur int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, - size_t len); +__owur int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *out, + size_t outlen); +__owur int EVP_DigestSqueeze(EVP_MD_CTX *ctx, unsigned char *out, + size_t outlen); __owur EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties); diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 423bed7983..19576c7190 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -33,10 +33,12 @@ static OSSL_FUNC_digest_update_fn keccak_update; static OSSL_FUNC_digest_final_fn keccak_final; static OSSL_FUNC_digest_freectx_fn keccak_freectx; static OSSL_FUNC_digest_dupctx_fn keccak_dupctx; +static OSSL_FUNC_digest_squeeze_fn shake_squeeze; static OSSL_FUNC_digest_set_ctx_params_fn shake_set_ctx_params; static OSSL_FUNC_digest_settable_ctx_params_fn shake_settable_ctx_params; static sha3_absorb_fn generic_sha3_absorb; static sha3_final_fn generic_sha3_final; +static sha3_squeeze_fn generic_sha3_squeeze; #if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) && defined(KECCAK1600_ASM) /* @@ -103,20 +105,37 @@ static int keccak_update(void *vctx, const unsigned char *inp, size_t len) } static int keccak_final(void *vctx, unsigned char *out, size_t *outl, - size_t outsz) + size_t outlen) { int ret = 1; KECCAK1600_CTX *ctx = vctx; if (!ossl_prov_is_running()) return 0; - if (outsz > 0) - ret = ctx->meth.final(out, ctx); + if (outlen > 0) + ret = ctx->meth.final(ctx, out, ctx->md_size); *outl = ctx->md_size; return ret; } +static int shake_squeeze(void *vctx, unsigned char *out, size_t *outl, + size_t outlen) +{ + int ret = 1; + KECCAK1600_CTX *ctx = vctx; + + if (!ossl_prov_is_running()) + return 0; + if (ctx->meth.squeeze == NULL) + return 0; + if (outlen > 0) + ret = ctx->meth.squeeze(ctx, out, outlen); + + *outl = outlen; + return ret; +} + /*- * Generic software version of the absorb() and final(). */ @@ -127,15 +146,28 @@ static size_t generic_sha3_absorb(void *vctx, const void *inp, size_t len) return SHA3_absorb(ctx->A, inp, len, ctx->block_size); } -static int generic_sha3_final(unsigned char *md, void *vctx) +static int generic_sha3_final(void *vctx, unsigned char *out, size_t outlen) { - return ossl_sha3_final(md, (KECCAK1600_CTX *)vctx); + return ossl_sha3_final((KECCAK1600_CTX *)vctx, out, outlen); +} + +static int generic_sha3_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + return ossl_sha3_squeeze((KECCAK1600_CTX *)vctx, out, outlen); } static PROV_SHA3_METHOD sha3_generic_md = { generic_sha3_absorb, - generic_sha3_final + generic_sha3_final, + NULL +}; + +static PROV_SHA3_METHOD shake_generic_md = +{ + generic_sha3_absorb, + generic_sha3_final, + generic_sha3_squeeze }; #if defined(S390_SHA3) @@ -156,59 +188,60 @@ static size_t s390x_sha3_absorb(void *vctx, const void *inp, size_t len) return rem; } -static int s390x_sha3_final(unsigned char *md, void *vctx) +static int s390x_sha3_final(void *vctx, unsigned char *out, size_t outlen) { KECCAK1600_CTX *ctx = vctx; if (!ossl_prov_is_running()) return 0; s390x_klmd(ctx->buf, ctx->bufsz, NULL, 0, ctx->pad, ctx->A); - memcpy(md, ctx->A, ctx->md_size); + memcpy(out, ctx->A, outlen); return 1; } -static int s390x_shake_final(unsigned char *md, void *vctx) +static int s390x_shake_final(void *vctx, unsigned char *out, size_t outlen) { KECCAK1600_CTX *ctx = vctx; if (!ossl_prov_is_running()) return 0; - s390x_klmd(ctx->buf, ctx->bufsz, md, ctx->md_size, ctx->pad, ctx->A); + s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, ctx->pad, ctx->A); return 1; } -static int s390x_keccakc_final(unsigned char *md, void *vctx, int padding) +static int s390x_keccakc_final(void *vctx, unsigned char *out, size_t outlen, + int padding) { KECCAK1600_CTX *ctx = vctx; size_t bsz = ctx->block_size; size_t num = ctx->bufsz; - size_t needed = ctx->md_size; + size_t needed = outlen; if (!ossl_prov_is_running()) return 0; - if (ctx->md_size == 0) + if (outlen == 0) return 1; memset(ctx->buf + num, 0, bsz - num); ctx->buf[num] = padding; ctx->buf[bsz - 1] |= 0x80; s390x_kimd(ctx->buf, bsz, ctx->pad, ctx->A); num = needed > bsz ? bsz : needed; - memcpy(md, ctx->A, num); + memcpy(out, ctx->A, num); needed -= num; if (needed > 0) - s390x_klmd(NULL, 0, md + bsz, needed, ctx->pad | S390X_KLMD_PS, ctx->A); + s390x_klmd(NULL, 0, out + bsz, needed, ctx->pad | S390X_KLMD_PS, ctx->A); return 1; } -static int s390x_keccak_final(unsigned char *md, void *vctx) +static int s390x_keccak_final(void *vctx, unsigned char *out, size_t outlen) { - return s390x_keccakc_final(md, vctx, 0x01); + return s390x_keccakc_final(vctx, out, outlen, 0x01); } -static int s390x_kmac_final(unsigned char *md, void *vctx) +static int s390x_kmac_final(void *vctx, unsigned char *out, size_t outlen) { - return s390x_keccakc_final(md, vctx, 0x04); + return s390x_keccakc_final(vctx, out, outlen, 0x04); } static PROV_SHA3_METHOD sha3_s390x_md = @@ -220,7 +253,7 @@ static PROV_SHA3_METHOD sha3_s390x_md = static PROV_SHA3_METHOD keccak_s390x_md = { s390x_sha3_absorb, - s390x_keccak_final + s390x_keccak_final, }; static PROV_SHA3_METHOD shake_s390x_md = @@ -235,6 +268,14 @@ static PROV_SHA3_METHOD kmac_s390x_md = s390x_kmac_final }; +# define SHAKE_SET_MD(uname, typ) \ + if (S390_SHA3_CAPABLE(uname)) { \ + ctx->pad = S390X_##uname; \ + ctx->meth = typ##_s390x_md; \ + } else { \ + ctx->meth = shake_generic_md; \ + } + # define SHA3_SET_MD(uname, typ) \ if (S390_SHA3_CAPABLE(uname)) { \ ctx->pad = S390X_##uname; \ @@ -255,7 +296,7 @@ static PROV_SHA3_METHOD kmac_s390x_md = static sha3_absorb_fn armsha3_sha3_absorb; size_t SHA3_absorb_cext(uint64_t A[5][5], const unsigned char *inp, size_t len, - size_t r); + size_t r); /*- * Hardware-assisted ARMv8.2 SHA3 extension version of the absorb() */ @@ -271,6 +312,19 @@ static PROV_SHA3_METHOD sha3_ARMSHA3_md = armsha3_sha3_absorb, generic_sha3_final }; +static PROV_SHA3_METHOD shake_ARMSHA3_md = +{ + armsha3_sha3_absorb, + generic_sha3_final, + generic_sha3_squeeze +}; +# define SHAKE_SET_MD(uname, typ) \ + if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \ + ctx->meth = shake_ARMSHA3_md; \ + } else { \ + ctx->meth = shake_generic_md; \ + } + # define SHA3_SET_MD(uname, typ) \ if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \ ctx->meth = sha3_ARMSHA3_md; \ @@ -286,6 +340,7 @@ static PROV_SHA3_METHOD sha3_ARMSHA3_md = #else # define SHA3_SET_MD(uname, typ) ctx->meth = sha3_generic_md; # define KMAC_SET_MD(bitlen) ctx->meth = sha3_generic_md; +# define SHAKE_SET_MD(uname, typ) ctx->meth = shake_generic_md; #endif /* S390_SHA3 */ #define SHA3_newctx(typ, uname, name, bitlen, pad) \ @@ -302,6 +357,20 @@ static void *name##_newctx(void *provctx) \ return ctx; \ } +#define SHAKE_newctx(typ, uname, name, bitlen, pad) \ +static OSSL_FUNC_digest_newctx_fn name##_newctx; \ +static void *name##_newctx(void *provctx) \ +{ \ + KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx))\ + : NULL; \ + \ + if (ctx == NULL) \ + return NULL; \ + ossl_sha3_init(ctx, pad, bitlen); \ + SHAKE_SET_MD(uname, typ) \ + return ctx; \ +} + #define KMAC_newctx(uname, bitlen, pad) \ static OSSL_FUNC_digest_newctx_fn uname##_newctx; \ static void *uname##_newctx(void *provctx) \ @@ -333,6 +402,7 @@ const OSSL_DISPATCH ossl_##name##_functions[] = { \ #define PROV_FUNC_SHAKE_DIGEST(name, bitlen, blksize, dgstsize, flags) \ PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags), \ + { OSSL_FUNC_DIGEST_SQUEEZE, (void (*)(void))shake_squeeze }, \ { OSSL_FUNC_DIGEST_INIT, (void (*)(void))keccak_init_params }, \ { OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))shake_set_ctx_params }, \ { OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, \ @@ -398,7 +468,7 @@ static int shake_set_ctx_params(void *vctx, const OSSL_PARAM params[]) SHA3_FLAGS) #define IMPLEMENT_SHAKE_functions(bitlen) \ - SHA3_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, '\x1f') \ + SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, '\x1f') \ PROV_FUNC_SHAKE_DIGEST(shake_##bitlen, bitlen, \ SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \ SHAKE_FLAGS) diff --git a/test/build.info b/test/build.info index 1784a41d8d..10f29b19cf 100644 --- a/test/build.info +++ b/test/build.info @@ -62,7 +62,8 @@ IF[{- !$disabled{tests} -}] bio_readbuffer_test user_property_test pkcs7_test upcallstest \ provfetchtest prov_config_test rand_test ca_internals_test \ bio_tfo_test membio_test bio_dgram_test list_test fips_version_test \ - x509_test hpke_test pairwise_fail_test nodefltctxtest + x509_test hpke_test pairwise_fail_test nodefltctxtest \ + evp_xof_test IF[{- !$disabled{'rpk'} -}] PROGRAMS{noinst}=rpktest @@ -558,6 +559,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[evp_kdf_test]=../include ../apps/include DEPEND[evp_kdf_test]=../libcrypto libtestutil.a + SOURCE[evp_xof_test]=evp_xof_test.c + INCLUDE[evp_xof_test]=../include ../apps/include + DEPEND[evp_xof_test]=../libcrypto libtestutil.a + SOURCE[evp_pkey_dparams_test]=evp_pkey_dparams_test.c INCLUDE[evp_pkey_dparams_test]=../include ../apps/include DEPEND[evp_pkey_dparams_test]=../libcrypto libtestutil.a diff --git a/test/evp_xof_test.c b/test/evp_xof_test.c new file mode 100644 index 0000000000..eeff8667c4 --- /dev/null +++ b/test/evp_xof_test.c @@ -0,0 +1,492 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "testutil.h" +#include "internal/nelem.h" + +static const unsigned char shake256_input[] = { + 0x8d, 0x80, 0x01, 0xe2, 0xc0, 0x96, 0xf1, 0xb8, + 0x8e, 0x7c, 0x92, 0x24, 0xa0, 0x86, 0xef, 0xd4, + 0x79, 0x7f, 0xbf, 0x74, 0xa8, 0x03, 0x3a, 0x2d, + 0x42, 0x2a, 0x2b, 0x6b, 0x8f, 0x67, 0x47, 0xe4 +}; + +/* + * This KAT output is 250 bytes, which is more than + * the SHAKE256 block size (136 bytes). + */ +static const unsigned char shake256_output[] = { + 0x2e, 0x97, 0x5f, 0x6a, 0x8a, 0x14, 0xf0, 0x70, + 0x4d, 0x51, 0xb1, 0x36, 0x67, 0xd8, 0x19, 0x5c, + 0x21, 0x9f, 0x71, 0xe6, 0x34, 0x56, 0x96, 0xc4, + 0x9f, 0xa4, 0xb9, 0xd0, 0x8e, 0x92, 0x25, 0xd3, + 0xd3, 0x93, 0x93, 0x42, 0x51, 0x52, 0xc9, 0x7e, + 0x71, 0xdd, 0x24, 0x60, 0x1c, 0x11, 0xab, 0xcf, + 0xa0, 0xf1, 0x2f, 0x53, 0xc6, 0x80, 0xbd, 0x3a, + 0xe7, 0x57, 0xb8, 0x13, 0x4a, 0x9c, 0x10, 0xd4, + 0x29, 0x61, 0x58, 0x69, 0x21, 0x7f, 0xdd, 0x58, + 0x85, 0xc4, 0xdb, 0x17, 0x49, 0x85, 0x70, 0x3a, + 0x6d, 0x6d, 0xe9, 0x4a, 0x66, 0x7e, 0xac, 0x30, + 0x23, 0x44, 0x3a, 0x83, 0x37, 0xae, 0x1b, 0xc6, + 0x01, 0xb7, 0x6d, 0x7d, 0x38, 0xec, 0x3c, 0x34, + 0x46, 0x31, 0x05, 0xf0, 0xd3, 0x94, 0x9d, 0x78, + 0xe5, 0x62, 0xa0, 0x39, 0xe4, 0x46, 0x95, 0x48, + 0xb6, 0x09, 0x39, 0x5d, 0xe5, 0xa4, 0xfd, 0x43, + 0xc4, 0x6c, 0xa9, 0xfd, 0x6e, 0xe2, 0x9a, 0xda, + 0x5e, 0xfc, 0x07, 0xd8, 0x4d, 0x55, 0x32, 0x49, + 0x45, 0x0d, 0xab, 0x4a, 0x49, 0xc4, 0x83, 0xde, + 0xd2, 0x50, 0xc9, 0x33, 0x8f, 0x85, 0xcd, 0x93, + 0x7a, 0xe6, 0x6b, 0xb4, 0x36, 0xf3, 0xb4, 0x02, + 0x6e, 0x85, 0x9f, 0xda, 0x1c, 0xa5, 0x71, 0x43, + 0x2f, 0x3b, 0xfc, 0x09, 0xe7, 0xc0, 0x3c, 0xa4, + 0xd1, 0x83, 0xb7, 0x41, 0x11, 0x1c, 0xa0, 0x48, + 0x3d, 0x0e, 0xda, 0xbc, 0x03, 0xfe, 0xb2, 0x3b, + 0x17, 0xee, 0x48, 0xe8, 0x44, 0xba, 0x24, 0x08, + 0xd9, 0xdc, 0xfd, 0x01, 0x39, 0xd2, 0xe8, 0xc7, + 0x31, 0x01, 0x25, 0xae, 0xe8, 0x01, 0xc6, 0x1a, + 0xb7, 0x90, 0x0d, 0x1e, 0xfc, 0x47, 0xc0, 0x78, + 0x28, 0x17, 0x66, 0xf3, 0x61, 0xc5, 0xe6, 0x11, + 0x13, 0x46, 0x23, 0x5e, 0x1d, 0xc3, 0x83, 0x25, + 0x66, 0x6c +}; + +static const unsigned char shake256_largemsg_input[] = { + 0xb2, 0xd2, 0x38, 0x65, 0xaf, 0x8f, 0x25, 0x6e, + 0x64, 0x40, 0xe2, 0x0d, 0x49, 0x8e, 0x3e, 0x64, + 0x46, 0xd2, 0x03, 0xa4, 0x19, 0xe3, 0x7b, 0x80, + 0xf7, 0x2b, 0x32, 0xe2, 0x76, 0x01, 0xfe, 0xdd, + 0xaa, 0x33, 0x3d, 0xe4, 0x8e, 0xe1, 0x5e, 0x39, + 0xa6, 0x92, 0xa3, 0xa7, 0xe3, 0x81, 0x24, 0x74, + 0xc7, 0x38, 0x18, 0x92, 0xc9, 0x60, 0x50, 0x15, + 0xfb, 0xd8, 0x04, 0xea, 0xea, 0x04, 0xd2, 0xc5, + 0xc6, 0x68, 0x04, 0x5b, 0xc3, 0x75, 0x12, 0xd2, + 0xbe, 0xa2, 0x67, 0x75, 0x24, 0xbf, 0x68, 0xad, + 0x10, 0x86, 0xb3, 0x2c, 0xb3, 0x74, 0xa4, 0x6c, + 0xf9, 0xd7, 0x1e, 0x58, 0x69, 0x27, 0x88, 0x49, + 0x4e, 0x99, 0x15, 0x33, 0x14, 0xf2, 0x49, 0x21, + 0xf4, 0x99, 0xb9, 0xde, 0xd4, 0xf1, 0x12, 0xf5, + 0x68, 0xe5, 0x5c, 0xdc, 0x9e, 0xc5, 0x80, 0x6d, + 0x39, 0x50, 0x08, 0x95, 0xbb, 0x12, 0x27, 0x50, + 0x89, 0xf0, 0xf9, 0xd5, 0x4a, 0x01, 0x0b, 0x0d, + 0x90, 0x9f, 0x1e, 0x4a, 0xba, 0xbe, 0x28, 0x36, + 0x19, 0x7d, 0x9c, 0x0a, 0x51, 0xfb, 0xeb, 0x00, + 0x02, 0x6c, 0x4b, 0x0a, 0xa8, 0x6c, 0xb7, 0xc4, + 0xc0, 0x92, 0x37, 0xa7, 0x2d, 0x49, 0x61, 0x80, + 0xd9, 0xdb, 0x20, 0x21, 0x9f, 0xcf, 0xb4, 0x57, + 0x69, 0x75, 0xfa, 0x1c, 0x95, 0xbf, 0xee, 0x0d, + 0x9e, 0x52, 0x6e, 0x1e, 0xf8, 0xdd, 0x41, 0x8c, + 0x3b, 0xaa, 0x57, 0x13, 0x84, 0x73, 0x52, 0x62, + 0x18, 0x76, 0x46, 0xcc, 0x4b, 0xcb, 0xbd, 0x40, + 0xa1, 0xf6, 0xff, 0x7b, 0x32, 0xb9, 0x90, 0x7c, + 0x53, 0x2c, 0xf9, 0x38, 0x72, 0x0f, 0xcb, 0x90, + 0x42, 0x5e, 0xe2, 0x80, 0x19, 0x26, 0xe7, 0x99, + 0x96, 0x98, 0x18, 0xb1, 0x86, 0x5b, 0x4c, 0xd9, + 0x08, 0x27, 0x31, 0x8f, 0xf0, 0x90, 0xd9, 0x35, + 0x6a, 0x1f, 0x75, 0xc2, 0xe0, 0xa7, 0x60, 0xb8, + 0x1d, 0xd6, 0x5f, 0x56, 0xb2, 0x0b, 0x27, 0x0e, + 0x98, 0x67, 0x1f, 0x39, 0x18, 0x27, 0x68, 0x0a, + 0xe8, 0x31, 0x1b, 0xc0, 0x97, 0xec, 0xd1, 0x20, + 0x2a, 0x55, 0x69, 0x23, 0x08, 0x50, 0x05, 0xec, + 0x13, 0x3b, 0x56, 0xfc, 0x18, 0xc9, 0x1a, 0xa9, + 0x69, 0x0e, 0xe2, 0xcc, 0xc8, 0xd6, 0x19, 0xbb, + 0x87, 0x3b, 0x42, 0x77, 0xee, 0x77, 0x81, 0x26, + 0xdd, 0xf6, 0x5d, 0xc3, 0xb2, 0xb0, 0xc4, 0x14, + 0x6d, 0xb5, 0x4f, 0xdc, 0x13, 0x09, 0xc8, 0x53, + 0x50, 0xb3, 0xea, 0xd3, 0x5f, 0x11, 0x67, 0xd4, + 0x2f, 0x6e, 0x30, 0x1a, 0xbe, 0xd6, 0xf0, 0x2d, + 0xc9, 0x29, 0xd9, 0x0a, 0xa8, 0x6f, 0xa4, 0x18, + 0x74, 0x6b, 0xd3, 0x5d, 0x6a, 0x73, 0x3a, 0xf2, + 0x94, 0x7f, 0xbd, 0xb4, 0xa6, 0x7f, 0x5b, 0x3d, + 0x26, 0xf2, 0x6c, 0x13, 0xcf, 0xb4, 0x26, 0x1e, + 0x38, 0x17, 0x66, 0x60, 0xb1, 0x36, 0xae, 0xe0, + 0x6d, 0x86, 0x69, 0xe7, 0xe7, 0xae, 0x77, 0x6f, + 0x7e, 0x99, 0xe5, 0xd9, 0x62, 0xc9, 0xfc, 0xde, + 0xb4, 0xee, 0x7e, 0xc8, 0xe9, 0xb7, 0x2c, 0xe2, + 0x70, 0xe8, 0x8b, 0x2d, 0x94, 0xad, 0xe8, 0x54, + 0xa3, 0x2d, 0x9a, 0xe2, 0x50, 0x63, 0x87, 0xb3, + 0x56, 0x29, 0xea, 0xa8, 0x5e, 0x96, 0x53, 0x9f, + 0x23, 0x8a, 0xef, 0xa3, 0xd4, 0x87, 0x09, 0x5f, + 0xba, 0xc3, 0xd1, 0xd9, 0x1a, 0x7b, 0x5c, 0x5d, + 0x5d, 0x89, 0xed, 0xb6, 0x6e, 0x39, 0x73, 0xa5, + 0x64, 0x59, 0x52, 0x8b, 0x61, 0x8f, 0x66, 0x69, + 0xb9, 0xf0, 0x45, 0x0a, 0x57, 0xcd, 0xc5, 0x7f, + 0x5d, 0xd0, 0xbf, 0xcc, 0x0b, 0x48, 0x12, 0xe1, + 0xe2, 0xc2, 0xea, 0xcc, 0x09, 0xd9, 0x42, 0x2c, + 0xef, 0x4f, 0xa7, 0xe9, 0x32, 0x5c, 0x3f, 0x22, + 0xc0, 0x45, 0x0b, 0x67, 0x3c, 0x31, 0x69, 0x29, + 0xa3, 0x39, 0xdd, 0x6e, 0x2f, 0xbe, 0x10, 0xc9, + 0x7b, 0xff, 0x19, 0x8a, 0xe9, 0xea, 0xfc, 0x32, + 0x41, 0x33, 0x70, 0x2a, 0x9a, 0xa4, 0xe6, 0xb4, + 0x7e, 0xb4, 0xc6, 0x21, 0x49, 0x5a, 0xfc, 0x45, + 0xd2, 0x23, 0xb3, 0x28, 0x4d, 0x83, 0x60, 0xfe, + 0x70, 0x68, 0x03, 0x59, 0xd5, 0x15, 0xaa, 0x9e, + 0xa0, 0x2e, 0x36, 0xb5, 0x61, 0x0f, 0x61, 0x05, + 0x3c, 0x62, 0x00, 0xa0, 0x47, 0xf1, 0x86, 0xba, + 0x33, 0xb8, 0xca, 0x60, 0x2f, 0x3f, 0x0a, 0x67, + 0x09, 0x27, 0x2f, 0xa2, 0x96, 0x02, 0x52, 0x58, + 0x55, 0x68, 0x80, 0xf4, 0x4f, 0x47, 0xba, 0xff, + 0x41, 0x7a, 0x40, 0x4c, 0xfd, 0x9d, 0x10, 0x72, + 0x0e, 0x20, 0xa9, 0x7f, 0x9b, 0x9b, 0x14, 0xeb, + 0x8e, 0x61, 0x25, 0xcb, 0xf4, 0x58, 0xff, 0x47, + 0xa7, 0x08, 0xd6, 0x4e, 0x2b, 0xf1, 0xf9, 0x89, + 0xd7, 0x22, 0x0f, 0x8d, 0x35, 0x07, 0xa0, 0x54, + 0xab, 0x83, 0xd8, 0xee, 0x5a, 0x3e, 0x88, 0x74, + 0x46, 0x41, 0x6e, 0x3e, 0xb7, 0xc0, 0xb6, 0x55, + 0xe0, 0x36, 0xc0, 0x2b, 0xbf, 0xb8, 0x24, 0x8a, + 0x44, 0x82, 0xf4, 0xcb, 0xb5, 0xd7, 0x41, 0x48, + 0x51, 0x08, 0xe0, 0x14, 0x34, 0xd2, 0x6d, 0xe9, + 0x7a, 0xec, 0x91, 0x61, 0xa7, 0xe1, 0x81, 0x69, + 0x47, 0x1c, 0xc7, 0xf3 +}; + +static const unsigned char shake256_largemsg_output[] = { + 0x64, 0xea, 0x24, 0x6a, 0xab, 0x80, 0x37, 0x9e, + 0x08, 0xe2, 0x19, 0x9e, 0x09, 0x69, 0xe2, 0xee, + 0x1a, 0x5d, 0xd1, 0x68, 0x68, 0xec, 0x8d, 0x42, + 0xd0, 0xf8, 0xb8, 0x44, 0x74, 0x54, 0x87, 0x3e, +}; + +static EVP_MD_CTX *shake_setup(const char *name) +{ + EVP_MD_CTX *ctx = NULL; + EVP_MD *md = NULL; + + if (!TEST_ptr(md = EVP_MD_fetch(NULL, name, NULL))) + return NULL; + + if (!TEST_ptr(ctx = EVP_MD_CTX_new())) + goto err; + if (!TEST_true(EVP_DigestInit_ex2(ctx, md, NULL))) + goto err; + EVP_MD_free(md); + return ctx; +err: + EVP_MD_free(md); + EVP_MD_CTX_free(ctx); + return NULL; +} + +static int shake_kat_test(void) +{ + int ret = 0; + EVP_MD_CTX *ctx = NULL; + unsigned char out[sizeof(shake256_output)]; + + if (!TEST_ptr(ctx = shake_setup("SHAKE256"))) + return 0; + if (!TEST_true(EVP_DigestUpdate(ctx, shake256_input, + sizeof(shake256_input))) + || !TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out))) + || !TEST_mem_eq(out, sizeof(out), + shake256_output,sizeof(shake256_output)) + /* Test that a second call to EVP_DigestFinalXOF fails */ + || !TEST_false(EVP_DigestFinalXOF(ctx, out, sizeof(out))) + /* Test that a call to EVP_DigestSqueeze fails */ + || !TEST_false(EVP_DigestSqueeze(ctx, out, sizeof(out)))) + goto err; + ret = 1; +err: + EVP_MD_CTX_free(ctx); + return ret; +} + +static int shake_kat_digestfinal_test(void) +{ + int ret = 0; + unsigned int digest_length = 0; + EVP_MD_CTX *ctx = NULL; + unsigned char out[sizeof(shake256_output)]; + + if (!TEST_ptr(ctx = shake_setup("SHAKE256"))) + return 0; + if (!TEST_true(EVP_DigestUpdate(ctx, shake256_input, + sizeof(shake256_input))) + || !TEST_true(EVP_DigestFinal(ctx, out, &digest_length)) + || !TEST_uint_eq(digest_length, 32) + || !TEST_mem_eq(out, digest_length, + shake256_output, digest_length) + || !TEST_false(EVP_DigestFinalXOF(ctx, out, sizeof(out)))) + goto err; + ret = 1; +err: + EVP_MD_CTX_free(ctx); + return ret; +} + +/* + * Test that EVP_DigestFinal() returns the output length + * set by the OSSL_DIGEST_PARAM_XOFLEN param. + */ +static int shake_kat_digestfinal_xoflen_test(void) +{ + int ret = 0; + unsigned int digest_length = 0; + EVP_MD_CTX *ctx = NULL; + unsigned char out[sizeof(shake256_output)]; + OSSL_PARAM params[2]; + size_t sz = 12; + + if (!TEST_ptr(ctx = shake_setup("SHAKE256"))) + return 0; + + memset(out, 0, sizeof(out)); + params[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &sz); + params[1] = OSSL_PARAM_construct_end(); + + if (!TEST_int_eq(EVP_MD_CTX_set_params(ctx, params), 1) + || !TEST_true(EVP_DigestUpdate(ctx, shake256_input, + sizeof(shake256_input))) + || !TEST_true(EVP_DigestFinal(ctx, out, &digest_length)) + || !TEST_uint_eq(digest_length, (unsigned int)sz) + || !TEST_mem_eq(out, digest_length, + shake256_output, digest_length) + || !TEST_uchar_eq(out[digest_length], 0)) + goto err; + ret = 1; +err: + EVP_MD_CTX_free(ctx); + return ret; +} + +/* + * Test that multiple absorb calls gives the expected result. + * This is a nested test that uses multiple strides for the input. + */ +static int shake_absorb_test(void) +{ + int ret = 0; + EVP_MD_CTX *ctx = NULL; + unsigned char out[sizeof(shake256_largemsg_output)]; + size_t total = sizeof(shake256_largemsg_input); + size_t i, stride, sz; + + if (!TEST_ptr(ctx = shake_setup("SHAKE256"))) + return 0; + + for (stride = 1; stride < total; ++stride) { + sz = 0; + for (i = 0; i < total; i += sz) { + sz += stride; + if ((i + sz) > total) + sz = total - i; + if (!TEST_true(EVP_DigestUpdate(ctx, shake256_largemsg_input + i, + sz))) + goto err; + } + if (!TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out))) + || !TEST_mem_eq(out, sizeof(out), + shake256_largemsg_output, + sizeof(shake256_largemsg_output))) + goto err; + if (!TEST_true(EVP_DigestInit_ex2(ctx, NULL, NULL))) + goto err; + } + ret = 1; +err: + EVP_MD_CTX_free(ctx); + return ret; +} + +/* + * Table containing the size of the output to squeeze for the + * initially call, followed by a size for each subsequent call. + */ +static const struct { + size_t startsz, incsz; +} stride_tests[] = { + { 1, 1 }, + { 1, 136 }, + { 1, 136/2 }, + { 1, 136/2-1 }, + { 1, 136/2+1 }, + { 1, 136*3 }, + { 8, 8 }, + { 9, 9 }, + { 10, 10 }, + { 136/2 - 1, 136 }, + { 136/2 - 1, 136-1 }, + { 136/2 - 1, 136+1 }, + { 136/2, 136 }, + { 136/2, 136-1 }, + { 136/2, 136+1 }, + { 136/2 + 1, 136 }, + { 136/2 + 1, 136-1 }, + { 136/2 + 1, 136+1 }, + { 136, 2 }, + { 136, 136 }, + { 136-1, 136 }, + { 136-1, 136-1 }, + { 136-1, 136+1 }, + { 136+1, 136 }, + { 136+1, 136-1 }, + { 136+1, 136+1 }, + { 136*3, 136 }, + { 136*3, 136 + 1 }, + { 136*3, 136 - 1 }, + { 136*3, 136/2 }, + { 136*3, 136/2 + 1 }, + { 136*3, 136/2 - 1 }, +}; + +/* + * Helper to do multiple squeezes of output data using SHAKE256. + * tst is an index into the stride_tests[] containing an initial starting + * output length, followed by a second output length to use for all remaining + * squeezes. expected_outlen contains the total number of bytes to squeeze. + * in and inlen represent the input to absorb. expected_out and expected_outlen + * represent the expected output. + */ +static int do_shake_squeeze_test(int tst, + const unsigned char *in, size_t inlen, + const unsigned char *expected_out, + size_t expected_outlen) +{ + int ret = 0; + EVP_MD_CTX *ctx = NULL; + unsigned char *out = NULL; + size_t i = 0, sz = stride_tests[tst].startsz; + + if (!TEST_ptr(ctx = shake_setup("SHAKE256"))) + return 0; + if (!TEST_ptr(out = OPENSSL_malloc(expected_outlen))) + goto err; + if (!TEST_true(EVP_DigestUpdate(ctx, in, inlen))) + goto err; + + while (i < expected_outlen) { + if ((i + sz) > expected_outlen) + sz = expected_outlen - i; + if (!TEST_true(EVP_DigestSqueeze(ctx, out + i, sz))) + goto err; + i += sz; + sz = stride_tests[tst].incsz; + } + if (!TEST_mem_eq(out, expected_outlen, expected_out, expected_outlen)) + goto err; + ret = 1; +err: + OPENSSL_free(out); + EVP_MD_CTX_free(ctx); + return ret; +} + +static int shake_squeeze_kat_test(int tst) +{ + return do_shake_squeeze_test(tst, shake256_input, sizeof(shake256_input), + shake256_output, sizeof(shake256_output)); +} + +/* + * Generate some random input to absorb, and then + * squeeze it out in one operation to get a expected + * output. Use this to test that multiple squeeze calls + * on the same input gives the same output. + */ +static int shake_squeeze_large_test(int tst) +{ + int ret = 0; + EVP_MD_CTX *ctx = NULL; + unsigned char msg[16]; + unsigned char out[2000]; + + if (!TEST_int_gt(RAND_bytes(msg, sizeof(msg)), 0) + || !TEST_ptr(ctx = shake_setup("SHAKE256")) + || !TEST_true(EVP_DigestUpdate(ctx, msg, sizeof(msg))) + || !TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out)))) + goto err; + + ret = do_shake_squeeze_test(tst, msg, sizeof(msg), out, sizeof(out)); +err: + EVP_MD_CTX_free(ctx); + return ret; +} + +static const size_t dupoffset_tests[] = { + 1, 135, 136, 137, 136*3-1, 136*3, 136*3+1 +}; + +/* Helper function to test that EVP_MD_CTX_dup() copies the internal state */ +static int do_shake_squeeze_dup_test(int tst, const char *alg, + const unsigned char *in, size_t inlen, + const unsigned char *expected_out, + size_t expected_outlen) +{ + int ret = 0; + EVP_MD_CTX *cur, *ctx = NULL, *dupctx = NULL; + unsigned char *out = NULL; + size_t i = 0, sz = 10; + size_t dupoffset = dupoffset_tests[tst]; + + if (!TEST_ptr(ctx = shake_setup(alg))) + return 0; + cur = ctx; + if (!TEST_ptr(out = OPENSSL_malloc(expected_outlen))) + goto err; + if (!TEST_true(EVP_DigestUpdate(ctx, in, inlen))) + goto err; + + while (i < expected_outlen) { + if ((i + sz) > expected_outlen) + sz = expected_outlen - i; + if (!TEST_true(EVP_DigestSqueeze(cur, out + i, sz))) + goto err; + i += sz; + /* At a certain offset we swap to a new ctx that copies the state */ + if (dupctx == NULL && i >= dupoffset) { + if (!TEST_ptr(dupctx = EVP_MD_CTX_dup(ctx))) + goto err; + cur = dupctx; + } + } + if (!TEST_mem_eq(out, expected_outlen, expected_out, expected_outlen)) + goto err; + ret = 1; +err: + OPENSSL_free(out); + EVP_MD_CTX_free(ctx); + EVP_MD_CTX_free(dupctx); + return ret; +} + +/* Test that the internal state can be copied */ +static int shake_squeeze_dup_test(int tst) +{ + int ret = 0; + EVP_MD_CTX *ctx = NULL; + unsigned char msg[16]; + unsigned char out[1000]; + const char *alg = "SHAKE128"; + + if (!TEST_int_gt(RAND_bytes(msg, sizeof(msg)), 0) + || !TEST_ptr(ctx = shake_setup(alg)) + || !TEST_true(EVP_DigestUpdate(ctx, msg, sizeof(msg))) + || !TEST_true(EVP_DigestFinalXOF(ctx, out, sizeof(out)))) + goto err; + + ret = do_shake_squeeze_dup_test(tst, alg, msg, sizeof(msg), + out, sizeof(out)); +err: + EVP_MD_CTX_free(ctx); + return ret; +} + +int setup_tests(void) +{ + ADD_TEST(shake_kat_test); + ADD_TEST(shake_kat_digestfinal_test); + ADD_TEST(shake_kat_digestfinal_xoflen_test); + ADD_TEST(shake_absorb_test); + ADD_ALL_TESTS(shake_squeeze_kat_test, OSSL_NELEM(stride_tests)); + ADD_ALL_TESTS(shake_squeeze_large_test, OSSL_NELEM(stride_tests)); + ADD_ALL_TESTS(shake_squeeze_dup_test, OSSL_NELEM(dupoffset_tests)); + return 1; +} diff --git a/test/recipes/30-test_evp_xof.t b/test/recipes/30-test_evp_xof.t new file mode 100644 index 0000000000..c3dd6062de --- /dev/null +++ b/test/recipes/30-test_evp_xof.t @@ -0,0 +1,12 @@ +#! /usr/bin/env perl +# Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +use OpenSSL::Test::Simple; + +simple_test("test_evp_xof", "evp_xof_test"); diff --git a/util/libcrypto.num b/util/libcrypto.num index a16f93db47..b64b0ddc5c 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5536,3 +5536,4 @@ X509_STORE_CTX_set_get_crl ? 3_2_0 EXIST::FUNCTION: X509_STORE_CTX_set_current_reasons ? 3_2_0 EXIST::FUNCTION: OSSL_STORE_delete ? 3_2_0 EXIST::FUNCTION: BIO_ADDR_copy ? 3_2_0 EXIST::FUNCTION:SOCK +EVP_DigestSqueeze ? 3_2_0 EXIST::FUNCTION: -- Gitee From f36b4b4f3046a5492f2abe04c5670d6144979803 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 Sep 2023 11:18:18 +0200 Subject: [PATCH 47/54] Support multiple calls of low level SHA3_squeeze() for s390x. The low level SHA3_Squeeze() function needed to change slightly so that it can handle multiple squeezes. Support this on s390x architecture as well. Signed-off-by: Holger Dengler Reviewed-by: Shane Lontis Reviewed-by: Todd Short Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22221) Signed-off-by: fly2x --- crypto/sha/asm/keccak1600-s390x.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crypto/sha/asm/keccak1600-s390x.pl b/crypto/sha/asm/keccak1600-s390x.pl index 86233c7e38..7d5ebde117 100755 --- a/crypto/sha/asm/keccak1600-s390x.pl +++ b/crypto/sha/asm/keccak1600-s390x.pl @@ -472,7 +472,7 @@ SHA3_absorb: .size SHA3_absorb,.-SHA3_absorb ___ } -{ my ($A_flat,$out,$len,$bsz) = map("%r$_",(2..5)); +{ my ($A_flat,$out,$len,$bsz,$next) = map("%r$_",(2..6)); $code.=<<___; .globl SHA3_squeeze @@ -484,6 +484,7 @@ SHA3_squeeze: lghi %r14,8 st${g} $bsz,5*$SIZE_T($sp) la %r1,0($A_flat) + cijne $next,0,.Lnext_block j .Loop_squeeze @@ -501,6 +502,7 @@ SHA3_squeeze: brct $bsz,.Loop_squeeze # bsz-- +.Lnext_block: stm${g} $out,$len,3*$SIZE_T($sp) bras %r14,.LKeccakF1600 lm${g} $out,$bsz,3*$SIZE_T($sp) -- Gitee From b508487f9327eadba9183f53ec4511cc63d83728 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 Sep 2023 21:54:34 +0200 Subject: [PATCH 48/54] Add xof state handing for generic sha3 absorb. The digest life-cycle diagram specifies state transitions to `updated` (aka XOF_STATE_ABSORB) only from `initialised` and `updated`. Add this checking to the generic sha3 absorb implementation. Signed-off-by: Holger Dengler Reviewed-by: Shane Lontis Reviewed-by: Todd Short Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22221) Signed-off-by: fly2x --- providers/implementations/digests/sha3_prov.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 19576c7190..13735153f2 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -143,6 +143,10 @@ static size_t generic_sha3_absorb(void *vctx, const void *inp, size_t len) { KECCAK1600_CTX *ctx = vctx; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + ctx->xof_state = XOF_STATE_ABSORB; return SHA3_absorb(ctx->A, inp, len, ctx->block_size); } -- Gitee From 5e22c08dbc50e858c20bcd1708bd55b8b5caec6c Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 Sep 2023 15:36:23 +0200 Subject: [PATCH 49/54] Fix state handling of sha3_absorb for s390x. The digest life-cycle state diagram has been updated for XOF. Fix the state handling in s390x_sha3_aborb() according to the updated state diagram. Signed-off-by: Holger Dengler Reviewed-by: Shane Lontis Reviewed-by: Todd Short Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22221) Signed-off-by: fly2x --- providers/implementations/digests/sha3_prov.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 13735153f2..85370912f6 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -188,6 +188,10 @@ static size_t s390x_sha3_absorb(void *vctx, const void *inp, size_t len) KECCAK1600_CTX *ctx = vctx; size_t rem = len % ctx->block_size; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + ctx->xof_state = XOF_STATE_ABSORB; s390x_kimd(inp, len - rem, ctx->pad, ctx->A); return rem; } -- Gitee From 66d2ef4abb1a6848a0003b86ff72b9a02ec31a3b Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 Sep 2023 15:36:59 +0200 Subject: [PATCH 50/54] Fix state handling of sha3_final for s390x. The digest life-cycle state diagram has been updated for XOF. Fix the state handling in s390x_sha3_final() according to the updated state diagram. Signed-off-by: Holger Dengler Reviewed-by: Shane Lontis Reviewed-by: Todd Short Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22221) Signed-off-by: fly2x --- providers/implementations/digests/sha3_prov.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 85370912f6..5cf9791bbf 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -202,6 +202,10 @@ static int s390x_sha3_final(void *vctx, unsigned char *out, size_t outlen) if (!ossl_prov_is_running()) return 0; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + ctx->xof_state = XOF_STATE_FINAL; s390x_klmd(ctx->buf, ctx->bufsz, NULL, 0, ctx->pad, ctx->A); memcpy(out, ctx->A, outlen); return 1; -- Gitee From a3cb54a084432ab50e4e2bdc5437b369528dade5 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 Sep 2023 15:37:29 +0200 Subject: [PATCH 51/54] Fix state handling of shake_final for s390x. The digest life-cycle state diagram has been updated for XOF. Fix the state handling in s390x_shake_final() according to the updated state diagram. Signed-off-by: Holger Dengler Reviewed-by: Shane Lontis Reviewed-by: Todd Short Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22221) Signed-off-by: fly2x --- providers/implementations/digests/sha3_prov.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 5cf9791bbf..34620cf95a 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -217,6 +217,10 @@ static int s390x_shake_final(void *vctx, unsigned char *out, size_t outlen) if (!ossl_prov_is_running()) return 0; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + ctx->xof_state = XOF_STATE_FINAL; s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, ctx->pad, ctx->A); return 1; } -- Gitee From c9ecdfdac87bcf951aa6a5c0ec9173d484f16f79 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 Sep 2023 15:43:18 +0200 Subject: [PATCH 52/54] Fix state handling of keccak_final for s390x. The digest life-cycle state diagram has been updated for XOF. Fix the state handling in s390x_keccac_final() according to the updated state diagram. Signed-off-by: Holger Dengler Reviewed-by: Shane Lontis Reviewed-by: Todd Short Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22221) Signed-off-by: fly2x --- providers/implementations/digests/sha3_prov.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index 34620cf95a..f691273baf 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -235,6 +235,10 @@ static int s390x_keccakc_final(void *vctx, unsigned char *out, size_t outlen, if (!ossl_prov_is_running()) return 0; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + ctx->xof_state = XOF_STATE_FINAL; if (outlen == 0) return 1; memset(ctx->buf + num, 0, bsz - num); -- Gitee From f58b26298b1a4248598a72bd5e46ed34d590c44e Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 Sep 2023 15:40:47 +0200 Subject: [PATCH 53/54] Support EVP_DigestSqueeze() for in the digest provider for s390x. The new EVP_DigestSqueeze() API requires changes to all keccak-based digest provider implementations. Update the s390x-part of the SHA3 digest provider. Squeeze for SHA3 is not supported, so add an empty function pointer (NULL). Signed-off-by: Holger Dengler Reviewed-by: Shane Lontis Reviewed-by: Todd Short Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22221) Signed-off-by: fly2x --- providers/implementations/digests/sha3_prov.c | 103 +++++++++++++++++- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/providers/implementations/digests/sha3_prov.c b/providers/implementations/digests/sha3_prov.c index f691273baf..2fd0f928e7 100644 --- a/providers/implementations/digests/sha3_prov.c +++ b/providers/implementations/digests/sha3_prov.c @@ -225,6 +225,45 @@ static int s390x_shake_final(void *vctx, unsigned char *out, size_t outlen) return 1; } +static int s390x_shake_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + KECCAK1600_CTX *ctx = vctx; + size_t len; + + if (!ossl_prov_is_running()) + return 0; + if (ctx->xof_state == XOF_STATE_FINAL) + return 0; + /* + * On the first squeeze call, finish the absorb process (incl. padding). + */ + if (ctx->xof_state != XOF_STATE_SQUEEZE) { + ctx->xof_state = XOF_STATE_SQUEEZE; + s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, ctx->pad, ctx->A); + ctx->bufsz = outlen % ctx->block_size; + /* reuse ctx->bufsz to count bytes squeezed from current sponge */ + return 1; + } + ctx->xof_state = XOF_STATE_SQUEEZE; + if (ctx->bufsz != 0) { + len = ctx->block_size - ctx->bufsz; + if (outlen < len) + len = outlen; + memcpy(out, (char *)ctx->A + ctx->bufsz, len); + out += len; + outlen -= len; + ctx->bufsz += len; + if (ctx->bufsz == ctx->block_size) + ctx->bufsz = 0; + } + if (outlen == 0) + return 1; + s390x_klmd(NULL, 0, out, outlen, ctx->pad | S390X_KLMD_PS, ctx->A); + ctx->bufsz = outlen % ctx->block_size; + + return 1; +} + static int s390x_keccakc_final(void *vctx, unsigned char *out, size_t outlen, int padding) { @@ -264,28 +303,86 @@ static int s390x_kmac_final(void *vctx, unsigned char *out, size_t outlen) return s390x_keccakc_final(vctx, out, outlen, 0x04); } +static int s390x_keccakc_squeeze(void *vctx, unsigned char *out, size_t outlen, + int padding) +{ + KECCAK1600_CTX *ctx = vctx; + size_t len; + + if (!ossl_prov_is_running()) + return 0; + if (ctx->xof_state == XOF_STATE_FINAL) + return 0; + /* + * On the first squeeze call, finish the absorb process + * by adding the trailing padding and then doing + * a final absorb. + */ + if (ctx->xof_state != XOF_STATE_SQUEEZE) { + len = ctx->block_size - ctx->bufsz; + memset(ctx->buf + ctx->bufsz, 0, len); + ctx->buf[ctx->bufsz] = padding; + ctx->buf[ctx->block_size - 1] |= 0x80; + s390x_kimd(ctx->buf, ctx->block_size, ctx->pad, ctx->A); + ctx->bufsz = 0; + /* reuse ctx->bufsz to count bytes squeezed from current sponge */ + } + if (ctx->bufsz != 0 || ctx->xof_state != XOF_STATE_SQUEEZE) { + len = ctx->block_size - ctx->bufsz; + if (outlen < len) + len = outlen; + memcpy(out, (char *)ctx->A + ctx->bufsz, len); + out += len; + outlen -= len; + ctx->bufsz += len; + if (ctx->bufsz == ctx->block_size) + ctx->bufsz = 0; + } + ctx->xof_state = XOF_STATE_SQUEEZE; + if (outlen == 0) + return 1; + s390x_klmd(NULL, 0, out, outlen, ctx->pad | S390X_KLMD_PS, ctx->A); + ctx->bufsz = outlen % ctx->block_size; + + return 1; +} + +static int s390x_keccak_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + return s390x_keccakc_squeeze(vctx, out, outlen, 0x01); +} + +static int s390x_kmac_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + return s390x_keccakc_squeeze(vctx, out, outlen, 0x04); +} + static PROV_SHA3_METHOD sha3_s390x_md = { s390x_sha3_absorb, - s390x_sha3_final + s390x_sha3_final, + NULL, }; static PROV_SHA3_METHOD keccak_s390x_md = { s390x_sha3_absorb, s390x_keccak_final, + s390x_keccak_squeeze, }; static PROV_SHA3_METHOD shake_s390x_md = { s390x_sha3_absorb, - s390x_shake_final + s390x_shake_final, + s390x_shake_squeeze, }; static PROV_SHA3_METHOD kmac_s390x_md = { s390x_sha3_absorb, - s390x_kmac_final + s390x_kmac_final, + s390x_kmac_squeeze, }; # define SHAKE_SET_MD(uname, typ) \ -- Gitee From cc62c4a221e54b3dd13f303e5671cbc77d227009 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 10 Nov 2023 16:00:18 +0100 Subject: [PATCH 54/54] Fix CI breakage due to ABIDIFF failure Also sync libcrypto.num and libssl.num with 3.2 branch and fix the EVP_DigestSqueeze symbol version. Reviewed-by: Matt Caswell Reviewed-by: Hugo Landau Reviewed-by: Dmitry Belyavskiy (Merged from https://github.com/openssl/openssl/pull/22688) Signed-off-by: fly2x --- .github/workflows/libcrypto-abi.xml | 1 + util/libcrypto.num | 220 ++++++++++++++-------------- util/libssl.num | 114 +++++++------- 3 files changed, 168 insertions(+), 167 deletions(-) diff --git a/.github/workflows/libcrypto-abi.xml b/.github/workflows/libcrypto-abi.xml index d82bc8b320..6e93efd7d4 100644 --- a/.github/workflows/libcrypto-abi.xml +++ b/.github/workflows/libcrypto-abi.xml @@ -1780,6 +1780,7 @@ + diff --git a/util/libcrypto.num b/util/libcrypto.num index b64b0ddc5c..c9941a383b 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5427,113 +5427,113 @@ EVP_PKEY_get0_provider 5554 3_0_0 EXIST::FUNCTION: EVP_PKEY_CTX_get0_provider 5555 3_0_0 EXIST::FUNCTION: OPENSSL_strcasecmp 5556 3_0_3 EXIST::FUNCTION: OPENSSL_strncasecmp 5557 3_0_3 EXIST::FUNCTION: -EVP_RAND_CTX_up_ref ? 3_1_0 EXIST::FUNCTION: -RAND_set0_public ? 3_1_0 EXIST::FUNCTION: -RAND_set0_private ? 3_1_0 EXIST::FUNCTION: -BN_are_coprime ? 3_1_0 EXIST::FUNCTION: -X509_PUBKEY_set0_public_key ? 3_2_0 EXIST::FUNCTION: -OSSL_STACK_OF_X509_free ? 3_2_0 EXIST::FUNCTION: -OSSL_trace_string ? 3_2_0 EXIST::FUNCTION: -EVP_MD_CTX_dup ? 3_2_0 EXIST::FUNCTION: -EVP_CIPHER_CTX_dup ? 3_2_0 EXIST::FUNCTION: -BN_signed_bin2bn ? 3_2_0 EXIST::FUNCTION: -BN_signed_bn2bin ? 3_2_0 EXIST::FUNCTION: -BN_signed_lebin2bn ? 3_2_0 EXIST::FUNCTION: -BN_signed_bn2lebin ? 3_2_0 EXIST::FUNCTION: -BN_signed_native2bn ? 3_2_0 EXIST::FUNCTION: -BN_signed_bn2native ? 3_2_0 EXIST::FUNCTION: -ASYNC_set_mem_functions ? 3_2_0 EXIST::FUNCTION: -ASYNC_get_mem_functions ? 3_2_0 EXIST::FUNCTION: -BIO_ADDR_dup ? 3_2_0 EXIST::FUNCTION:SOCK -OSSL_CMP_ITAV_new_caCerts ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_ITAV_get0_caCerts ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_get1_caCerts ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_ITAV_new_rootCaCert ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_ITAV_get0_rootCaCert ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_ITAV_new_rootCaKeyUpdate ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_ITAV_get0_rootCaKeyUpdate ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_get1_rootCaKeyUpdate ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_CTX_get0_libctx ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_CTX_get0_propq ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_CTX_reset_geninfo_ITAVs ? 3_0_8 EXIST::FUNCTION:CMP -OSSL_CMP_CTX_get0_validatedSrvCert ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_CTX_set1_serialNumber ? 3_2_0 EXIST::FUNCTION:CMP -OSSL_CMP_MSG_update_recipNonce ? 3_0_9 EXIST::FUNCTION:CMP -OSSL_CRMF_CERTTEMPLATE_get0_publicKey ? 3_2_0 EXIST::FUNCTION:CRMF -CMS_final_digest ? 3_2_0 EXIST::FUNCTION:CMS -CMS_EnvelopedData_it ? 3_2_0 EXIST::FUNCTION:CMS -CMS_EnvelopedData_decrypt ? 3_2_0 EXIST::FUNCTION:CMS -CMS_SignedData_free ? 3_2_0 EXIST::FUNCTION:CMS -CMS_SignedData_new ? 3_2_0 EXIST::FUNCTION:CMS -CMS_SignedData_verify ? 3_2_0 EXIST::FUNCTION:CMS -BIO_s_dgram_mem ? 3_2_0 EXIST::FUNCTION:DGRAM -BIO_recvmmsg ? 3_2_0 EXIST::FUNCTION: -BIO_sendmmsg ? 3_2_0 EXIST::FUNCTION: -BIO_meth_set_sendmmsg ? 3_2_0 EXIST::FUNCTION: -BIO_meth_get_sendmmsg ? 3_2_0 EXIST::FUNCTION: -BIO_meth_set_recvmmsg ? 3_2_0 EXIST::FUNCTION: -BIO_meth_get_recvmmsg ? 3_2_0 EXIST::FUNCTION: -BIO_err_is_non_fatal ? 3_2_0 EXIST::FUNCTION:SOCK -BIO_s_dgram_pair ? 3_2_0 EXIST::FUNCTION:DGRAM -BIO_new_bio_dgram_pair ? 3_2_0 EXIST::FUNCTION:DGRAM -EVP_PKEY_auth_encapsulate_init ? 3_2_0 EXIST::FUNCTION: -EVP_PKEY_auth_decapsulate_init ? 3_2_0 EXIST::FUNCTION: -PKCS12_SAFEBAG_set0_attrs ? 3_2_0 EXIST::FUNCTION: -PKCS12_create_ex2 ? 3_2_0 EXIST::FUNCTION: -OSSL_sleep ? 3_2_0 EXIST::FUNCTION: -OSSL_get_thread_support_flags ? 3_2_0 EXIST::FUNCTION: -OSSL_set_max_threads ? 3_2_0 EXIST::FUNCTION: -OSSL_get_max_threads ? 3_2_0 EXIST::FUNCTION: -COMP_brotli ? 3_2_0 EXIST::FUNCTION:COMP -COMP_brotli_oneshot ? 3_2_0 EXIST::FUNCTION:COMP -BIO_f_brotli ? 3_2_0 EXIST::FUNCTION:COMP -COMP_zstd ? 3_2_0 EXIST::FUNCTION:COMP -COMP_zstd_oneshot ? 3_2_0 EXIST::FUNCTION:COMP -BIO_f_zstd ? 3_2_0 EXIST::FUNCTION:COMP -d2i_PUBKEY_ex_fp ? 3_2_0 EXIST::FUNCTION:STDIO -d2i_PUBKEY_ex_bio ? 3_2_0 EXIST::FUNCTION: -COMP_zlib_oneshot ? 3_2_0 EXIST::FUNCTION:COMP -OSSL_HPKE_keygen ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_suite_check ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_get_grease_value ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_str2suite ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_free ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_new ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_set1_authpriv ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_set1_authpub ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_set1_psk ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_set1_ikme ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_get_ciphertext_size ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_get_public_encap_size ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_export ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_encap ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_decap ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_seal ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_open ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_get_seq ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_CTX_set_seq ? 3_2_0 EXIST::FUNCTION: -OSSL_HPKE_get_recommended_ikmelen ? 3_2_0 EXIST::FUNCTION: -OSSL_PROVIDER_get0_default_search_path ? 3_2_0 EXIST::FUNCTION: -BIO_get_rpoll_descriptor ? 3_2_0 EXIST::FUNCTION: -BIO_get_wpoll_descriptor ? 3_2_0 EXIST::FUNCTION: -ASN1_item_unpack_ex ? 3_2_0 EXIST::FUNCTION: -PKCS12_SAFEBAG_get1_cert_ex ? 3_2_0 EXIST::FUNCTION: -PKCS12_SAFEBAG_get1_crl_ex ? 3_2_0 EXIST::FUNCTION: -EC_GROUP_to_params ? 3_2_0 EXIST::FUNCTION:EC -X509_STORE_CTX_init_rpk ? 3_2_0 EXIST::FUNCTION: -X509_STORE_CTX_get0_rpk ? 3_2_0 EXIST::FUNCTION: -X509_STORE_CTX_set0_rpk ? 3_2_0 EXIST::FUNCTION: -CRYPTO_atomic_load_int ? 3_2_0 EXIST::FUNCTION: -OSSL_ERR_STATE_new ? 3_2_0 EXIST::FUNCTION: -OSSL_ERR_STATE_save ? 3_2_0 EXIST::FUNCTION: -OSSL_ERR_STATE_restore ? 3_2_0 EXIST::FUNCTION: -OSSL_ERR_STATE_free ? 3_2_0 EXIST::FUNCTION: -ERR_count_to_mark ? 3_2_0 EXIST::FUNCTION: -OSSL_PROVIDER_load_ex ? 3_2_0 EXIST::FUNCTION: -OSSL_PROVIDER_try_load_ex ? 3_2_0 EXIST::FUNCTION: -OSSL_ERR_STATE_save_to_mark ? 3_2_0 EXIST::FUNCTION: -X509_STORE_CTX_set_get_crl ? 3_2_0 EXIST::FUNCTION: -X509_STORE_CTX_set_current_reasons ? 3_2_0 EXIST::FUNCTION: -OSSL_STORE_delete ? 3_2_0 EXIST::FUNCTION: -BIO_ADDR_copy ? 3_2_0 EXIST::FUNCTION:SOCK -EVP_DigestSqueeze ? 3_2_0 EXIST::FUNCTION: +EVP_RAND_CTX_up_ref 5558 3_1_0 EXIST::FUNCTION: +RAND_set0_public 5559 3_1_0 EXIST::FUNCTION: +RAND_set0_private 5560 3_1_0 EXIST::FUNCTION: +BN_are_coprime 5561 3_1_0 EXIST::FUNCTION: +X509_PUBKEY_set0_public_key 5562 3_2_0 EXIST::FUNCTION: +OSSL_STACK_OF_X509_free 5563 3_2_0 EXIST::FUNCTION: +OSSL_trace_string 5564 3_2_0 EXIST::FUNCTION: +EVP_MD_CTX_dup 5565 3_2_0 EXIST::FUNCTION: +EVP_CIPHER_CTX_dup 5566 3_2_0 EXIST::FUNCTION: +BN_signed_bin2bn 5567 3_2_0 EXIST::FUNCTION: +BN_signed_bn2bin 5568 3_2_0 EXIST::FUNCTION: +BN_signed_lebin2bn 5569 3_2_0 EXIST::FUNCTION: +BN_signed_bn2lebin 5570 3_2_0 EXIST::FUNCTION: +BN_signed_native2bn 5571 3_2_0 EXIST::FUNCTION: +BN_signed_bn2native 5572 3_2_0 EXIST::FUNCTION: +ASYNC_set_mem_functions 5573 3_2_0 EXIST::FUNCTION: +ASYNC_get_mem_functions 5574 3_2_0 EXIST::FUNCTION: +BIO_ADDR_dup 5575 3_2_0 EXIST::FUNCTION:SOCK +OSSL_CMP_ITAV_new_caCerts 5576 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_ITAV_get0_caCerts 5577 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_get1_caCerts 5578 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_ITAV_new_rootCaCert 5579 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_ITAV_get0_rootCaCert 5580 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_ITAV_new_rootCaKeyUpdate 5581 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_ITAV_get0_rootCaKeyUpdate 5582 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_get1_rootCaKeyUpdate 5583 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_CTX_get0_libctx 5584 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_CTX_get0_propq 5585 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_CTX_reset_geninfo_ITAVs 5586 3_0_8 EXIST::FUNCTION:CMP +OSSL_CMP_CTX_get0_validatedSrvCert 5587 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_CTX_set1_serialNumber 5588 3_2_0 EXIST::FUNCTION:CMP +OSSL_CMP_MSG_update_recipNonce 5589 3_0_9 EXIST::FUNCTION:CMP +OSSL_CRMF_CERTTEMPLATE_get0_publicKey 5590 3_2_0 EXIST::FUNCTION:CRMF +CMS_final_digest 5591 3_2_0 EXIST::FUNCTION:CMS +CMS_EnvelopedData_it 5592 3_2_0 EXIST::FUNCTION:CMS +CMS_EnvelopedData_decrypt 5593 3_2_0 EXIST::FUNCTION:CMS +CMS_SignedData_free 5594 3_2_0 EXIST::FUNCTION:CMS +CMS_SignedData_new 5595 3_2_0 EXIST::FUNCTION:CMS +CMS_SignedData_verify 5596 3_2_0 EXIST::FUNCTION:CMS +BIO_s_dgram_mem 5597 3_2_0 EXIST::FUNCTION:DGRAM +BIO_recvmmsg 5598 3_2_0 EXIST::FUNCTION: +BIO_sendmmsg 5599 3_2_0 EXIST::FUNCTION: +BIO_meth_set_sendmmsg 5600 3_2_0 EXIST::FUNCTION: +BIO_meth_get_sendmmsg 5601 3_2_0 EXIST::FUNCTION: +BIO_meth_set_recvmmsg 5602 3_2_0 EXIST::FUNCTION: +BIO_meth_get_recvmmsg 5603 3_2_0 EXIST::FUNCTION: +BIO_err_is_non_fatal 5604 3_2_0 EXIST::FUNCTION:SOCK +BIO_s_dgram_pair 5605 3_2_0 EXIST::FUNCTION:DGRAM +BIO_new_bio_dgram_pair 5606 3_2_0 EXIST::FUNCTION:DGRAM +EVP_PKEY_auth_encapsulate_init 5607 3_2_0 EXIST::FUNCTION: +EVP_PKEY_auth_decapsulate_init 5608 3_2_0 EXIST::FUNCTION: +PKCS12_SAFEBAG_set0_attrs 5609 3_2_0 EXIST::FUNCTION: +PKCS12_create_ex2 5610 3_2_0 EXIST::FUNCTION: +OSSL_sleep 5611 3_2_0 EXIST::FUNCTION: +OSSL_get_thread_support_flags 5612 3_2_0 EXIST::FUNCTION: +OSSL_set_max_threads 5613 3_2_0 EXIST::FUNCTION: +OSSL_get_max_threads 5614 3_2_0 EXIST::FUNCTION: +COMP_brotli 5615 3_2_0 EXIST::FUNCTION:COMP +COMP_brotli_oneshot 5616 3_2_0 EXIST::FUNCTION:COMP +BIO_f_brotli 5617 3_2_0 EXIST::FUNCTION:COMP +COMP_zstd 5618 3_2_0 EXIST::FUNCTION:COMP +COMP_zstd_oneshot 5619 3_2_0 EXIST::FUNCTION:COMP +BIO_f_zstd 5620 3_2_0 EXIST::FUNCTION:COMP +d2i_PUBKEY_ex_fp 5621 3_2_0 EXIST::FUNCTION:STDIO +d2i_PUBKEY_ex_bio 5622 3_2_0 EXIST::FUNCTION: +COMP_zlib_oneshot 5623 3_2_0 EXIST::FUNCTION:COMP +OSSL_HPKE_keygen 5624 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_suite_check 5625 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_get_grease_value 5626 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_str2suite 5627 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_free 5628 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_new 5629 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_set1_authpriv 5630 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_set1_authpub 5631 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_set1_psk 5632 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_set1_ikme 5633 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_get_ciphertext_size 5634 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_get_public_encap_size 5635 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_export 5636 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_encap 5637 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_decap 5638 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_seal 5639 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_open 5640 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_get_seq 5641 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_CTX_set_seq 5642 3_2_0 EXIST::FUNCTION: +OSSL_HPKE_get_recommended_ikmelen 5643 3_2_0 EXIST::FUNCTION: +OSSL_PROVIDER_get0_default_search_path 5644 3_2_0 EXIST::FUNCTION: +BIO_get_rpoll_descriptor 5645 3_2_0 EXIST::FUNCTION: +BIO_get_wpoll_descriptor 5646 3_2_0 EXIST::FUNCTION: +ASN1_item_unpack_ex 5647 3_2_0 EXIST::FUNCTION: +PKCS12_SAFEBAG_get1_cert_ex 5648 3_2_0 EXIST::FUNCTION: +PKCS12_SAFEBAG_get1_crl_ex 5649 3_2_0 EXIST::FUNCTION: +EC_GROUP_to_params 5650 3_2_0 EXIST::FUNCTION:EC +X509_STORE_CTX_init_rpk 5651 3_2_0 EXIST::FUNCTION: +X509_STORE_CTX_get0_rpk 5652 3_2_0 EXIST::FUNCTION: +X509_STORE_CTX_set0_rpk 5653 3_2_0 EXIST::FUNCTION: +CRYPTO_atomic_load_int 5654 3_2_0 EXIST::FUNCTION: +OSSL_ERR_STATE_new 5655 3_2_0 EXIST::FUNCTION: +OSSL_ERR_STATE_save 5656 3_2_0 EXIST::FUNCTION: +OSSL_ERR_STATE_restore 5657 3_2_0 EXIST::FUNCTION: +OSSL_ERR_STATE_free 5658 3_2_0 EXIST::FUNCTION: +ERR_count_to_mark 5659 3_2_0 EXIST::FUNCTION: +OSSL_PROVIDER_load_ex 5660 3_2_0 EXIST::FUNCTION: +OSSL_PROVIDER_try_load_ex 5661 3_2_0 EXIST::FUNCTION: +OSSL_ERR_STATE_save_to_mark 5662 3_2_0 EXIST::FUNCTION: +X509_STORE_CTX_set_get_crl 5663 3_2_0 EXIST::FUNCTION: +X509_STORE_CTX_set_current_reasons 5664 3_2_0 EXIST::FUNCTION: +OSSL_STORE_delete 5665 3_2_0 EXIST::FUNCTION: +BIO_ADDR_copy 5666 3_2_0 EXIST::FUNCTION:SOCK +EVP_DigestSqueeze ? 3_3_0 EXIST::FUNCTION: diff --git a/util/libssl.num b/util/libssl.num index 225064943b..22b31fb012 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -520,60 +520,60 @@ SSL_load_client_CA_file_ex 520 3_0_0 EXIST::FUNCTION: SSL_set0_tmp_dh_pkey 521 3_0_0 EXIST::FUNCTION: SSL_CTX_set0_tmp_dh_pkey 522 3_0_0 EXIST::FUNCTION: SSL_group_to_name 523 3_0_0 EXIST::FUNCTION: -SSL_client_hello_get_extension_order ? 3_2_0 EXIST::FUNCTION: -OSSL_QUIC_client_method ? 3_2_0 EXIST::FUNCTION:QUIC -OSSL_QUIC_client_thread_method ? 3_2_0 EXIST::FUNCTION:QUIC -SSL_CTX_set1_cert_comp_preference ? 3_2_0 EXIST::FUNCTION: -SSL_set1_cert_comp_preference ? 3_2_0 EXIST::FUNCTION: -SSL_CTX_compress_certs ? 3_2_0 EXIST::FUNCTION: -SSL_compress_certs ? 3_2_0 EXIST::FUNCTION: -SSL_CTX_set1_compressed_cert ? 3_2_0 EXIST::FUNCTION: -SSL_set1_compressed_cert ? 3_2_0 EXIST::FUNCTION: -SSL_CTX_get1_compressed_cert ? 3_2_0 EXIST::FUNCTION: -SSL_get1_compressed_cert ? 3_2_0 EXIST::FUNCTION: -SSL_get_rpoll_descriptor ? 3_2_0 EXIST::FUNCTION: -SSL_get_wpoll_descriptor ? 3_2_0 EXIST::FUNCTION: -SSL_set_blocking_mode ? 3_2_0 EXIST::FUNCTION: -SSL_get_blocking_mode ? 3_2_0 EXIST::FUNCTION: -SSL_set1_initial_peer_addr ? 3_2_0 EXIST::FUNCTION: -SSL_net_read_desired ? 3_2_0 EXIST::FUNCTION: -SSL_net_write_desired ? 3_2_0 EXIST::FUNCTION: -SSL_shutdown_ex ? 3_2_0 EXIST::FUNCTION: -SSL_stream_conclude ? 3_2_0 EXIST::FUNCTION: -SSL_inject_net_dgram ? 3_2_0 EXIST::FUNCTION:QUIC -SSL_get0_peer_rpk ? 3_2_0 EXIST::FUNCTION: -SSL_SESSION_get0_peer_rpk ? 3_2_0 EXIST::FUNCTION: -SSL_set1_client_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_get0_client_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_set1_server_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_get0_server_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_CTX_set1_client_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_CTX_get0_client_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_CTX_set1_server_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_CTX_get0_server_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_get_negotiated_client_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_get_negotiated_server_cert_type ? 3_2_0 EXIST::FUNCTION: -SSL_add_expected_rpk ? 3_2_0 EXIST::FUNCTION: -d2i_SSL_SESSION_ex ? 3_2_0 EXIST::FUNCTION: -SSL_is_tls ? 3_2_0 EXIST::FUNCTION: -SSL_is_quic ? 3_2_0 EXIST::FUNCTION: -SSL_get_handshake_rtt ? 3_2_0 EXIST::FUNCTION: -SSL_new_stream ? 3_2_0 EXIST::FUNCTION: -SSL_get0_connection ? 3_2_0 EXIST::FUNCTION: -SSL_is_connection ? 3_2_0 EXIST::FUNCTION: -SSL_get_stream_type ? 3_2_0 EXIST::FUNCTION: -SSL_get_stream_id ? 3_2_0 EXIST::FUNCTION: -SSL_set_default_stream_mode ? 3_2_0 EXIST::FUNCTION: -SSL_accept_stream ? 3_2_0 EXIST::FUNCTION: -SSL_get_accept_stream_queue_len ? 3_2_0 EXIST::FUNCTION: -SSL_stream_reset ? 3_2_0 EXIST::FUNCTION: -SSL_get_stream_read_state ? 3_2_0 EXIST::FUNCTION: -SSL_get_stream_write_state ? 3_2_0 EXIST::FUNCTION: -SSL_get_stream_read_error_code ? 3_2_0 EXIST::FUNCTION: -SSL_get_stream_write_error_code ? 3_2_0 EXIST::FUNCTION: -SSL_get_conn_close_info ? 3_2_0 EXIST::FUNCTION: -SSL_set_incoming_stream_policy ? 3_2_0 EXIST::FUNCTION: -SSL_handle_events ? 3_2_0 EXIST::FUNCTION: -SSL_get_event_timeout ? 3_2_0 EXIST::FUNCTION: -SSL_get0_group_name ? 3_2_0 EXIST::FUNCTION: -SSL_is_stream_local ? 3_2_0 EXIST::FUNCTION: +SSL_client_hello_get_extension_order 524 3_2_0 EXIST::FUNCTION: +OSSL_QUIC_client_method 525 3_2_0 EXIST::FUNCTION:QUIC +OSSL_QUIC_client_thread_method 526 3_2_0 EXIST::FUNCTION:QUIC +SSL_CTX_set1_cert_comp_preference 527 3_2_0 EXIST::FUNCTION: +SSL_set1_cert_comp_preference 528 3_2_0 EXIST::FUNCTION: +SSL_CTX_compress_certs 529 3_2_0 EXIST::FUNCTION: +SSL_compress_certs 530 3_2_0 EXIST::FUNCTION: +SSL_CTX_set1_compressed_cert 531 3_2_0 EXIST::FUNCTION: +SSL_set1_compressed_cert 532 3_2_0 EXIST::FUNCTION: +SSL_CTX_get1_compressed_cert 533 3_2_0 EXIST::FUNCTION: +SSL_get1_compressed_cert 534 3_2_0 EXIST::FUNCTION: +SSL_get_rpoll_descriptor 535 3_2_0 EXIST::FUNCTION: +SSL_get_wpoll_descriptor 536 3_2_0 EXIST::FUNCTION: +SSL_set_blocking_mode 537 3_2_0 EXIST::FUNCTION: +SSL_get_blocking_mode 538 3_2_0 EXIST::FUNCTION: +SSL_set1_initial_peer_addr 539 3_2_0 EXIST::FUNCTION: +SSL_net_read_desired 540 3_2_0 EXIST::FUNCTION: +SSL_net_write_desired 541 3_2_0 EXIST::FUNCTION: +SSL_shutdown_ex 542 3_2_0 EXIST::FUNCTION: +SSL_stream_conclude 543 3_2_0 EXIST::FUNCTION: +SSL_inject_net_dgram 544 3_2_0 EXIST::FUNCTION:QUIC +SSL_get0_peer_rpk 545 3_2_0 EXIST::FUNCTION: +SSL_SESSION_get0_peer_rpk 546 3_2_0 EXIST::FUNCTION: +SSL_set1_client_cert_type 547 3_2_0 EXIST::FUNCTION: +SSL_get0_client_cert_type 548 3_2_0 EXIST::FUNCTION: +SSL_set1_server_cert_type 549 3_2_0 EXIST::FUNCTION: +SSL_get0_server_cert_type 550 3_2_0 EXIST::FUNCTION: +SSL_CTX_set1_client_cert_type 551 3_2_0 EXIST::FUNCTION: +SSL_CTX_get0_client_cert_type 552 3_2_0 EXIST::FUNCTION: +SSL_CTX_set1_server_cert_type 553 3_2_0 EXIST::FUNCTION: +SSL_CTX_get0_server_cert_type 554 3_2_0 EXIST::FUNCTION: +SSL_get_negotiated_client_cert_type 555 3_2_0 EXIST::FUNCTION: +SSL_get_negotiated_server_cert_type 556 3_2_0 EXIST::FUNCTION: +SSL_add_expected_rpk 557 3_2_0 EXIST::FUNCTION: +d2i_SSL_SESSION_ex 558 3_2_0 EXIST::FUNCTION: +SSL_is_tls 559 3_2_0 EXIST::FUNCTION: +SSL_is_quic 560 3_2_0 EXIST::FUNCTION: +SSL_get_handshake_rtt 561 3_2_0 EXIST::FUNCTION: +SSL_new_stream 562 3_2_0 EXIST::FUNCTION: +SSL_get0_connection 563 3_2_0 EXIST::FUNCTION: +SSL_is_connection 564 3_2_0 EXIST::FUNCTION: +SSL_get_stream_type 565 3_2_0 EXIST::FUNCTION: +SSL_get_stream_id 566 3_2_0 EXIST::FUNCTION: +SSL_set_default_stream_mode 567 3_2_0 EXIST::FUNCTION: +SSL_accept_stream 568 3_2_0 EXIST::FUNCTION: +SSL_get_accept_stream_queue_len 569 3_2_0 EXIST::FUNCTION: +SSL_stream_reset 570 3_2_0 EXIST::FUNCTION: +SSL_get_stream_read_state 571 3_2_0 EXIST::FUNCTION: +SSL_get_stream_write_state 572 3_2_0 EXIST::FUNCTION: +SSL_get_stream_read_error_code 573 3_2_0 EXIST::FUNCTION: +SSL_get_stream_write_error_code 574 3_2_0 EXIST::FUNCTION: +SSL_get_conn_close_info 575 3_2_0 EXIST::FUNCTION: +SSL_set_incoming_stream_policy 576 3_2_0 EXIST::FUNCTION: +SSL_handle_events 577 3_2_0 EXIST::FUNCTION: +SSL_get_event_timeout 578 3_2_0 EXIST::FUNCTION: +SSL_get0_group_name 579 3_2_0 EXIST::FUNCTION: +SSL_is_stream_local 580 3_2_0 EXIST::FUNCTION: -- Gitee