From ce10661d7d9bd4deaf426a76b1fb8797875e784a Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 31 Oct 2023 11:54:03 -0400 Subject: [PATCH 01/31] augment quic demos to support ipv4/6 connections Because the quicserver utility supports expressly listening in ipv4/6 mode, its possible/likely that the server will listen on an ipv4 address, while the clients will connect via ipv6, leading to connection failures. Augment quic demo clients to afford them the same -6 option that the server has so that connection family can be co-ordinated Reviewed-by: Nicola Tuveri Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22577) Signed-off-by: fly2x --- demos/guide/quic-client-block.c | 24 +++++++++++++++------- demos/guide/quic-client-non-block.c | 25 ++++++++++++++++------- demos/guide/quic-multi-stream.c | 24 +++++++++++++++------- demos/guide/tls-client-block.c | 24 +++++++++++++++------- demos/guide/tls-client-non-block.c | 25 ++++++++++++++++------- doc/man7/ossl-guide-quic-client-block.pod | 2 +- doc/man7/ossl-guide-tls-client-block.pod | 6 ++++-- 7 files changed, 92 insertions(+), 38 deletions(-) diff --git a/demos/guide/quic-client-block.c b/demos/guide/quic-client-block.c index 782f571559..baf5292c47 100644 --- a/demos/guide/quic-client-block.c +++ b/demos/guide/quic-client-block.c @@ -27,7 +27,7 @@ /* Helper function to create a BIO connected to the server */ static BIO *create_socket_bio(const char *hostname, const char *port, - BIO_ADDR **peer_addr) + int family, BIO_ADDR **peer_addr) { int sock = -1; BIO_ADDRINFO *res; @@ -37,7 +37,7 @@ static BIO *create_socket_bio(const char *hostname, const char *port, /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_DGRAM, 0, + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0, &res)) return NULL; @@ -128,14 +128,24 @@ int main(int argc, char *argv[]) char buf[160]; BIO_ADDR *peer_addr = NULL; char *hostname, *port; + int argnext = 1; + int ipv6 = 0; - if (argc != 3) { - printf("Usage: quic-client-block hostname port\n"); + if (argc < 3) { + printf("Usage: quic-client-block [-6] hostname port\n"); goto end; } - hostname = argv[1]; - port = argv[2]; + if (!strcmp(argv[argnext], "-6")) { + if (argc < 4) { + printf("Usage: quic-client-block [-6] hostname port\n"); + goto end; + } + ipv6 = 1; + argnext++; + } + hostname = argv[argnext++]; + port = argv[argnext]; /* * Create an SSL_CTX which we can use to create SSL objects from. We @@ -172,7 +182,7 @@ int main(int argc, char *argv[]) * Create the underlying transport socket/BIO and associate it with the * connection. */ - bio = create_socket_bio(hostname, port, &peer_addr); + bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET, &peer_addr); if (bio == NULL) { printf("Failed to crete the BIO\n"); goto end; diff --git a/demos/guide/quic-client-non-block.c b/demos/guide/quic-client-non-block.c index 31596d84c5..a6c1802fcd 100644 --- a/demos/guide/quic-client-non-block.c +++ b/demos/guide/quic-client-non-block.c @@ -28,7 +28,7 @@ /* Helper function to create a BIO connected to the server */ static BIO *create_socket_bio(const char *hostname, const char *port, - BIO_ADDR **peer_addr) + int family, BIO_ADDR **peer_addr) { int sock = -1; BIO_ADDRINFO *res; @@ -38,7 +38,7 @@ static BIO *create_socket_bio(const char *hostname, const char *port, /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_DGRAM, 0, + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0, &res)) return NULL; @@ -236,14 +236,24 @@ int main(int argc, char *argv[]) BIO_ADDR *peer_addr = NULL; int eof = 0; char *hostname, *port; + int ipv6 = 0; + int argnext = 1; - if (argc != 3) { - printf("Usage: quic-client-non-block hostname port\n"); + if (argc < 3) { + printf("Usage: quic-client-non-block [-6] hostname port\n"); goto end; } - hostname = argv[1]; - port = argv[2]; + if (!strcmp(argv[argnext], "-6")) { + if (argc < 4) { + printf("Usage: quic-client-non-block [-6] hostname port\n"); + goto end; + } + ipv6 = 1; + argnext++; + } + hostname = argv[argnext++]; + port = argv[argnext]; /* * Create an SSL_CTX which we can use to create SSL objects from. We @@ -280,7 +290,8 @@ int main(int argc, char *argv[]) * Create the underlying transport socket/BIO and associate it with the * connection. */ - bio = create_socket_bio(hostname, port, &peer_addr); + bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET, + &peer_addr); if (bio == NULL) { printf("Failed to crete the BIO\n"); goto end; diff --git a/demos/guide/quic-multi-stream.c b/demos/guide/quic-multi-stream.c index 469c5ba4b2..d31ea245c8 100644 --- a/demos/guide/quic-multi-stream.c +++ b/demos/guide/quic-multi-stream.c @@ -27,7 +27,7 @@ /* Helper function to create a BIO connected to the server */ static BIO *create_socket_bio(const char *hostname, const char *port, - BIO_ADDR **peer_addr) + int family, BIO_ADDR **peer_addr) { int sock = -1; BIO_ADDRINFO *res; @@ -37,7 +37,7 @@ static BIO *create_socket_bio(const char *hostname, const char *port, /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_DGRAM, 0, + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0, &res)) return NULL; @@ -148,14 +148,24 @@ int main(int argc, char *argv[]) char buf[160]; BIO_ADDR *peer_addr = NULL; char *hostname, *port; + int argnext = 1; + int ipv6 = 0; - if (argc != 3) { - printf("Usage: quic-client-non-block hostname port\n"); + if (argc < 3) { + printf("Usage: quic-client-non-block [-6] hostname port\n"); goto end; } - hostname = argv[1]; - port = argv[2]; + if (!strcmp(argv[argnext], "-6")) { + if (argc < 4) { + printf("Usage: quic-client-non-block [-6] hostname port\n"); + goto end; + } + ipv6 = 1; + argnext++; + } + hostname = argv[argnext++]; + port = argv[argnext]; /* * Create an SSL_CTX which we can use to create SSL objects from. We @@ -201,7 +211,7 @@ int main(int argc, char *argv[]) * Create the underlying transport socket/BIO and associate it with the * connection. */ - bio = create_socket_bio(hostname, port, &peer_addr); + bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET, &peer_addr); if (bio == NULL) { printf("Failed to crete the BIO\n"); goto end; diff --git a/demos/guide/tls-client-block.c b/demos/guide/tls-client-block.c index ea7d68467a..c6ba5850f7 100644 --- a/demos/guide/tls-client-block.c +++ b/demos/guide/tls-client-block.c @@ -26,7 +26,7 @@ #include /* Helper function to create a BIO connected to the server */ -static BIO *create_socket_bio(const char *hostname, const char *port) +static BIO *create_socket_bio(const char *hostname, const char *port, int family) { int sock = -1; BIO_ADDRINFO *res; @@ -36,7 +36,7 @@ static BIO *create_socket_bio(const char *hostname, const char *port) /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_STREAM, 0, + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_STREAM, 0, &res)) return NULL; @@ -109,14 +109,24 @@ int main(int argc, char *argv[]) size_t written, readbytes; char buf[160]; char *hostname, *port; + int argnext = 1; + int ipv6 = 0; - if (argc != 3) { - printf("Usage: tls-client-block hostname port\n"); + if (argc < 3) { + printf("Usage: tls-client-block [-6] hostname port\n"); goto end; } - hostname = argv[1]; - port = argv[2]; + if (!strcmp(argv[argnext], "-6")) { + if (argc < 4) { + printf("Usage: tls-client-block [-6] hostname port\n"); + goto end; + } + ipv6 = 1; + argnext++; + } + hostname = argv[argnext++]; + port = argv[argnext]; /* * Create an SSL_CTX which we can use to create SSL objects from. We @@ -162,7 +172,7 @@ int main(int argc, char *argv[]) * Create the underlying transport socket/BIO and associate it with the * connection. */ - bio = create_socket_bio(hostname, port); + bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET); if (bio == NULL) { printf("Failed to crete the BIO\n"); goto end; diff --git a/demos/guide/tls-client-non-block.c b/demos/guide/tls-client-non-block.c index 8748e4fffc..0b19d67762 100644 --- a/demos/guide/tls-client-non-block.c +++ b/demos/guide/tls-client-non-block.c @@ -27,7 +27,7 @@ #include /* Helper function to create a BIO connected to the server */ -static BIO *create_socket_bio(const char *hostname, const char *port) +static BIO *create_socket_bio(const char *hostname, const char *port, int family) { int sock = -1; BIO_ADDRINFO *res; @@ -37,7 +37,7 @@ static BIO *create_socket_bio(const char *hostname, const char *port) /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_STREAM, 0, + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_STREAM, 0, &res)) return NULL; @@ -187,14 +187,25 @@ int main(int argc, char *argv[]) char buf[160]; int eof = 0; char *hostname, *port; + int argnext = 1; + int ipv6 = 0; - if (argc != 3) { - printf("Usage: tls-client-non-block hostname port\n"); + if (argc < 3) { + printf("Usage: tls-client-non-block [-6] hostname port\n"); goto end; } - hostname = argv[1]; - port = argv[2]; + if (!strcmp(argv[argnext], "-6")) { + if (argc < 4) { + printf("Usage: tls-client-non-block [-6] hostname port\n"); + goto end; + } + ipv6 = 1; + argnext++; + } + + hostname = argv[argnext++]; + port = argv[argnext]; /* * Create an SSL_CTX which we can use to create SSL objects from. We @@ -240,7 +251,7 @@ int main(int argc, char *argv[]) * Create the underlying transport socket/BIO and associate it with the * connection. */ - bio = create_socket_bio(hostname, port); + bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET); if (bio == NULL) { printf("Failed to crete the BIO\n"); goto end; diff --git a/doc/man7/ossl-guide-quic-client-block.pod b/doc/man7/ossl-guide-quic-client-block.pod index fc8912086d..ab018e4a22 100644 --- a/doc/man7/ossl-guide-quic-client-block.pod +++ b/doc/man7/ossl-guide-quic-client-block.pod @@ -94,7 +94,7 @@ for TCP). /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_DGRAM, 0, + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0, &res)) return NULL; diff --git a/doc/man7/ossl-guide-tls-client-block.pod b/doc/man7/ossl-guide-tls-client-block.pod index cb67bf8fa9..ba59bd4ab3 100644 --- a/doc/man7/ossl-guide-tls-client-block.pod +++ b/doc/man7/ossl-guide-tls-client-block.pod @@ -174,7 +174,7 @@ integrate into the OpenSSL error system to log error data, e.g. /* * Lookup IP address info for the server. */ - if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, 0, SOCK_STREAM, 0, + if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_STREAM, 0, &res)) return NULL; @@ -212,7 +212,9 @@ See L, L, L, L, L, L and L for further information on the functions used here. In the above example code the B and B variables are strings, e.g. -"www.example.com" and "443". +"www.example.com" and "443". Note also the use of the family variable, which +can take the values of AF_INET or AF_INET6 based on the command line -6 option, +to allow specific connections to an ipv4 or ipv6 enabled host. Sockets created using the methods described above will automatically be blocking sockets - which is exactly what we want for this example. -- Gitee From 7c9aed11234cc5d218cc5c44ff2ec1355bb84e6d Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 9 Nov 2023 08:13:58 -0500 Subject: [PATCH 02/31] zero data in hm_fragment on alloc if we allocate a new hm_frament in dtls1_buffer_message with dtls1_hm_fragment_new, the returned fragment contains uninitalized data in the msg_header field. If an error then occurs, and we free the fragment, dtls_hm_fragment_free interrogates the msg_header field (which is garbage), and potentially references undefined values, or worse, accidentally references available memory that is not owned, leading to various corruptions. Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tim Hudson Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22679) Signed-off-by: fly2x --- ssl/statem/statem_dtls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssl/statem/statem_dtls.c b/ssl/statem/statem_dtls.c index a88b0dfeac..97d9f4591c 100644 --- a/ssl/statem/statem_dtls.c +++ b/ssl/statem/statem_dtls.c @@ -62,7 +62,7 @@ static hm_fragment *dtls1_hm_fragment_new(size_t frag_len, int reassembly) unsigned char *buf = NULL; unsigned char *bitmask = NULL; - if ((frag = OPENSSL_malloc(sizeof(*frag))) == NULL) + if ((frag = OPENSSL_zalloc(sizeof(*frag))) == NULL) return NULL; if (frag_len) { -- Gitee From 3d3514414e1b16543483a368865972e5e7cd5bcc Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Thu, 9 Nov 2023 14:45:33 +0000 Subject: [PATCH 03/31] Move freeing of an old record layer to dtls1_clear_sent_buffer When we are clearing the sent messages queue we should ensure we free any old write record layers that are no longer in use. Previously this logic was in dtls1_hm_fragment_free() - but this can end up freeing the current record layer under certain error conditions. Fixes #22664 Reviewed-by: Dmitry Belyavskiy Reviewed-by: Tim Hudson Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22679) Signed-off-by: fly2x --- ssl/d1_lib.c | 19 +++++++++++++++---- ssl/statem/statem_dtls.c | 9 +-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index be4ff02a89..1ac0975d0a 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -130,6 +130,17 @@ void dtls1_clear_sent_buffer(SSL_CONNECTION *s) while ((item = pqueue_pop(s->d1->sent_messages)) != NULL) { frag = (hm_fragment *)item->data; + + if (frag->msg_header.is_ccs + && frag->msg_header.saved_retransmit_state.wrlmethod != NULL + && s->rlayer.wrl != frag->msg_header.saved_retransmit_state.wrl) { + /* + * If we're freeing the CCS then we're done with the old wrl and it + * can bee freed + */ + frag->msg_header.saved_retransmit_state.wrlmethod->free(frag->msg_header.saved_retransmit_state.wrl); + } + dtls1_hm_fragment_free(frag); pitem_free(item); } @@ -143,16 +154,16 @@ void dtls1_free(SSL *ssl) if (s == NULL) return; - DTLS_RECORD_LAYER_free(&s->rlayer); - - ssl3_free(ssl); - if (s->d1 != NULL) { dtls1_clear_queues(s); pqueue_free(s->d1->buffered_messages); pqueue_free(s->d1->sent_messages); } + DTLS_RECORD_LAYER_free(&s->rlayer); + + ssl3_free(ssl); + OPENSSL_free(s->d1); s->d1 = NULL; } diff --git a/ssl/statem/statem_dtls.c b/ssl/statem/statem_dtls.c index 97d9f4591c..c674ddfb54 100644 --- a/ssl/statem/statem_dtls.c +++ b/ssl/statem/statem_dtls.c @@ -94,14 +94,7 @@ void dtls1_hm_fragment_free(hm_fragment *frag) { if (!frag) return; - if (frag->msg_header.is_ccs) { - /* - * If we're freeing the CCS then we're done with the old wrl and it - * can bee freed - */ - if (frag->msg_header.saved_retransmit_state.wrlmethod != NULL) - frag->msg_header.saved_retransmit_state.wrlmethod->free(frag->msg_header.saved_retransmit_state.wrl); - } + OPENSSL_free(frag->fragment); OPENSSL_free(frag->reassembly); OPENSSL_free(frag); -- Gitee From 8c77c8cf5e4059bc93c429d7d93e27d6e2306d94 Mon Sep 17 00:00:00 2001 From: Anders Jansson Date: Sat, 18 Nov 2023 23:40:33 +0100 Subject: [PATCH 04/31] Fix typo in variable name Fix spelling $cppfags2 => $cppflags2 in file Configurations/windows-makefile.tmpl CLA: trivial Reviewed-by: Matt Caswell Reviewed-by: Matthias St. Pierre Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22771) Signed-off-by: fly2x --- Configurations/windows-makefile.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations/windows-makefile.tmpl b/Configurations/windows-makefile.tmpl index 66550e1f82..20036f5c3d 100644 --- a/Configurations/windows-makefile.tmpl +++ b/Configurations/windows-makefile.tmpl @@ -301,7 +301,7 @@ RCOUTFLAG={- $target{rcoutflag} -}$(OSSL_EMPTY) CNF_ASFLAGS={- join(' ', $target{asflags} || (), @{$config{asflags}}) -} -CNF_CPPFLAGS={- our $cppfags2 = +CNF_CPPFLAGS={- our $cppflags2 = join(' ', $target{cppflags} || (), (map { '-D'.quotify1($_) } @{$target{defines}}, @{$config{defines}}), -- Gitee From b11786d613a51634f3f6fd956e02ac38b0233dc0 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Wed, 15 Nov 2023 19:46:17 +0100 Subject: [PATCH 05/31] Fix a possible memory leak in dane_tlsa_add Several error cases leak either the X509 object or the pkey or the danetls_record object. Reviewed-by: Hugo Landau Reviewed-by: Matthias St. Pierre Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22743) Signed-off-by: fly2x --- ssl/ssl_lib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 5314e1ec0d..70d3b17c19 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -327,11 +327,13 @@ static int dane_tlsa_add(SSL_DANE *dane, case DANETLS_SELECTOR_CERT: if (!d2i_X509(&cert, &p, ilen) || p < data || dlen != (size_t)(p - data)) { + X509_free(cert); tlsa_free(t); ERR_raise(ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE); return 0; } if (X509_get0_pubkey(cert) == NULL) { + X509_free(cert); tlsa_free(t); ERR_raise(ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE); return 0; @@ -339,6 +341,7 @@ static int dane_tlsa_add(SSL_DANE *dane, if ((DANETLS_USAGE_BIT(usage) & DANETLS_TA_MASK) == 0) { X509_free(cert); + tlsa_free(t); break; } @@ -362,6 +365,7 @@ static int dane_tlsa_add(SSL_DANE *dane, case DANETLS_SELECTOR_SPKI: if (!d2i_PUBKEY(&pkey, &p, ilen) || p < data || dlen != (size_t)(p - data)) { + EVP_PKEY_free(pkey); tlsa_free(t); ERR_raise(ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_PUBLIC_KEY); return 0; -- Gitee From 811eb97c43539a1d617b9386534916b1abcaa54c Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Wed, 15 Nov 2023 20:32:59 +0100 Subject: [PATCH 06/31] Fix a possible memleak in PKCS7_add_attrib_smimecap When PKCS7_add_signed_attribute fails, the ASN1_STRING object may be leaked. Reviewed-by: Neil Horman Reviewed-by: Shane Lontis Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22744) Signed-off-by: fly2x --- crypto/pkcs7/pk7_attr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crypto/pkcs7/pk7_attr.c b/crypto/pkcs7/pk7_attr.c index 68f0a5c290..72690c5e1b 100644 --- a/crypto/pkcs7/pk7_attr.c +++ b/crypto/pkcs7/pk7_attr.c @@ -28,8 +28,12 @@ int PKCS7_add_attrib_smimecap(PKCS7_SIGNER_INFO *si, } seq->length = ASN1_item_i2d((ASN1_VALUE *)cap, &seq->data, ASN1_ITEM_rptr(X509_ALGORS)); - return PKCS7_add_signed_attribute(si, NID_SMIMECapabilities, - V_ASN1_SEQUENCE, seq); + if (!PKCS7_add_signed_attribute(si, NID_SMIMECapabilities, + V_ASN1_SEQUENCE, seq)) { + ASN1_STRING_free(seq); + return 0; + } + return 1; } STACK_OF(X509_ALGOR) *PKCS7_get_smimecap(PKCS7_SIGNER_INFO *si) -- Gitee From 2693d5f9e119a243482662d6cab967734567d8e5 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Fri, 17 Nov 2023 07:12:42 +0100 Subject: [PATCH 07/31] Fix a possible memleak in CMS_sign_receipt When an error happens after cms_encode_Receipt the ASN1_OCTET_STRING object "os" may be leaked. Reviewed-by: Shane Lontis Reviewed-by: Tim Hudson Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22758) Signed-off-by: fly2x --- crypto/cms/cms_smime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c index 65f9674037..99a72f4dff 100644 --- a/crypto/cms/cms_smime.c +++ b/crypto/cms/cms_smime.c @@ -560,7 +560,7 @@ CMS_ContentInfo *CMS_sign_receipt(CMS_SignerInfo *si, { CMS_SignerInfo *rct_si; CMS_ContentInfo *cms = NULL; - ASN1_OCTET_STRING **pos, *os; + ASN1_OCTET_STRING **pos, *os = NULL; BIO *rct_cont = NULL; int r = 0; const CMS_CTX *ctx = si->cms_ctx; @@ -622,6 +622,7 @@ CMS_ContentInfo *CMS_sign_receipt(CMS_SignerInfo *si, if (r) return cms; CMS_ContentInfo_free(cms); + ASN1_OCTET_STRING_free(os); return NULL; } -- Gitee From 6168f54024744278fd1a8bcdf53ac4aab0d2cc98 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Mon, 20 Nov 2023 10:05:49 +0100 Subject: [PATCH 08/31] Fix a possible use-after-free in custom_exts_free This may happen when ssl_cert_dup calls custom_exts_copy, where a possible memory allocation error causes custom_exts_free to be called twice: once in the error handling of custom_exts_copy and a second time in the error handling of ssl_cert_dup. Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22772) Signed-off-by: fly2x --- ssl/statem/extensions_cust.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c index 7c049d2970..fd840e8918 100644 --- a/ssl/statem/extensions_cust.c +++ b/ssl/statem/extensions_cust.c @@ -342,6 +342,8 @@ void custom_exts_free(custom_ext_methods *exts) OPENSSL_free(meth->parse_arg); } OPENSSL_free(exts->meths); + exts->meths = NULL; + exts->meths_count = 0; } /* Return true if a client custom extension exists, false otherwise */ -- Gitee From 3cb1bee86c22d5b0a336dc691774a4d7edcef895 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Tue, 14 Nov 2023 02:42:42 +0100 Subject: [PATCH 09/31] Fix possible memleak in PKCS7_add0_attrib_signing_time When PKCS7_add_signed_attribute fails, the ASN1_TIME object may be leaked when it was not passed in as input parameter. Reviewed-by: Neil Horman Reviewed-by: Shane Lontis Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22772) Signed-off-by: fly2x --- crypto/pkcs7/pk7_attr.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crypto/pkcs7/pk7_attr.c b/crypto/pkcs7/pk7_attr.c index 72690c5e1b..a12d65bb8e 100644 --- a/crypto/pkcs7/pk7_attr.c +++ b/crypto/pkcs7/pk7_attr.c @@ -102,12 +102,18 @@ int PKCS7_add_attrib_content_type(PKCS7_SIGNER_INFO *si, ASN1_OBJECT *coid) int PKCS7_add0_attrib_signing_time(PKCS7_SIGNER_INFO *si, ASN1_TIME *t) { - if (t == NULL && (t = X509_gmtime_adj(NULL, 0)) == NULL) { + ASN1_TIME *tmp = NULL; + + if (t == NULL && (tmp = t = X509_gmtime_adj(NULL, 0)) == NULL) { ERR_raise(ERR_LIB_PKCS7, ERR_R_X509_LIB); return 0; } - return PKCS7_add_signed_attribute(si, NID_pkcs9_signingTime, - V_ASN1_UTCTIME, t); + if (!PKCS7_add_signed_attribute(si, NID_pkcs9_signingTime, + V_ASN1_UTCTIME, t)) { + ASN1_TIME_free(tmp); + return 0; + } + return 1; } int PKCS7_add1_attrib_digest(PKCS7_SIGNER_INFO *si, -- Gitee From 7d7d16ff94f6a97c0f80848db17eaa18951ef54c Mon Sep 17 00:00:00 2001 From: James Muir Date: Sat, 4 Nov 2023 23:06:06 -0400 Subject: [PATCH 10/31] Update OpenSSL logos Add two new files doc/images/openssl-square.svg doc/images/openssl-square-nontransparent.png and update the existing file doc/images/openssl.svg The "square" versions of the logo write "Open" and "SSL" on separate lines, so that less horizontal space is used. The png file (nontransparent, white background) can be used to update the profile picture for the OpenSSL organization on GitHub. For the existing logo, openssl.svg, the subtitle "Cryptography and SSL/TLS Toolkit" has been dropped and the text-elements have been converted to paths (so they are no longer dependent on what fonts the renderer provides). The svg files were provided by Anton A. Part of https://github.com/openssl/project/issues/262 Reviewed-by: Anton Arapov Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22626) Signed-off-by: fly2x --- doc/images/openssl-square-nontransparent.png | Bin 0 -> 78086 bytes doc/images/openssl-square.svg | 49 +++++++++++ doc/images/openssl.svg | 88 ++++++++++--------- 3 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 doc/images/openssl-square-nontransparent.png create mode 100644 doc/images/openssl-square.svg diff --git a/doc/images/openssl-square-nontransparent.png b/doc/images/openssl-square-nontransparent.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6b747ce0879921715ad64523a2d055be27f075 GIT binary patch literal 78086 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 + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/images/openssl.svg b/doc/images/openssl.svg index 9cd6794fbd..988d3a0e91 100644 --- a/doc/images/openssl.svg +++ b/doc/images/openssl.svg @@ -1,41 +1,49 @@ - - - - - OpenSSL - Cryptography and SSL/TLS Toolkit - + + + + + + + + + + + + + + + + + + + -- Gitee From 4f5464bd506d9589b5404e9e2ed1611568ed9c4d Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Fri, 10 Nov 2023 15:33:21 +0000 Subject: [PATCH 11/31] Update the provider documentation Make the documentation match reality. Add lots of missing algorithms. Reviewed-by: Shane Lontis Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22694) Signed-off-by: fly2x --- doc/man7/OSSL_PROVIDER-FIPS.pod | 40 +++++++++- doc/man7/OSSL_PROVIDER-base.pod | 86 ++++++++++++++++++--- doc/man7/OSSL_PROVIDER-default.pod | 120 ++++++++++++++++++++++++++--- doc/man7/OSSL_PROVIDER-legacy.pod | 2 + 4 files changed, 226 insertions(+), 22 deletions(-) diff --git a/doc/man7/OSSL_PROVIDER-FIPS.pod b/doc/man7/OSSL_PROVIDER-FIPS.pod index 449d5624e0..485c4c6341 100644 --- a/doc/man7/OSSL_PROVIDER-FIPS.pod +++ b/doc/man7/OSSL_PROVIDER-FIPS.pod @@ -72,6 +72,8 @@ The OpenSSL FIPS provider supports these operations and algorithms: =item KECCAK-KMAC, see L +=item SHAKE, see L + =back =head2 Symmetric Ciphers @@ -80,6 +82,10 @@ The OpenSSL FIPS provider supports these operations and algorithms: =item AES, see L +=item 3DES, see L + +This is an unapproved algorithm. + =back =head2 Message Authentication Code (MAC) @@ -134,6 +140,10 @@ The OpenSSL FIPS provider supports these operations and algorithms: =item X448, see L +=item TLS1-PRF + +=item HKDF + =back =head2 Asymmetric Signature @@ -142,9 +152,17 @@ The OpenSSL FIPS provider supports these operations and algorithms: =item RSA, see L -=item X25519, see L +=item DSA, see L + +=item ED25519, see L + +This is an unapproved algorithm. + +=item ED448, see L + +This is an unapproved algorithm. -=item X448, see L +=item ECDSA, see L =item HMAC, see L @@ -180,12 +198,30 @@ The OpenSSL FIPS provider supports these operations and algorithms: =item RSA, see L +=item RSA-PSS + =item EC, see L =item X25519, see L =item X448, see L +=item ED25519, see L + +This is an unapproved algorithm. + +=item ED448, see L + +This is an unapproved algorithm. + +=item TLS1-PRF + +=item HKDF + +=item HMAC, see L + +=item CMAC, see L + =back =head2 Random Number Generation diff --git a/doc/man7/OSSL_PROVIDER-base.pod b/doc/man7/OSSL_PROVIDER-base.pod index c51adbde1e..24d610f28c 100644 --- a/doc/man7/OSSL_PROVIDER-base.pod +++ b/doc/man7/OSSL_PROVIDER-base.pod @@ -57,28 +57,96 @@ currently permitted. The OpenSSL base provider supports these operations and algorithms: +=head2 Random Number Generation + +=over 4 + +=item SEED-SRC, see L + +=back + +In addition to this provider, the "SEED-SRC" algorithm is also available in the +default provider. + =head2 Asymmetric Key Encoder -In addition to "provider=base", some of these encoders define the -property "fips=yes", to allow them to be used together with the FIPS -provider. +=over 4 + +=item RSA + +=item RSA-PSS + +=item DH + +=item DHX + +=item DSA + +=item EC + +=item ED25519 + +=item ED448 + +=item X25519 + +=item X448 + +=item SM2 + +=back + +In addition to this provider, all of these encoding algorithms are also +available in the default provider. Some of these algorithms may be used in +combination with the FIPS provider. + +=head2 Asymmetric Key Decoder =over 4 -=item RSA, see L +=item RSA + +=item RSA-PSS + +=item DH + +=item DHX + +=item DSA + +=item EC + +=item ED25519 -=item DH, see L +=item ED448 -=item DSA, see L +=item X25519 -=item EC, see L +=item X448 -=item X25519, see L +=item SM2 -=item X448, see L +=item DER =back +In addition to this provider, all of these decoding algorithms are also +available in the default provider. Some of these algorithms may be used in +combination with the FIPS provider. + +=head2 Stores + +=over 4 + +=item file + +=item org.openssl.winstore + +=back + +In addition to this provider, all of these store algorithms are also +available in the default provider. + =head1 SEE ALSO L, L, diff --git a/doc/man7/OSSL_PROVIDER-default.pod b/doc/man7/OSSL_PROVIDER-default.pod index 603fd06331..feba00aa76 100644 --- a/doc/man7/OSSL_PROVIDER-default.pod +++ b/doc/man7/OSSL_PROVIDER-default.pod @@ -89,8 +89,6 @@ The OpenSSL default provider supports these operations and algorithms: =item 3DES, see L -=item SEED, see L - =item SM4, see L =item ChaCha20, see L @@ -127,6 +125,8 @@ The OpenSSL default provider supports these operations and algorithms: =item HKDF, see L +=item TLS13-KDF, see L + =item SSKDF, see L =item PBKDF2, see L @@ -167,6 +167,12 @@ The OpenSSL default provider supports these operations and algorithms: =item X448, see L +=item TLS1-PRF + +=item HKDF + +=item SCRYPT + =back =head2 Asymmetric Signature @@ -177,6 +183,14 @@ The OpenSSL default provider supports these operations and algorithms: =item RSA, see L +=item ED25519, see L + +=item ED448, see L + +=item ECDSA, see L + +=item SM2 + =item HMAC, see L =item SIPHASH, see L @@ -205,6 +219,8 @@ The OpenSSL default provider supports these operations and algorithms: =item X25519, see L +=item X448, see L + =item EC, see L =back @@ -221,12 +237,34 @@ The OpenSSL default provider supports these operations and algorithms: =item RSA, see L +=item RSA-PSS + =item EC, see L =item X25519, see L =item X448, see L +=item ED25519, see L + +=item ED448, see L + +=item TLS1-PRF + +=item HKDF + +=item SCRYPT + +=item HMAC, see L + +=item SIPHASH, see L + +=item POLY1305, see L + +=item CMAC, see L + +=item SM2, see L + =back =head2 Random Number Generation @@ -245,28 +283,88 @@ The OpenSSL default provider supports these operations and algorithms: =back +In addition to this provider, the "SEED-SRC" algorithm is also available in the +base provider. + =head2 Asymmetric Key Encoder -The default provider also includes all of the encoding algorithms -present in the base provider. Some of these have the property "fips=yes", -to allow them to be used together with the FIPS provider. +=over 4 + +=item RSA + +=item RSA-PSS + +=item DH + +=item DHX + +=item DSA + +=item EC + +=item ED25519 + +=item ED448 + +=item X25519 + +=item X448 + +=item SM2 + +=back + +In addition to this provider, all of these encoding algorithms are also +available in the base provider. Some of these algorithms may be used in +combination with the FIPS provider. + +=head2 Asymmetric Key Decoder =over 4 -=item RSA, see L +=item RSA + +=item RSA-PSS + +=item DH -=item DH, see L +=item DHX -=item DSA, see L +=item DSA -=item EC, see L +=item EC -=item X25519, see L +=item ED25519 -=item X448, see L +=item ED448 + +=item X25519 + +=item X448 + +=item SM2 + +=item DER =back +In addition to this provider, all of these decoding algorithms are also +available in the base provider. Some of these algorithms may be used in +combination with the FIPS provider. + +=head2 Stores + +=over 4 + +=item file + +=item org.openssl.winstore + +=back + +In addition to this provider, all of these store algorithms are also +available in the base provider. + =head1 SEE ALSO L, L, L, diff --git a/doc/man7/OSSL_PROVIDER-legacy.pod b/doc/man7/OSSL_PROVIDER-legacy.pod index 82781a09b2..d70de3682f 100644 --- a/doc/man7/OSSL_PROVIDER-legacy.pod +++ b/doc/man7/OSSL_PROVIDER-legacy.pod @@ -42,6 +42,8 @@ The OpenSSL legacy provider supports these operations and algorithms: =item MD2, see L +Disabled by default. Use I config option to enable. + =item MD4, see L =item MDC2, see L -- Gitee From d30cec43dc7f5c37ec79629c9a10b50b4686dc60 Mon Sep 17 00:00:00 2001 From: James Muir Date: Mon, 13 Nov 2023 14:28:23 -0500 Subject: [PATCH 12/31] doc: better description of KECCAK-KMAC XOF KECCAK-KMAC-128 and KECCAK-KMAC-256 are extendable output functions that have been defined because they are convenient for implementing KMAC. Give definitions for them so that users aren't left to figure that out themselves. KECCAK-KMAC-128 is very similar to SHAKE-128, and KECCAK-KMAC-256 is very similar to SHAKE-256. Related to #22619. Reviewed-by: Neil Horman Reviewed-by: Shane Lontis Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22755) Signed-off-by: fly2x --- doc/man7/EVP_MD-SHAKE.pod | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/doc/man7/EVP_MD-SHAKE.pod b/doc/man7/EVP_MD-SHAKE.pod index 6f1fe9cae6..e0395f887b 100644 --- a/doc/man7/EVP_MD-SHAKE.pod +++ b/doc/man7/EVP_MD-SHAKE.pod @@ -10,8 +10,9 @@ EVP_MD-SHAKE, EVP_MD-KECCAK-KMAC Support for computing SHAKE or KECCAK-KMAC digests through the B API. -KECCAK-KMAC is a special digest that's used by the KMAC EVP_MAC -implementation (see L). +KECCAK-KMAC is an Extendable Output Function (XOF), with a definition +similar to SHAKE, used by the KMAC EVP_MAC implementation (see +L). =head2 Identities @@ -22,21 +23,25 @@ provider, and includes the following varieties: =item KECCAK-KMAC-128 -Known names are "KECCAK-KMAC-128" and "KECCAK-KMAC128" -This is used by L +Known names are "KECCAK-KMAC-128" and "KECCAK-KMAC128". This is used +by L. Using the notation from NIST FIPS 202 +(Section 6.2), we have KECCAK-KMAC-128(M, d) = KECCAK[256](M || 00, d) +(see the description of KMAC128 in Appendix A of NIST SP 800-185). =item KECCAK-KMAC-256 -Known names are "KECCAK-KMAC-256" and "KECCAK-KMAC256" -This is used by L +Known names are "KECCAK-KMAC-256" and "KECCAK-KMAC256". This is used +by L. Using the notation from NIST FIPS 202 +(Section 6.2), we have KECCAK-KMAC-256(M, d) = KECCAK[512](M || 00, d) +(see the description of KMAC256 in Appendix A of NIST SP 800-185). =item SHAKE-128 -Known names are "SHAKE-128" and "SHAKE128" +Known names are "SHAKE-128" and "SHAKE128". =item SHAKE-256 -Known names are "SHAKE-256" and "SHAKE256" +Known names are "SHAKE-256" and "SHAKE256". =back -- Gitee From deecc996c912f2992dca503e2ff0a2e7451d0419 Mon Sep 17 00:00:00 2001 From: James Muir Date: Fri, 17 Nov 2023 17:58:24 -0500 Subject: [PATCH 13/31] doc: fix description of mac "block-size" parameter The macro for "block-size" is OSSL_MAC_PARAM_BLOCK_SIZE, and this parameter is not settable. Refer to the "customization string" rather than the "custom value" (in the Blake2 spec, this is called the personalization string). Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22763) Signed-off-by: fly2x --- doc/man7/EVP_MAC-BLAKE2.pod | 9 +++++---- doc/man7/EVP_MAC-CMAC.pod | 2 +- doc/man7/EVP_MAC-HMAC.pod | 2 +- doc/man7/EVP_MAC-KMAC.pod | 12 +++++++----- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/doc/man7/EVP_MAC-BLAKE2.pod b/doc/man7/EVP_MAC-BLAKE2.pod index 5557e15353..f1106ef5a2 100644 --- a/doc/man7/EVP_MAC-BLAKE2.pod +++ b/doc/man7/EVP_MAC-BLAKE2.pod @@ -27,7 +27,8 @@ properties, to be used with EVP_MAC_fetch(): The general description of these parameters can be found in L. -All these parameters can be set with EVP_MAC_CTX_set_params(). +All these parameters (except for "block-size") can be set with +EVP_MAC_CTX_set_params(). Furthermore, the "size" parameter can be retrieved with EVP_MAC_CTX_get_params(), or with EVP_MAC_CTX_get_mac_size(). The length of the "size" parameter should not exceed that of a B. @@ -45,7 +46,7 @@ Setting this parameter is identical to passing a I to L. =item "custom" (B) -Sets the custom value. +Sets the customization/personalization string. It is an optional value of at most 16 bytes for BLAKE2BMAC or 8 for BLAKE2SMAC, and is empty by default. @@ -62,10 +63,10 @@ It can be any number between 1 and 32 for EVP_MAC_BLAKE2S or between 1 and 64 for EVP_MAC_BLAKE2B. It is 32 and 64 respectively by default. -=item "block-size" (B) +=item "block-size" (B) Gets the MAC block size. -By default, it is 64 for EVP_MAC_BLAKE2S and 128 for EVP_MAC_BLAKE2B. +It is 64 for EVP_MAC_BLAKE2S and 128 for EVP_MAC_BLAKE2B. =back diff --git a/doc/man7/EVP_MAC-CMAC.pod b/doc/man7/EVP_MAC-CMAC.pod index 3fb530c4cf..44c5fc7c44 100644 --- a/doc/man7/EVP_MAC-CMAC.pod +++ b/doc/man7/EVP_MAC-CMAC.pod @@ -63,7 +63,7 @@ The length of the "size" parameter is equal to that of an B. =over 4 -=item "block-size" (B) +=item "block-size" (B) Gets the MAC block size. The "block-size" parameter can also be retrieved with EVP_MAC_CTX_get_block_size(). diff --git a/doc/man7/EVP_MAC-HMAC.pod b/doc/man7/EVP_MAC-HMAC.pod index ea2eda9ec8..0e401710fc 100644 --- a/doc/man7/EVP_MAC-HMAC.pod +++ b/doc/man7/EVP_MAC-HMAC.pod @@ -76,7 +76,7 @@ The length of the "size" parameter is equal to that of an B. =over 4 -=item "block-size" (B) +=item "block-size" (B) Gets the MAC block size. The "block-size" parameter can also be retrieved with EVP_MAC_CTX_get_block_size(). diff --git a/doc/man7/EVP_MAC-KMAC.pod b/doc/man7/EVP_MAC-KMAC.pod index dc67c66336..9c4fbc0b2a 100644 --- a/doc/man7/EVP_MAC-KMAC.pod +++ b/doc/man7/EVP_MAC-KMAC.pod @@ -27,7 +27,8 @@ properties, to be used with EVP_MAC_fetch(): The general description of these parameters can be found in L. -All these parameters can be set with EVP_MAC_CTX_set_params(). +All these parameters (except for "block-size") can be set with +EVP_MAC_CTX_set_params(). Furthermore, the "size" parameter can be retrieved with EVP_MAC_CTX_get_params(), or with EVP_MAC_CTX_get_mac_size(). The length of the "size" parameter should not exceed that of a B. @@ -45,18 +46,19 @@ The length of the key (in bytes) must be in the range 4...512. =item "custom" (B) -Sets the custom value. -It is an optional value with a length of at most 512 bytes, and is empty by default. +Sets the customization string. +It is an optional value with a length of at most 512 bytes, and is +empty by default. =item "size" (B) Sets the MAC size. By default, it is 32 for C and 64 for C. -=item "block-size" (B) +=item "block-size" (B) Gets the MAC block size. -By default, it is 168 for C and 136 for C. +It is 168 for C and 136 for C. =item "xof" (B) -- Gitee From f1df2df95c3e365e5895897fda0fe98146763275 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Mon, 20 Nov 2023 14:06:42 +0000 Subject: [PATCH 14/31] Amend NEWS.md to be more like release notes Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22785) (cherry picked from commit 5e07ea4f82b5250d64183ddda2b56ebf37df126a) Signed-off-by: fly2x --- NEWS.md | 107 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/NEWS.md b/NEWS.md index 07914e3fdc..f85a87657c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,32 +31,87 @@ OpenSSL 3.2 ### Major changes between OpenSSL 3.1 and OpenSSL 3.2 [under development] - * Added client side support for QUIC. - * Added multiple tutorials on the OpenSSL library and in particular - on writing various clients (using TLS and QUIC protocols) with libssl. - * Added support for Brainpool curves in TLS-1.3. - * Added Raw Public Key (RFC7250) support. - * Added support for certificate compression (RFC8879), including - library support for Brotli and Zstandard compression. - * Implemented support for all five instances of EdDSA from RFC8032. - * Implemented SM4-XTS support. - * Implemented deterministic ECDSA signatures (RFC6979) support. - * Implemented AES-GCM-SIV (RFC8452) support. - * Implemented Hybrid Public Key Encryption (HPKE) as defined in RFC9180. - * Multiple new features and improvements of the CMP protocol support. - * Subject or issuer names in X.509 objects are now displayed as UTF-8 strings - by default. - * TCP Fast Open (RFC7413) support is available on Linux, macOS, and FreeBSD - where enabled and supported. +OpenSSL 3.2.0 is a feature release adding significant new functionality to +OpenSSL. + +This release incorporates the following potentially significant or incompatible +changes: + * The default SSL/TLS security level has been changed from 1 to 2. - * Full support for provider-based/pluggable signature algorithms in TLS 1.3 - operations as well as CMS and X.509 data structure support. With a suitable - provider this fully enables use of post-quantum/quantum-safe cryptography. - * It is now possible to use the IANA standard names in TLS cipher - configuration. + * The `x509`, `ca`, and `req` apps now always produce X.509v3 certificates. - * Support for Argon2d, Argon2i, Argon2id KDFs has been added along with - a basic thread pool implementation for select platforms. + + * Subject or issuer names in X.509 objects are now displayed as UTF-8 strings + by default. + +This release adds the following new features: + + * Support for client side QUIC, including support for + multiple streams (RFC 9000) + + * Support for Ed25519ctx, Ed25519ph and Ed448ph in addition + to existing support for Ed25519 and Ed448 (RFC 8032) + + * Support for deterministic ECDSA signatures (RFC 6979) + + * Support for AES-GCM-SIV, a nonce-misuse-resistant AEAD (RFC 8452) + + * Support for the Argon2 KDF, along with supporting thread pool + functionality (RFC 9106) + + * Support for Hybrid Public Key Encryption (HPKE) (RFC 9180) + + * Support for SM4-XTS + + * Support for Brainpool curves in TLS 1.3 + + * Support for TLS Raw Public Keys (RFC 7250) + + * Support for TCP Fast Open on Linux, macOS and FreeBSD, + where enabled and supported (RFC 7413) + + * Support for TLS certificate compression, including library + support for zlib, Brotli and zstd (RFC 8879) + + * Support for provider-based pluggable signature algorithms + in TLS 1.3 with supporting CMS and X.509 functionality + + With a suitable provider this enables the use of post-quantum/quantum-safe + cryptography. + + * Support for using the Windows system certificate store as a source of + trusted root certificates + + This is not yet enabled by default and must be activated using an + environment variable. This is likely to become enabled by default + in a future feature release. + + * Support for using the IANA standard names in TLS ciphersuite configuration + + * Multiple new features and improvements to CMP protocol support + +The following known issues are present in this release and will be rectified +in a future release: + + * Provider-based signature algorithms cannot be configured using the + SignatureAlgorithms configuration file parameter (#22761) + +This release incorporates the following documentation enhancements: + + * Added multiple tutorials on the OpenSSL library and in particular + on writing various clients (using TLS and QUIC protocols) with libssl + + See [OpenSSL Guide]. + +A more detailed list of changes in this release can be found in the +[CHANGES.md] file. + +Users interested in using the new QUIC functionality are encouraged to read the +[README file for QUIC][README-QUIC.md], which provides links to relevant +documentation and example code. + +As always, bug reports and issues relating to OpenSSL can be [filed on our issue +tracker][issue tracker]. OpenSSL 3.1 ----------- @@ -1686,3 +1741,7 @@ OpenSSL 0.9.x [CVE-2006-2940]: https://www.openssl.org/news/vulnerabilities.html#CVE-2006-2940 [CVE-2006-2937]: https://www.openssl.org/news/vulnerabilities.html#CVE-2006-2937 [CVE-2005-2969]: https://www.openssl.org/news/vulnerabilities.html#CVE-2005-2969 +[OpenSSL Guide]: https://www.openssl.org/docs/manmaster/man7/ossl-guide-introduction.html +[CHANGES.md]: ./CHANGES.md +[README-QUIC.md]: ./README-QUIC.md +[issue tracker]: https://github.com/openssl/openssl/issues -- Gitee From 06fdf3c2335bfe0765ebc23482d08acf56806da6 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Mon, 20 Nov 2023 15:07:09 +0000 Subject: [PATCH 15/31] Make CHANGES.md header more appropriate Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/22785) (cherry picked from commit d330fef1f1446c968e31803778bc7b3d067c7e99) Signed-off-by: fly2x --- CHANGES.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e7f9dc4c8a..2aa0617974 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,11 @@ OpenSSL CHANGES =============== -This is a high-level summary of the most important changes. -For a full list of changes, see the [git commit log][log] and -pick the appropriate release branch. +This is a detailed breakdown of significant changes. For a high-level overview +of changes in each release, see [NEWS.md](./NEWS.md). + +For a full list of changes, see the [git commit log][log] and pick the +appropriate release branch. [log]: https://github.com/openssl/openssl/commits/ -- Gitee From 5c798b9174bd69b684dc69fb24ac0d2b690f9a43 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 11:56:14 +0000 Subject: [PATCH 16/31] QUIC SRTM: Add SRTM Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- include/internal/quic_srtm.h | 109 ++++++++ ssl/quic/build.info | 1 + ssl/quic/quic_srtm.c | 520 +++++++++++++++++++++++++++++++++++ 3 files changed, 630 insertions(+) create mode 100644 include/internal/quic_srtm.h create mode 100644 ssl/quic/quic_srtm.c diff --git a/include/internal/quic_srtm.h b/include/internal/quic_srtm.h new file mode 100644 index 0000000000..9a2c18fdf3 --- /dev/null +++ b/include/internal/quic_srtm.h @@ -0,0 +1,109 @@ +/* +* 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 +*/ + +#ifndef OSSL_INTERNAL_QUIC_SRTM_H +# define OSSL_INTERNAL_QUIC_SRTM_H +# pragma once + +# include "internal/e_os.h" +# include "internal/time.h" +# include "internal/quic_types.h" +# include "internal/quic_wire.h" + +# ifndef OPENSSL_NO_QUIC + +/* + * QUIC Stateless Reset Token Manager + * ================================== + * + * The stateless reset token manager is responsible for mapping stateless reset + * tokens to connections. It is used to identify stateless reset tokens in + * incoming packets. In this regard it can be considered an alternate "routing" + * mechanism for incoming packets, and is somewhat analagous with the LCIDM, + * except that it uses SRTs to route rather than DCIDs. + * + * The SRTM specifically stores a bidirectional mapping of the form + * + * (opaque pointer, sequence number) [1] <-> [0..n] SRT + * + * The (opaque pointer, sequence number) tuple is used to refer to an entry (for + * example for the purposes of removing it later when it is no longer needed). + * Likewise, an entry can be looked up using SRT to get the opaque pointer and + * sequence number. + * + * It is important to note that the same SRT may exist multiple times and map to + * multiple (opaque pointer, sequence number) tuples, for example, if we + * initiate multiple connections to the same peer using the same local QUIC_PORT + * and the peer decides to behave bizarrely and issue the same SRT for both + * connections. It should not do this, but we have to be resilient against + * byzantine peer behaviour. Thus we are capable of storing multiple identical + * SRTs for different (opaque pointer, sequence number) keys. + * + * The SRTM supports arbitrary insertion, arbitrary deletion of specific keys + * identified by a (opaque pointer, sequence number) key, and mass deletion of + * all entries under a specific opaque pointer. It supports lookup by SRT to + * identify zero or more corresponding (opaque pointer, sequence number) tuples. + * + * The opaque pointer may be used for any purpose but is intended to represent a + * connection identity and must therefore be consistent (usefully comparable). + */ +typedef struct quic_srtm_st QUIC_SRTM; + +/* Creates a new empty SRTM instance. */ +QUIC_SRTM *ossl_quic_srtm_new(OSSL_LIB_CTX *libctx, const char *propq); + +/* Frees a SRTM instance. No-op if srtm is NULL. */ +void ossl_quic_srtm_free(QUIC_SRTM *srtm); + +/* + * Add a (opaque, seq_num) -> SRT entry to the SRTM. This operation fails if a + * SRT entry already exists with the same (opaque, seq_num) tuple. The token is + * copied. Returns 1 on success or 0 on failure. + */ +int ossl_quic_srtm_add(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, + const QUIC_STATELESS_RESET_TOKEN *token); + +/* + * Removes an entry by identifying it via its (opaque, seq_num) tuple. + * Returns 1 if the entry was found and removed, and 0 if it was not found. + */ +int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num); + +/* + * Removes all entries (opaque, *) with the given opaque pointer. + * + * Returns 1 on success and 0 on failure. If no entries with the given opaque + * pointer were found, this is considered a success condition. + */ +int ossl_quic_srtm_cull(QUIC_SRTM *strm, void *opaque); + +/* + * Looks up a SRT to find the corresponding opaque pointer and sequence number. + * An output field pointer can be set to NULL if it is not required. + * + * This function is designed to avoid exposing timing channels on token values + * or the contents of the SRT mapping. + * + * If there are several identical SRTs, idx can be used to get the nth entry. + * Call this function with idx set to 0 first, and keep calling it after + * incrementing idx until it returns 0. + * + * Returns 1 if an entry was found and 0 otherwise. + */ +int ossl_quic_srtm_lookup(QUIC_SRTM *srtm, + const QUIC_STATELESS_RESET_TOKEN *token, + size_t idx, + void **opaque, uint64_t *seq_num); + +/* Verify internal invariants and assert if they are not met. */ +void ossl_quic_srtm_check(const QUIC_SRTM *srtm); + +# endif + +#endif diff --git a/ssl/quic/build.info b/ssl/quic/build.info index 3a9e2d55af..866f581fa9 100644 --- a/ssl/quic/build.info +++ b/ssl/quic/build.info @@ -14,3 +14,4 @@ SOURCE[$LIBSSL]=quic_tserver.c SOURCE[$LIBSSL]=quic_tls.c SOURCE[$LIBSSL]=quic_thread_assist.c SOURCE[$LIBSSL]=quic_trace.c +SOURCE[$LIBSSL]=quic_srtm.c diff --git a/ssl/quic/quic_srtm.c b/ssl/quic/quic_srtm.c new file mode 100644 index 0000000000..d3639a4cc7 --- /dev/null +++ b/ssl/quic/quic_srtm.c @@ -0,0 +1,520 @@ +/* + * 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 "internal/quic_srtm.h" +#include +#include +#include + +/* + * QUIC Stateless Reset Token Manager + * ================================== + */ +typedef struct srtm_item_st SRTM_ITEM; + +#define BLINDED_SRT_LEN 16 + +DEFINE_LHASH_OF_EX(SRTM_ITEM); + +/* + * The SRTM is implemented using two LHASH instances, one matching opaque pointers to + * an item structure, and another matching a SRT-derived value to an item + * structure. Multiple items with different seq_num values under a given opaque, + * and duplicate SRTs, are handled using sorted singly-linked lists. + * + * The O(n) insert and lookup performance is tolerated on the basis that the + * total number of entries for a given opaque (total number of extant CIDs for a + * connection) should be quite small, and the QUIC protocol allows us to place a + * hard limit on this via the active_connection_id_limit TPARAM. Thus there is + * no risk of a large number of SRTs needing to be registered under a given + * opaque. + * + * It is expected one SRTM will exist per QUIC_PORT and track all SRTs across + * all connections for that QUIC_PORT. + */ +struct srtm_item_st { + SRTM_ITEM *next_by_srt_blinded; /* SORT BY opaque DESC */ + SRTM_ITEM *next_by_seq_num; /* SORT BY seq_num DESC */ + void *opaque; /* \__ unique identity for item */ + uint64_t seq_num; /* / */ + QUIC_STATELESS_RESET_TOKEN srt; + unsigned char srt_blinded[BLINDED_SRT_LEN]; /* H(srt) */ + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + uint32_t debug_token; +#endif +}; + +struct quic_srtm_st { + /* Crypto context used to calculate blinded SRTs H(srt). */ + EVP_MAC *blind_mac; + EVP_MAC_CTX *blind_ctx; /* kept with key */ + + LHASH_OF(SRTM_ITEM) *items_fwd; /* (opaque) -> SRTM_ITEM */ + LHASH_OF(SRTM_ITEM) *items_rev; /* (H(srt)) -> SRTM_ITEM */ +}; + +static unsigned long items_fwd_hash(const SRTM_ITEM *item) +{ + return (unsigned long)(uintptr_t)item->opaque; +} + +static int items_fwd_cmp(const SRTM_ITEM *a, const SRTM_ITEM *b) +{ + return a->opaque != b->opaque; +} + +static unsigned long items_rev_hash(const SRTM_ITEM *item) +{ + /* + * srt_blinded has already been through a crypto-grade hash function, so we + * can just use bits from that. + */ + unsigned long l; + + memcpy(&l, item->srt_blinded, sizeof(l)); + return l; +} + +static int items_rev_cmp(const SRTM_ITEM *a, const SRTM_ITEM *b) +{ + /* + * We don't really need to use CRYPTO_memcmp here as the relationship of + * srt_blinded to srt is already cryptographically obfuscated, but it + * doesn't hurt. + */ + return CRYPTO_memcmp(a->srt_blinded, b->srt_blinded, sizeof(a->srt_blinded)); +} + +QUIC_SRTM *ossl_quic_srtm_new(OSSL_LIB_CTX *libctx, const char *propq) +{ + QUIC_SRTM *srtm = NULL; + OSSL_PARAM params[3], *p = params; + unsigned char key[16]; + + if ((srtm = OPENSSL_zalloc(sizeof(*srtm))) == NULL) + return NULL; + + /* + * Construct our blinding context using a random key. Since this only exists + * for side channel mitigation, we don't need to make the key configurable + * and simply use a random key each time. We never reconfigure the + * EVP_MAC_CTX after setup so we don't need to keep the key around. + */ + if ((srtm->blind_mac = EVP_MAC_fetch(libctx, "CMAC", propq)) == NULL) + goto err; + + if ((srtm->blind_ctx = EVP_MAC_CTX_new(srtm->blind_mac)) == NULL) + goto err; + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, + "AES-128-CBC", 12); + if (propq != NULL) + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_PROPERTIES, + (char *)propq, 0); + + *p++ = OSSL_PARAM_construct_end(); + + if (RAND_bytes_ex(libctx, key, sizeof(key), sizeof(key) * 8) != 1) + goto err; + + if (!EVP_MAC_init(srtm->blind_ctx, key, sizeof(key), params)) + goto err; + + /* Create mappings. */ + if ((srtm->items_fwd = lh_SRTM_ITEM_new(items_fwd_hash, items_fwd_cmp)) == NULL + || (srtm->items_rev = lh_SRTM_ITEM_new(items_rev_hash, items_rev_cmp)) == NULL) + goto err; + + return srtm; + +err: + /* + * No cleansing of key needed as blinding exists only for side channel + * mitigation. + */ + ossl_quic_srtm_free(srtm); + return NULL; +} + +static void srtm_free_each(SRTM_ITEM *ihead) +{ + SRTM_ITEM *inext, *item = ihead; + + for (item = item->next_by_seq_num; item != NULL; item = inext) { + inext = item->next_by_seq_num; + OPENSSL_free(item); + } + + OPENSSL_free(ihead); +} + +void ossl_quic_srtm_free(QUIC_SRTM *srtm) +{ + if (srtm == NULL) + return; + + lh_SRTM_ITEM_free(srtm->items_rev); + if (srtm->items_fwd != NULL) { + lh_SRTM_ITEM_doall(srtm->items_fwd, srtm_free_each); + lh_SRTM_ITEM_free(srtm->items_fwd); + } + + EVP_MAC_CTX_free(srtm->blind_ctx); + EVP_MAC_free(srtm->blind_mac); + OPENSSL_free(srtm); +} + +/* + * Find a SRTM_ITEM by (opaque, seq_num). Returns NULL if no match. + * If head is non-NULL, writes the head of the relevant opaque list to *head if + * there is one. + * If prev is non-NULL, writes the previous node to *prev or NULL if it is + * the first item. + */ +static SRTM_ITEM *srtm_find(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, + SRTM_ITEM **head_p, SRTM_ITEM **prev_p) +{ + SRTM_ITEM key, *item = NULL, *prev = NULL; + + key.opaque = opaque; + + item = lh_SRTM_ITEM_retrieve(srtm->items_fwd, &key); + if (head_p != NULL) + *head_p = item; + + for (; item != NULL; prev = item, item = item->next_by_seq_num) + if (item->seq_num == seq_num) { + break; + } else if (item->seq_num < seq_num) { + /* + * List is sorted in descending order so there can't be any match + * after this. + */ + item = NULL; + break; + } + + if (prev_p != NULL) + *prev_p = prev; + + return item; +} + +/* + * Inserts a SRTM_ITEM into the singly-linked by-sequence-number linked list. + * The new head pointer is written to *new_head (which may or may not be + * unchanged). + */ +static void sorted_insert_seq_num(SRTM_ITEM *head, SRTM_ITEM *item, SRTM_ITEM **new_head) +{ + uint64_t seq_num = item->seq_num; + SRTM_ITEM *cur = head, **fixup = new_head; + + *new_head = head; + + while (cur != NULL && cur->seq_num > seq_num) { + fixup = &cur->next_by_seq_num; + cur = cur->next_by_seq_num; + } + + item->next_by_seq_num = *fixup; + *fixup = item; +} + +/* + * Inserts a SRTM_ITEM into the singly-linked by-SRT list. + * The new head pointer is written to *new_head (which may or may not be + * unchanged). + */ +static void sorted_insert_srt(SRTM_ITEM *head, SRTM_ITEM *item, SRTM_ITEM **new_head) +{ + uintptr_t opaque = (uintptr_t)item->opaque; + SRTM_ITEM *cur = head, **fixup = new_head; + + *new_head = head; + + while (cur != NULL && (uintptr_t)cur->opaque > opaque) { + fixup = &cur->next_by_srt_blinded; + cur = cur->next_by_srt_blinded; + } + + item->next_by_srt_blinded = *fixup; + *fixup = item; +} + +/* + * Computes the blinded SRT value used for internal lookup for side channel + * mitigation purposes. We compute this once as a cached value when an SRTM_ITEM + * is formed. + */ +static int srtm_compute_blinded(QUIC_SRTM *srtm, SRTM_ITEM *item, + const QUIC_STATELESS_RESET_TOKEN *token) +{ + size_t outl = 0; + + if (!EVP_MAC_init(srtm->blind_ctx, NULL, 0, NULL)) + return 0; + + if (!EVP_MAC_update(srtm->blind_ctx, (const unsigned char *)token, + sizeof(*token))) + return 0; + + if (!EVP_MAC_final(srtm->blind_ctx, item->srt_blinded, + &outl, sizeof(item->srt_blinded)) + || outl != sizeof(item->srt_blinded)) + return 0; + + return 1; +} + +int ossl_quic_srtm_add(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, + const QUIC_STATELESS_RESET_TOKEN *token) +{ + SRTM_ITEM *item = NULL, *head = NULL, *new_head, *r_item; + + /* (opaque, seq_num) duplicates not allowed */ + if ((item = srtm_find(srtm, opaque, seq_num, &head, NULL)) != NULL) + return 0; + + if ((item = OPENSSL_zalloc(sizeof(*item))) == NULL) + return 0; + + item->opaque = opaque; + item->seq_num = seq_num; + item->srt = *token; + if (!srtm_compute_blinded(srtm, item, &item->srt)) { + OPENSSL_free(item); + return 0; + } + + /* Add to forward mapping. */ + if (head == NULL) { + /* First item under this opaque */ + lh_SRTM_ITEM_insert(srtm->items_fwd, item); + assert(!lh_SRTM_ITEM_error(srtm->items_fwd)); + } else { + sorted_insert_seq_num(head, item, &new_head); + if (new_head != head) /* head changed, update in lhash */ + lh_SRTM_ITEM_insert(srtm->items_fwd, new_head); + } + + /* Add to reverse mapping. */ + r_item = lh_SRTM_ITEM_retrieve(srtm->items_rev, item); + if (r_item == NULL) { + /* First item under this blinded SRT */ + lh_SRTM_ITEM_insert(srtm->items_rev, item); + } else { + sorted_insert_srt(r_item, item, &new_head); + if (new_head != r_item) /* head changed, update in lhash */ + lh_SRTM_ITEM_insert(srtm->items_rev, new_head); + } + + return 1; +} + +/* Remove item from reverse mapping. */ +static void srtm_remove_from_rev(QUIC_SRTM *srtm, SRTM_ITEM *item) +{ + SRTM_ITEM *rh_item; + + rh_item = lh_SRTM_ITEM_retrieve(srtm->items_rev, item); + assert(rh_item != NULL); + if (rh_item == item) { + /* + * Change lhash to point to item after this one, or remove the entry if + * this is the last one. + */ + if (item->next_by_srt_blinded != NULL) + lh_SRTM_ITEM_insert(srtm->items_rev, item->next_by_srt_blinded); + else + lh_SRTM_ITEM_delete(srtm->items_rev, item); + } else { + /* Find our entry in the SRT list */ + for (; assert(rh_item != NULL), rh_item->next_by_srt_blinded != item; + rh_item = rh_item->next_by_srt_blinded); + rh_item->next_by_srt_blinded = item->next_by_srt_blinded; + } +} + +int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num) +{ + SRTM_ITEM *item, *prev = NULL; + + if ((item = srtm_find(srtm, opaque, seq_num, NULL, &prev)) == NULL) + /* No match */ + return 0; + + /* Remove from forward mapping. */ + if (prev == NULL) { + /* + * Change lhash to point to item after this one, or remove the entry if + * this is the last one. + */ + if (item->next_by_seq_num != NULL) + lh_SRTM_ITEM_insert(srtm->items_fwd, item->next_by_seq_num); + else + lh_SRTM_ITEM_delete(srtm->items_fwd, item); + } else { + prev->next_by_seq_num = item->next_by_seq_num; + } + + /* Remove from reverse mapping. */ + srtm_remove_from_rev(srtm, item); + + OPENSSL_free(item); + return 1; +} + +int ossl_quic_srtm_cull(QUIC_SRTM *srtm, void *opaque) +{ + SRTM_ITEM key, *item = NULL, *inext, *ihead; + + key.opaque = opaque; + + if ((ihead = lh_SRTM_ITEM_retrieve(srtm->items_fwd, &key)) == NULL) + return 1; /* nothing removed is a success condition */ + + for (item = ihead; item != NULL; item = inext) { + inext = item->next_by_seq_num; + if (item != ihead) { + srtm_remove_from_rev(srtm, item); + OPENSSL_free(item); + } + } + + lh_SRTM_ITEM_delete(srtm->items_fwd, ihead); + srtm_remove_from_rev(srtm, ihead); + OPENSSL_free(ihead); + return 1; +} + +int ossl_quic_srtm_lookup(QUIC_SRTM *srtm, + const QUIC_STATELESS_RESET_TOKEN *token, + size_t idx, + void **opaque, uint64_t *seq_num) +{ + SRTM_ITEM key, *item; + + if (!srtm_compute_blinded(srtm, &key, token)) + return 0; + + item = lh_SRTM_ITEM_retrieve(srtm->items_rev, &key); + for (; idx > 0 && item != NULL; --idx, item = item->next_by_srt_blinded); + if (item == NULL) + return 0; + + if (opaque != NULL) + *opaque = item->opaque; + if (seq_num != NULL) + *seq_num = item->seq_num; + + return 1; +} + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + +static uint32_t token_next = 0x5eadbeef; +static size_t tokens_seen; + +struct check_args { + uint32_t token; + int mode; +}; + +static void check_mark(SRTM_ITEM *item, void *arg) +{ + struct check_args *arg_ = arg; + uint32_t token = arg_->token; + uint64_t prev_seq_num; + void *prev_opaque; + int have_prev = 0; + + assert(item != NULL); + + while (item != NULL) { + if (have_prev) { + assert(!(item->opaque == prev_opaque && item->seq_num == prev_seq_num)); + if (!arg_->mode) + assert(item->opaque != prev_opaque || item->seq_num < prev_seq_num); + } + + ++tokens_seen; + item->debug_token = token; + prev_opaque = item->opaque; + prev_seq_num = item->seq_num; + have_prev = 1; + + if (arg_->mode) + item = item->next_by_srt_blinded; + else + item = item->next_by_seq_num; + } +} + +static void check_count(SRTM_ITEM *item, void *arg) +{ + struct check_args *arg_ = arg; + uint32_t token = arg_->token; + + assert(item != NULL); + + while (item != NULL) { + ++tokens_seen; + assert(item->debug_token == token); + + if (arg_->mode) + item = item->next_by_seq_num; + else + item = item->next_by_srt_blinded; + } +} + +#endif + +void ossl_quic_srtm_check(const QUIC_SRTM *srtm) +{ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + struct check_args args = {0}; + size_t tokens_expected, tokens_expected_old; + + args.token = token_next; + ++token_next; + + assert(srtm != NULL); + assert(srtm->blind_mac != NULL); + assert(srtm->blind_ctx != NULL); + assert(srtm->items_fwd != NULL); + assert(srtm->items_rev != NULL); + + tokens_seen = 0; + lh_SRTM_ITEM_doall_arg(srtm->items_fwd, check_mark, &args); + + tokens_expected = tokens_seen; + tokens_seen = 0; + lh_SRTM_ITEM_doall_arg(srtm->items_rev, check_count, &args); + + assert(tokens_seen == tokens_expected); + tokens_expected_old = tokens_expected; + + args.token = token_next; + ++token_next; + + args.mode = 1; + tokens_seen = 0; + lh_SRTM_ITEM_doall_arg(srtm->items_rev, check_mark, &args); + + tokens_expected = tokens_seen; + tokens_seen = 0; + lh_SRTM_ITEM_doall_arg(srtm->items_fwd, check_count, &args); + + assert(tokens_seen == tokens_expected); + assert(tokens_seen == tokens_expected_old); +#endif +} -- Gitee From f260189be31092f1a0edce179717f995e5da0888 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 11:56:29 +0000 Subject: [PATCH 17/31] QUIC SRTM: Add fuzzer for SRTM Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- fuzz/build.info | 12 ++- fuzz/quic-srtm.c | 119 ++++++++++++++++++++++++++ test/recipes/99-test_fuzz_quic_srtm.t | 25 ++++++ 3 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 fuzz/quic-srtm.c create mode 100644 test/recipes/99-test_fuzz_quic_srtm.t diff --git a/fuzz/build.info b/fuzz/build.info index de7cadc79e..bbbc7c9654 100644 --- a/fuzz/build.info +++ b/fuzz/build.info @@ -30,7 +30,7 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] ENDIF IF[{- !$disabled{"quic"} -}] - PROGRAMS{noinst}=quic-client + PROGRAMS{noinst}=quic-client quic-srtm ENDIF SOURCE[asn1]=asn1.c driver.c fuzz_rand.c @@ -97,6 +97,10 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] INCLUDE[quic-client]=../include {- $ex_inc -} DEPEND[quic-client]=../libcrypto.a ../libssl.a {- $ex_lib -} + SOURCE[quic-srtm]=quic-srtm.c driver.c fuzz_rand.c + INCLUDE[quic-srtm]=../include {- $ex_inc -} + DEPEND[quic-srtm]=../libcrypto.a ../libssl.a {- $ex_lib -} + SOURCE[server]=server.c driver.c fuzz_rand.c INCLUDE[server]=../include {- $ex_inc -} DEPEND[server]=../libcrypto ../libssl {- $ex_lib -} @@ -128,7 +132,7 @@ IF[{- !$disabled{tests} -}] ENDIF IF[{- !$disabled{"quic"} -}] - PROGRAMS{noinst}=quic-client-test + PROGRAMS{noinst}=quic-client-test quic-srtm-test ENDIF SOURCE[asn1-test]=asn1.c test-corpus.c fuzz_rand.c @@ -196,6 +200,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[quic-client-test]=../include DEPEND[quic-client-test]=../libcrypto.a ../libssl.a + SOURCE[quic-srtm-test]=quic-srtm.c test-corpus.c fuzz_rand.c + INCLUDE[quic-srtm-test]=../include + DEPEND[quic-srtm-test]=../libcrypto.a ../libssl.a + SOURCE[server-test]=server.c test-corpus.c fuzz_rand.c INCLUDE[server-test]=../include DEPEND[server-test]=../libcrypto ../libssl diff --git a/fuzz/quic-srtm.c b/fuzz/quic-srtm.c new file mode 100644 index 0000000000..eb676c2279 --- /dev/null +++ b/fuzz/quic-srtm.c @@ -0,0 +1,119 @@ +/* + * Copyright 2016-2022 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 may obtain a copy of the License at + * https://www.openssl.org/source/license.html + * or in the file LICENSE in the source distribution. + */ + +#include +#include +#include +#include "fuzzer.h" +#include "internal/quic_srtm.h" + +int FuzzerInitialize(int *argc, char ***argv) +{ + FuzzerSetRand(); + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ASYNC, NULL); + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); + ERR_clear_error(); + return 1; +} + +/* + * Fuzzer input "protocol": + * Big endian + * Zero or more of: + * ADD - u8(0x00) u64(opaque) u64(seq_num) u128(token) + * REMOVE - u8(0x01) u64(opaque) u64(seq_num) + * CULL - u8(0x02) u64(opaque) + * LOOKUP - u8(0x03) u128(token) u64(idx) + */ +enum { + CMD_ADD, + CMD_REMOVE, + CMD_CULL, + CMD_LOOKUP +}; + +int FuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + int rc = 0; + QUIC_SRTM *srtm = NULL; + PACKET pkt; + unsigned int cmd; + uint64_t arg_opaque, arg_seq_num, arg_idx; + QUIC_STATELESS_RESET_TOKEN arg_token; + + if ((srtm = ossl_quic_srtm_new(NULL, NULL)) == NULL) { + rc = -1; + goto err; + } + + if (!PACKET_buf_init(&pkt, buf, len)) + goto err; + + while (PACKET_remaining(&pkt) > 0) { + if (!PACKET_get_1(&pkt, &cmd)) + goto err; + + switch (cmd) { + case CMD_ADD: + if (!PACKET_get_net_8(&pkt, &arg_opaque) + || !PACKET_get_net_8(&pkt, &arg_seq_num) + || !PACKET_copy_bytes(&pkt, arg_token.token, + sizeof(arg_token.token))) + continue; /* just stop */ + + ossl_quic_srtm_add(srtm, (void *)(uintptr_t)arg_opaque, + arg_seq_num, &arg_token); + ossl_quic_srtm_check(srtm); + break; + + case CMD_REMOVE: + if (!PACKET_get_net_8(&pkt, &arg_opaque) + || !PACKET_get_net_8(&pkt, &arg_seq_num)) + continue; /* just stop */ + + ossl_quic_srtm_remove(srtm, (void *)(uintptr_t)arg_opaque, + arg_seq_num); + ossl_quic_srtm_check(srtm); + break; + + case CMD_CULL: + if (!PACKET_get_net_8(&pkt, &arg_opaque)) + continue; /* just stop */ + + ossl_quic_srtm_cull(srtm, (void *)(uintptr_t)arg_opaque); + ossl_quic_srtm_check(srtm); + break; + + case CMD_LOOKUP: + if (!PACKET_copy_bytes(&pkt, arg_token.token, + sizeof(arg_token.token)) + || !PACKET_get_net_8(&pkt, &arg_idx)) + continue; /* just stop */ + + ossl_quic_srtm_lookup(srtm, &arg_token, (size_t)arg_idx, + NULL, NULL); + ossl_quic_srtm_check(srtm); + break; + + default: + /* Other bytes are treated as no-ops */ + continue; + } + } + +err: + ossl_quic_srtm_free(srtm); + return rc; +} + +void FuzzerCleanup(void) +{ + FuzzerClearRand(); +} diff --git a/test/recipes/99-test_fuzz_quic_srtm.t b/test/recipes/99-test_fuzz_quic_srtm.t new file mode 100644 index 0000000000..de27a8764a --- /dev/null +++ b/test/recipes/99-test_fuzz_quic_srtm.t @@ -0,0 +1,25 @@ +#!/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 strict; +use warnings; + +use OpenSSL::Test qw/:DEFAULT srctop_file/; +use OpenSSL::Test::Utils; + +my $fuzzer = "quic-srtm"; +setup("test_fuzz_${fuzzer}"); + +plan skip_all => "This test requires quic support" + if disabled("quic"); + +plan tests => 2; # one more due to below require_ok(...) + +require_ok(srctop_file('test','recipes','fuzz.pl')); + +fuzz_ok($fuzzer); -- Gitee From dfddd7788bceeb420e982feab4bbcfecbba82c22 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 11:56:40 +0000 Subject: [PATCH 18/31] Fuzzing: Fix helper shebang on NixOS Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- fuzz/helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/helper.py b/fuzz/helper.py index 9185b17228..db6e8edc1e 100755 --- a/fuzz/helper.py +++ b/fuzz/helper.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved. # -- Gitee From e53fa5f5a73c8a6488e43cb32eb97b105b2fad23 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 12:23:14 +0000 Subject: [PATCH 19/31] QUIC SRTM: Add test Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- test/build.info | 5 ++ test/quic_srtm_test.c | 84 ++++++++++++++++++++++++++++++++ test/recipes/70-test_quic_srtm.t | 19 ++++++++ 3 files changed, 108 insertions(+) create mode 100644 test/quic_srtm_test.c create mode 100644 test/recipes/70-test_quic_srtm.t diff --git a/test/build.info b/test/build.info index 10f29b19cf..cba48e6db0 100644 --- a/test/build.info +++ b/test/build.info @@ -328,6 +328,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[quic_txpim_test]=../include ../apps/include DEPEND[quic_txpim_test]=../libcrypto.a ../libssl.a libtestutil.a + SOURCE[quic_srtm_test]=quic_srtm_test.c + INCLUDE[quic_srtm_test]=../include ../apps/include + DEPEND[quic_srtm_test]=../libcrypto.a ../libssl.a libtestutil.a + SOURCE[quic_fifd_test]=quic_fifd_test.c cc_dummy.c INCLUDE[quic_fifd_test]=../include ../apps/include DEPEND[quic_fifd_test]=../libcrypto.a ../libssl.a libtestutil.a @@ -1129,6 +1133,7 @@ ENDIF IF[{- !$disabled{'quic'} -}] PROGRAMS{noinst}=quic_wire_test quic_ackm_test quic_record_test PROGRAMS{noinst}=quic_fc_test quic_stream_test quic_cfq_test quic_txpim_test + PROGRAMS{noinst}=quic_srtm_test PROGRAMS{noinst}=quic_fifd_test quic_txp_test quic_tserver_test PROGRAMS{noinst}=quic_client_test quic_cc_test quic_multistream_test ENDIF diff --git a/test/quic_srtm_test.c b/test/quic_srtm_test.c new file mode 100644 index 0000000000..558e323cd6 --- /dev/null +++ b/test/quic_srtm_test.c @@ -0,0 +1,84 @@ +/* + * 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 "internal/quic_srtm.h" +#include "testutil.h" + +static char ptrs[8]; + +static const QUIC_STATELESS_RESET_TOKEN token_1 = {{ + 0x01, 0x02, 0x03, 0x04 +}}; + +static const QUIC_STATELESS_RESET_TOKEN token_2 = {{ + 0x01, 0x02, 0x03, 0x05 +}}; + +static int test_srtm(void) +{ + int testresult = 0; + QUIC_SRTM *srtm; + void *opaque = NULL; + uint64_t seq_num = 0; + + if (!TEST_ptr(srtm = ossl_quic_srtm_new(NULL, NULL))) + goto err; + + if (!TEST_true(ossl_quic_srtm_add(srtm, ptrs + 0, 0, &token_1)) + || !TEST_false(ossl_quic_srtm_add(srtm, ptrs + 0, 0, &token_1)) + || !TEST_false(ossl_quic_srtm_remove(srtm, ptrs + 0, 1)) + || !TEST_false(ossl_quic_srtm_remove(srtm, ptrs + 3, 0)) + || !TEST_true(ossl_quic_srtm_cull(srtm, ptrs + 3)) + || !TEST_true(ossl_quic_srtm_cull(srtm, ptrs + 3)) + || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 0, 1, &token_1)) + || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 0, 2, &token_1)) + || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 0, 3, &token_1)) + || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 1, 0, &token_1)) + || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 2, 0, &token_2)) + || !TEST_true(ossl_quic_srtm_add(srtm, ptrs + 3, 3, &token_2)) + || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 3, 3)) + || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_1, 0, &opaque, &seq_num)) + || !TEST_ptr_eq(opaque, ptrs + 1) + || !TEST_uint64_t_eq(seq_num, 0) + || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_1, 1, &opaque, &seq_num)) + || !TEST_ptr_eq(opaque, ptrs + 0) + || !TEST_uint64_t_eq(seq_num, 3) + || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_1, 2, &opaque, &seq_num)) + || !TEST_ptr_eq(opaque, ptrs + 0) + || !TEST_uint64_t_eq(seq_num, 2) + || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_1, 3, &opaque, &seq_num)) + || !TEST_ptr_eq(opaque, ptrs + 0) + || !TEST_uint64_t_eq(seq_num, 1) + || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_1, 4, &opaque, &seq_num)) + || !TEST_ptr_eq(opaque, ptrs + 0) + || !TEST_uint64_t_eq(seq_num, 0) + || !TEST_false(ossl_quic_srtm_lookup(srtm, &token_1, 5, &opaque, &seq_num)) + || !TEST_true(ossl_quic_srtm_cull(srtm, ptrs + 0)) + || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_1, 0, &opaque, &seq_num)) + || !TEST_ptr_eq(opaque, ptrs + 1) + || !TEST_uint64_t_eq(seq_num, 0) + || !TEST_true(ossl_quic_srtm_lookup(srtm, &token_2, 0, &opaque, &seq_num)) + || !TEST_ptr_eq(opaque, ptrs + 2) + || !TEST_uint64_t_eq(seq_num, 0) + || !TEST_true(ossl_quic_srtm_remove(srtm, ptrs + 2, 0)) + || !TEST_false(ossl_quic_srtm_lookup(srtm, &token_2, 0, &opaque, &seq_num)) + ) + goto err; + + testresult = 1; +err: + ossl_quic_srtm_free(srtm); + return testresult; +} + +int setup_tests(void) +{ + ADD_TEST(test_srtm); + return 1; +} diff --git a/test/recipes/70-test_quic_srtm.t b/test/recipes/70-test_quic_srtm.t new file mode 100644 index 0000000000..4b6818ab92 --- /dev/null +++ b/test/recipes/70-test_quic_srtm.t @@ -0,0 +1,19 @@ +#! /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; +use OpenSSL::Test::Utils; + +setup("test_quic_srtm"); + +plan skip_all => "QUIC protocol is not supported by this OpenSSL build" + if disabled('quic'); + +plan tests => 1; + +ok(run(test(["quic_srtm_test"]))); -- Gitee From 3269ef60e06ea0b9270ad6baf0cca6316c92cf67 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 14:14:14 +0000 Subject: [PATCH 20/31] QUIC SRTM: Amend glossary Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- doc/designs/quic-design/glossary.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/designs/quic-design/glossary.md b/doc/designs/quic-design/glossary.md index 1f947ef37d..fdaa822196 100644 --- a/doc/designs/quic-design/glossary.md +++ b/doc/designs/quic-design/glossary.md @@ -201,6 +201,11 @@ forming. This is the CMPPL minus any bytes we have already put into the payload. **SCID:** Source Connection ID. Found in some QUIC packet headers. +**SRT:** Stateless reset token. + +**SRTM:** Stateless reset token manager. Object which tracks SRTs we have +received. + **SSTREAM:** Send stream. Internal send buffer management object used to store data which has been passed to libssl for sending but which has not yet been transmitted, or not yet been acknowledged. -- Gitee From 6cd44b889eabf0de2a4325564beeb12ebb59eefa Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 15:38:27 +0000 Subject: [PATCH 21/31] QUIC SRTM: Update fuzz corpora Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- fuzz/corpora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/corpora b/fuzz/corpora index d600d8ed1b..9b8dafa129 160000 --- a/fuzz/corpora +++ b/fuzz/corpora @@ -1 +1 @@ -Subproject commit d600d8ed1b286040019b3c1ece629b5b174eb444 +Subproject commit 9b8dafa12966c856e57e507254eb8f4fa081e1de -- Gitee From e6223705f08b0895cc1371b79c0fcd5840f355df Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Fri, 3 Nov 2023 18:18:36 +0000 Subject: [PATCH 22/31] QUIC SRTM: Harden SRTM in event of allocation failure Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- ssl/quic/quic_srtm.c | 86 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/ssl/quic/quic_srtm.c b/ssl/quic/quic_srtm.c index d3639a4cc7..c64f81745b 100644 --- a/ssl/quic/quic_srtm.c +++ b/ssl/quic/quic_srtm.c @@ -58,6 +58,12 @@ struct quic_srtm_st { LHASH_OF(SRTM_ITEM) *items_fwd; /* (opaque) -> SRTM_ITEM */ LHASH_OF(SRTM_ITEM) *items_rev; /* (H(srt)) -> SRTM_ITEM */ + + /* + * Monotonically transitions to 1 in event of allocation failure. The only + * valid operation on such an object is to free it. + */ + unsigned int alloc_failed : 1; }; static unsigned long items_fwd_hash(const SRTM_ITEM *item) @@ -85,11 +91,20 @@ static unsigned long items_rev_hash(const SRTM_ITEM *item) static int items_rev_cmp(const SRTM_ITEM *a, const SRTM_ITEM *b) { /* - * We don't really need to use CRYPTO_memcmp here as the relationship of - * srt_blinded to srt is already cryptographically obfuscated, but it - * doesn't hurt. + * We don't need to use CRYPTO_memcmp here as the relationship of + * srt_blinded to srt is already cryptographically obfuscated. */ - return CRYPTO_memcmp(a->srt_blinded, b->srt_blinded, sizeof(a->srt_blinded)); + return memcmp(a->srt_blinded, b->srt_blinded, sizeof(a->srt_blinded)); +} + +static int srtm_check_lh(QUIC_SRTM *srtm, LHASH_OF(SRTM_ITEM) *lh) +{ + if (lh_SRTM_ITEM_error(lh)) { + srtm->alloc_failed = 1; + return 0; + } + + return 1; } QUIC_SRTM *ossl_quic_srtm_new(OSSL_LIB_CTX *libctx, const char *propq) @@ -121,7 +136,7 @@ QUIC_SRTM *ossl_quic_srtm_new(OSSL_LIB_CTX *libctx, const char *propq) *p++ = OSSL_PARAM_construct_end(); - if (RAND_bytes_ex(libctx, key, sizeof(key), sizeof(key) * 8) != 1) + if (RAND_priv_bytes_ex(libctx, key, sizeof(key), sizeof(key) * 8) != 1) goto err; if (!EVP_MAC_init(srtm->blind_ctx, key, sizeof(key), params)) @@ -279,6 +294,9 @@ int ossl_quic_srtm_add(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, { SRTM_ITEM *item = NULL, *head = NULL, *new_head, *r_item; + if (srtm->alloc_failed) + return 0; + /* (opaque, seq_num) duplicates not allowed */ if ((item = srtm_find(srtm, opaque, seq_num, &head, NULL)) != NULL) return 0; @@ -298,11 +316,19 @@ int ossl_quic_srtm_add(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, if (head == NULL) { /* First item under this opaque */ lh_SRTM_ITEM_insert(srtm->items_fwd, item); - assert(!lh_SRTM_ITEM_error(srtm->items_fwd)); + if (!srtm_check_lh(srtm, srtm->items_fwd)) { + OPENSSL_free(item); + return 0; + } } else { sorted_insert_seq_num(head, item, &new_head); - if (new_head != head) /* head changed, update in lhash */ + if (new_head != head) { /* head changed, update in lhash */ lh_SRTM_ITEM_insert(srtm->items_fwd, new_head); + if (!srtm_check_lh(srtm, srtm->items_fwd)) { + OPENSSL_free(item); + return 0; + } + } } /* Add to reverse mapping. */ @@ -310,17 +336,29 @@ int ossl_quic_srtm_add(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num, if (r_item == NULL) { /* First item under this blinded SRT */ lh_SRTM_ITEM_insert(srtm->items_rev, item); + if (!srtm_check_lh(srtm, srtm->items_rev)) + /* + * Can't free the item now as we would have to undo the insertion + * into the forward mapping which would require an insert operation + * to restore the previous value. which might also fail. However, + * the item will be freed OK when we free the entire SRTM. + */ + return 0; } else { sorted_insert_srt(r_item, item, &new_head); - if (new_head != r_item) /* head changed, update in lhash */ + if (new_head != r_item) { /* head changed, update in lhash */ lh_SRTM_ITEM_insert(srtm->items_rev, new_head); + if (!srtm_check_lh(srtm, srtm->items_rev)) + /* As above. */ + return 0; + } } return 1; } /* Remove item from reverse mapping. */ -static void srtm_remove_from_rev(QUIC_SRTM *srtm, SRTM_ITEM *item) +static int srtm_remove_from_rev(QUIC_SRTM *srtm, SRTM_ITEM *item) { SRTM_ITEM *rh_item; @@ -331,22 +369,30 @@ static void srtm_remove_from_rev(QUIC_SRTM *srtm, SRTM_ITEM *item) * Change lhash to point to item after this one, or remove the entry if * this is the last one. */ - if (item->next_by_srt_blinded != NULL) + if (item->next_by_srt_blinded != NULL) { lh_SRTM_ITEM_insert(srtm->items_rev, item->next_by_srt_blinded); - else + if (!srtm_check_lh(srtm, srtm->items_rev)) + return 0; + } else { lh_SRTM_ITEM_delete(srtm->items_rev, item); + } } else { /* Find our entry in the SRT list */ - for (; assert(rh_item != NULL), rh_item->next_by_srt_blinded != item; + for (; rh_item->next_by_srt_blinded != item; rh_item = rh_item->next_by_srt_blinded); rh_item->next_by_srt_blinded = item->next_by_srt_blinded; } + + return 1; } int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num) { SRTM_ITEM *item, *prev = NULL; + if (srtm->alloc_failed) + return 0; + if ((item = srtm_find(srtm, opaque, seq_num, NULL, &prev)) == NULL) /* No match */ return 0; @@ -357,16 +403,20 @@ int ossl_quic_srtm_remove(QUIC_SRTM *srtm, void *opaque, uint64_t seq_num) * Change lhash to point to item after this one, or remove the entry if * this is the last one. */ - if (item->next_by_seq_num != NULL) + if (item->next_by_seq_num != NULL) { lh_SRTM_ITEM_insert(srtm->items_fwd, item->next_by_seq_num); - else + if (!srtm_check_lh(srtm, srtm->items_fwd)) + return 0; + } else { lh_SRTM_ITEM_delete(srtm->items_fwd, item); + } } else { prev->next_by_seq_num = item->next_by_seq_num; } /* Remove from reverse mapping. */ - srtm_remove_from_rev(srtm, item); + if (!srtm_remove_from_rev(srtm, item)) + return 0; OPENSSL_free(item); return 1; @@ -378,6 +428,9 @@ int ossl_quic_srtm_cull(QUIC_SRTM *srtm, void *opaque) key.opaque = opaque; + if (srtm->alloc_failed) + return 0; + if ((ihead = lh_SRTM_ITEM_retrieve(srtm->items_fwd, &key)) == NULL) return 1; /* nothing removed is a success condition */ @@ -402,6 +455,9 @@ int ossl_quic_srtm_lookup(QUIC_SRTM *srtm, { SRTM_ITEM key, *item; + if (srtm->alloc_failed) + return 0; + if (!srtm_compute_blinded(srtm, &key, token)) return 0; -- Gitee From 06e4483a191b51c0d491be05fb4df95be205cbae Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Tue, 14 Nov 2023 11:03:46 +0000 Subject: [PATCH 23/31] QUIC SRTM: Switch to using AES-128-ECB Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22612) Signed-off-by: fly2x --- ssl/quic/quic_srtm.c | 57 ++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/ssl/quic/quic_srtm.c b/ssl/quic/quic_srtm.c index c64f81745b..faa667253e 100644 --- a/ssl/quic/quic_srtm.c +++ b/ssl/quic/quic_srtm.c @@ -8,6 +8,7 @@ */ #include "internal/quic_srtm.h" +#include "internal/common.h" #include #include #include @@ -53,8 +54,7 @@ struct srtm_item_st { struct quic_srtm_st { /* Crypto context used to calculate blinded SRTs H(srt). */ - EVP_MAC *blind_mac; - EVP_MAC_CTX *blind_ctx; /* kept with key */ + EVP_CIPHER_CTX *blind_ctx; /* kept with key */ LHASH_OF(SRTM_ITEM) *items_fwd; /* (opaque) -> SRTM_ITEM */ LHASH_OF(SRTM_ITEM) *items_rev; /* (H(srt)) -> SRTM_ITEM */ @@ -110,37 +110,27 @@ static int srtm_check_lh(QUIC_SRTM *srtm, LHASH_OF(SRTM_ITEM) *lh) QUIC_SRTM *ossl_quic_srtm_new(OSSL_LIB_CTX *libctx, const char *propq) { QUIC_SRTM *srtm = NULL; - OSSL_PARAM params[3], *p = params; unsigned char key[16]; + EVP_CIPHER *ecb = NULL; + + if (RAND_priv_bytes_ex(libctx, key, sizeof(key), sizeof(key) * 8) != 1) + goto err; if ((srtm = OPENSSL_zalloc(sizeof(*srtm))) == NULL) return NULL; - /* - * Construct our blinding context using a random key. Since this only exists - * for side channel mitigation, we don't need to make the key configurable - * and simply use a random key each time. We never reconfigure the - * EVP_MAC_CTX after setup so we don't need to keep the key around. - */ - if ((srtm->blind_mac = EVP_MAC_fetch(libctx, "CMAC", propq)) == NULL) + /* Use AES-128-ECB as a permutation over 128-bit SRTs. */ + if ((ecb = EVP_CIPHER_fetch(libctx, "AES-128-ECB", propq)) == NULL) goto err; - if ((srtm->blind_ctx = EVP_MAC_CTX_new(srtm->blind_mac)) == NULL) + if ((srtm->blind_ctx = EVP_CIPHER_CTX_new()) == NULL) goto err; - *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, - "AES-128-CBC", 12); - if (propq != NULL) - *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_PROPERTIES, - (char *)propq, 0); - - *p++ = OSSL_PARAM_construct_end(); - - if (RAND_priv_bytes_ex(libctx, key, sizeof(key), sizeof(key) * 8) != 1) + if (!EVP_EncryptInit_ex2(srtm->blind_ctx, ecb, key, NULL, NULL)) goto err; - if (!EVP_MAC_init(srtm->blind_ctx, key, sizeof(key), params)) - goto err; + EVP_CIPHER_free(ecb); + ecb = NULL; /* Create mappings. */ if ((srtm->items_fwd = lh_SRTM_ITEM_new(items_fwd_hash, items_fwd_cmp)) == NULL @@ -155,6 +145,7 @@ err: * mitigation. */ ossl_quic_srtm_free(srtm); + EVP_CIPHER_free(ecb); return NULL; } @@ -181,8 +172,7 @@ void ossl_quic_srtm_free(QUIC_SRTM *srtm) lh_SRTM_ITEM_free(srtm->items_fwd); } - EVP_MAC_CTX_free(srtm->blind_ctx); - EVP_MAC_free(srtm->blind_mac); + EVP_CIPHER_CTX_free(srtm->blind_ctx); OPENSSL_free(srtm); } @@ -272,18 +262,18 @@ static void sorted_insert_srt(SRTM_ITEM *head, SRTM_ITEM *item, SRTM_ITEM **new_ static int srtm_compute_blinded(QUIC_SRTM *srtm, SRTM_ITEM *item, const QUIC_STATELESS_RESET_TOKEN *token) { - size_t outl = 0; - - if (!EVP_MAC_init(srtm->blind_ctx, NULL, 0, NULL)) - return 0; + int outl = 0; - if (!EVP_MAC_update(srtm->blind_ctx, (const unsigned char *)token, - sizeof(*token))) + /* + * We use AES-128-ECB as a permutation using a random key to facilitate + * blinding for side-channel purposes. Encrypt the token as a single AES + * block. + */ + if (!EVP_EncryptUpdate(srtm->blind_ctx, item->srt_blinded, &outl, + (const unsigned char *)token, sizeof(*token))) return 0; - if (!EVP_MAC_final(srtm->blind_ctx, item->srt_blinded, - &outl, sizeof(item->srt_blinded)) - || outl != sizeof(item->srt_blinded)) + if (!ossl_assert(outl == sizeof(*token))) return 0; return 1; @@ -544,7 +534,6 @@ void ossl_quic_srtm_check(const QUIC_SRTM *srtm) ++token_next; assert(srtm != NULL); - assert(srtm->blind_mac != NULL); assert(srtm->blind_ctx != NULL); assert(srtm->items_fwd != NULL); assert(srtm->items_rev != NULL); -- Gitee From a73e2d91bc2704c2850e51dfdc03d21308cca1d3 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Thu, 23 Nov 2023 14:50:03 +0000 Subject: [PATCH 24/31] Update fuzz corpora Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22790) Signed-off-by: fly2x --- fuzz/corpora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/corpora b/fuzz/corpora index 9b8dafa129..8663e2d71f 160000 --- a/fuzz/corpora +++ b/fuzz/corpora @@ -1 +1 @@ -Subproject commit 9b8dafa12966c856e57e507254eb8f4fa081e1de +Subproject commit 8663e2d71fa934e7846fa4c5fb6de7cb3afc53fd -- Gitee From 2315a5ea62d54494c1fafaa066b7540c2ba8a952 Mon Sep 17 00:00:00 2001 From: Huiyue Xu Date: Wed, 22 Nov 2023 09:55:27 +0800 Subject: [PATCH 25/31] Fix a possible memory leak in SM2 provider ctx->propq that strdup from input parameter propq in sm2sig_newctx, is not released. It should be released in sm2sig_freectx and copied to dstctx in sm2sig_dupctx. And dstctx->id and dstctx->propq should be set NULL to avoid releasing id/propq of srcctx when err occurs. Signed-off-by: Huiyue Xu Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Paul Yang Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22796) Signed-off-by: fly2x --- providers/implementations/signature/sm2_sig.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/providers/implementations/signature/sm2_sig.c b/providers/implementations/signature/sm2_sig.c index a61fd0864f..479e4eebe8 100644 --- a/providers/implementations/signature/sm2_sig.c +++ b/providers/implementations/signature/sm2_sig.c @@ -329,6 +329,7 @@ static void sm2sig_freectx(void *vpsm2ctx) free_md(ctx); EC_KEY_free(ctx->ec); + OPENSSL_free(ctx->propq); OPENSSL_free(ctx->id); OPENSSL_free(ctx); } @@ -344,13 +345,21 @@ static void *sm2sig_dupctx(void *vpsm2ctx) *dstctx = *srcctx; dstctx->ec = NULL; + dstctx->propq = NULL; dstctx->md = NULL; dstctx->mdctx = NULL; + dstctx->id = NULL; if (srcctx->ec != NULL && !EC_KEY_up_ref(srcctx->ec)) goto err; dstctx->ec = srcctx->ec; + if (srcctx->propq != NULL) { + dstctx->propq = OPENSSL_strdup(srcctx->propq); + if (dstctx->propq == NULL) + goto err; + } + if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md)) goto err; dstctx->md = srcctx->md; -- Gitee From 1741c949ba0988d0f5d9ec6421adbd8ca4f2638a Mon Sep 17 00:00:00 2001 From: James Muir Date: Tue, 21 Nov 2023 00:09:38 -0500 Subject: [PATCH 26/31] speed: make hmac(sha256) the default hmac prefer hmac(sha256) rather than hmac(md5). Also, drop the "skip_hmac" label. If we are supposed to do hmac(hash_func) and hash_func cannot be found, then error out immediately. Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/22793) Signed-off-by: fly2x --- CHANGES.md | 5 +++++ apps/speed.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2aa0617974..81044b3d70 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,6 +28,11 @@ OpenSSL 3.3 ### Changes between 3.2 and 3.3 [xx XXX xxxx] + * In `openssl speed`, changed the default hash function used with `hmac` from + `md5` to `sha256`. + + *James Muir* + * The build of exporters (such as `.pc` files for pkg-config) cleaned up to be less hard coded in the build file templates, and to allow easier addition of more exporters. With that, an exporter for CMake is also diff --git a/apps/speed.c b/apps/speed.c index 57aeb67bf8..d1c61d72d0 100644 --- a/apps/speed.c +++ b/apps/speed.c @@ -310,7 +310,7 @@ enum { /* name of algorithms to test. MUST BE KEEP IN SYNC with above enum ! */ static const char *names[ALGOR_NUM] = { "md2", "mdc2", "md4", "md5", "sha1", "rmd160", - "sha256", "sha512", "whirlpool", "hmac(md5)", + "sha256", "sha512", "whirlpool", "hmac(sha256)", "des-cbc", "des-ede3", "rc4", "idea-cbc", "seed-cbc", "rc2-cbc", "rc5-cbc", "blowfish", "cast-cbc", "aes-128-cbc", "aes-192-cbc", "aes-256-cbc", @@ -570,7 +570,7 @@ static int run_benchmark(int async_jobs, int (*loop_function) (void *), static unsigned int testnum; -static char *evp_mac_mdname = "md5"; +static char *evp_mac_mdname = "sha256"; static char *evp_hmac_name = NULL; static const char *evp_md_name = NULL; static char *evp_mac_ciphername = "aes-128-cbc"; @@ -2544,7 +2544,7 @@ int speed_main(int argc, char **argv) goto end; if (!EVP_MAC_CTX_set_params(loopargs[i].mctx, params)) - goto skip_hmac; /* Digest not found */ + goto end; /* Digest not found */ } for (testnum = 0; testnum < size_num; testnum++) { print_message(names[D_HMAC], lengths[testnum], seconds.sym); @@ -2560,7 +2560,7 @@ int speed_main(int argc, char **argv) EVP_MAC_free(mac); mac = NULL; } -skip_hmac: + if (doit[D_CBC_DES]) { int st = 1; -- Gitee From c728019a983a525a271304907b2f87fd28f9e195 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Tue, 21 Nov 2023 14:36:37 +0100 Subject: [PATCH 27/31] Adapt C compiler detection for VSI C on x86_64 VSI C on OpenVMS for x86_64 has a bit more information than on other hardware. This is no doubt because it's based on LLVM which leaves an opening for cross compilation. VSI C on Itanium: $ CC/VERSION VSI C V7.4-001 on OpenVMS IA64 V8.4-2L3 VSI C on x86_64: $ CC/VERSION VSI C x86-64 X7.4-843 (GEM 50XB9) on OpenVMS x86_64 V9.2-1 Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22792) Signed-off-by: fly2x --- util/perl/OpenSSL/config.pm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util/perl/OpenSSL/config.pm b/util/perl/OpenSSL/config.pm index 791b19847a..8125f48b14 100755 --- a/util/perl/OpenSSL/config.pm +++ b/util/perl/OpenSSL/config.pm @@ -359,8 +359,15 @@ sub determine_compiler_settings { # However, other letters have been seen as well (for example X), # and it's documented that HP (now VSI) reserve the letter W, X, # Y and Z for their own uses. - my ($vendor, $version) = - ( $v =~ m/^([A-Z]+) C [VWXYZ]([0-9\.-]+)(:? +\(.*?\))? on / ); + my ($vendor, $arch, $version, $extra) = + ( $v =~ m/^ + ([A-Z]+) # Usually VSI + \s+ C + (?:\s+(.*?))? # Possible build arch + \s+ [VWXYZ]([0-9\.-]+) # Version + (?:\s+\((.*?)\))? # Possible extra data + \s+ on + /x ); my ($major, $minor, $patch) = ( $version =~ m/^([0-9]+)\.([0-9]+)-0*?(0|[1-9][0-9]*)$/ ); $CC = 'CC'; -- Gitee From ab01b0472552d612f43ac0eaf74a6c7b8a1691ff Mon Sep 17 00:00:00 2001 From: James Muir Date: Mon, 20 Nov 2023 13:14:12 -0500 Subject: [PATCH 28/31] contributing-doc: give example commit message with "CLA: trivial" The text "CLA: trivial" should go at the bottom of the commit message. Also, update the force-push command to include the repository and branch, which can avoid unexpected force-push results. Reviewed-by: Matthias St. Pierre Reviewed-by: Tomas Mraz Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22775) Signed-off-by: fly2x --- CONTRIBUTING.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 13a5d63695..0117fce16f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,9 +22,20 @@ guidelines: 1. Anything other than a trivial contribution requires a [Contributor License Agreement] (CLA), giving us permission to use your code. If your contribution is too small to require a CLA (e.g. fixing a spelling - mistake), place the text "`CLA: trivial`" on a line by itself separated by - an empty line from the rest of the commit message. It is not sufficient to - only place the text in the GitHub pull request description. + mistake), then place the text "`CLA: trivial`" on a line by itself below + the rest of your commit message separated by an empty line, like this: + + ``` + One-line summary of trivial change + + Optional main body of commit message. It might contain a sentence + or two explaining the trivial change. + + CLA: trivial + ``` + + It is not sufficient to only place the text "`CLA: trivial`" in the GitHub + pull request description. [Contributor License Agreement]: @@ -32,8 +43,8 @@ guidelines: ``` git commit --amend - [add the line, save and quit the editor] - git push -f + # add the line, save and quit the editor + git push -f [ []] ``` 2. All source files should start with the following text (with -- Gitee From 80cad8342e4906c64dc50f56c3b6c2fcfaccf662 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Thu, 16 Nov 2023 13:02:20 +0100 Subject: [PATCH 29/31] Cross Compiles CI: Run evp tests on pull requests Reviewed-by: Matt Caswell Reviewed-by: Shane Lontis Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22750) Signed-off-by: fly2x --- .github/workflows/cross-compiles.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cross-compiles.yml b/.github/workflows/cross-compiles.yml index 79af07c701..21683b731d 100644 --- a/.github/workflows/cross-compiles.yml +++ b/.github/workflows/cross-compiles.yml @@ -183,15 +183,15 @@ jobs: run: make -s -j4 - name: install qemu - if: github.event_name == 'push' && matrix.platform.tests != 'none' + if: matrix.platform.tests != 'none' run: sudo apt-get -yq --force-yes install qemu-user - name: Set QEMU environment - if: github.event_name == 'push' && matrix.platform.qemucpu != '' + if: matrix.platform.qemucpu != '' run: echo "QEMU_CPU=${{ matrix.platform.qemucpu }}" >> $GITHUB_ENV - name: Set OpenSSL caps environment - if: github.event_name == 'push' && matrix.platform.opensslcapsname != '' + if: matrix.platform.opensslcapsname != '' run: echo "OPENSSL_${{ matrix.platform.opensslcapsname }}=\ ${{ matrix.platform.opensslcaps }}" >> $GITHUB_ENV @@ -210,3 +210,9 @@ jobs: make test HARNESS_JOBS=${HARNESS_JOBS:-4} \ TESTS="${{ matrix.platform.tests }} -test_afalg" \ QEMU_LD_PREFIX=/usr/${{ matrix.platform.arch }} + - name: make evp tests + if: github.event_name == 'pull_request' && matrix.platform.tests != 'none' + run: | + make test HARNESS_JOBS=${HARNESS_JOBS:-4} \ + TESTS="test_evp*" \ + QEMU_LD_PREFIX=/usr/${{ matrix.platform.arch }} -- Gitee From 7b2234fc3522340564627e8abd452f2cb29a70f0 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Thu, 16 Nov 2023 17:52:39 +0100 Subject: [PATCH 30/31] keccak1600-armv4.pl: Further fix for the DigestSqueeze() support Reviewed-by: Matt Caswell Reviewed-by: Shane Lontis Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22750) Signed-off-by: fly2x --- crypto/sha/asm/keccak1600-armv4.pl | 10 +++++----- crypto/sha/build.info | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crypto/sha/asm/keccak1600-armv4.pl b/crypto/sha/asm/keccak1600-armv4.pl index f19076c248..f9a8cd1f00 100755 --- a/crypto/sha/asm/keccak1600-armv4.pl +++ b/crypto/sha/asm/keccak1600-armv4.pl @@ -933,7 +933,7 @@ SHA3_absorb: ___ } -{ my ($out,$len,$A_flat,$bsz,$next) = map("r$_", (4,5,10,12,11)); +{ my ($out,$len,$A_flat,$bsz,$next) = map("r$_", (4,5,10,12,0)); # void SHA3_squeeze(uint64_t A[5][5], @@ -947,13 +947,13 @@ $code.=<<___; .type SHA3_squeeze,%function .align 5 SHA3_squeeze: - stmdb sp!,{r0,r3-r11,lr} @ push 11 registers + stmdb sp!,{r0,r3-r10,lr} mov $A_flat,r0 mov $out,r1 mov $len,r2 mov $bsz,r3 - ldr $next, [sp, #48] @ next is after the 11 pushed registers (12*4) + ldr $next, [sp, #40] @ next is after the 10 pushed registers (10*4) #ifdef __thumb2__ mov r9,#0x00ff00ff @@ -1090,9 +1090,9 @@ SHA3_squeeze: .Lsqueeze_done: add sp,sp,#24 #if __ARM_ARCH__>=5 - ldmia sp!,{r4-r11,pc} + ldmia sp!,{r4-r10,pc} #else - ldmia sp!,{r4-r11,lr} + ldmia sp!,{r4-r10,lr} tst lr,#1 moveq pc,lr @ be binary compatible with V4, yet bx lr @ interoperable with Thumb ISA:-) diff --git a/crypto/sha/build.info b/crypto/sha/build.info index 92f48531ce..95767e9589 100644 --- a/crypto/sha/build.info +++ b/crypto/sha/build.info @@ -169,15 +169,16 @@ GENERATE[keccak1600-s390x.S]=asm/keccak1600-s390x.pl GENERATE[sha1-c64xplus.S]=asm/sha1-c64xplus.pl GENERATE[sha256-c64xplus.S]=asm/sha256-c64xplus.pl GENERATE[sha512-c64xplus.S]=asm/sha512-c64xplus.pl -GENERATE[keccak1600-c64x.S]=asm/keccak1600-c64x.pl GENERATE[sha256-riscv64-zvkb-zvknha_or_zvknhb.S]=asm/sha256-riscv64-zvkb-zvknha_or_zvknhb.pl GENERATE[sha512-riscv64-zvkb-zvknhb.S]=asm/sha512-riscv64-zvkb-zvknhb.pl -# These are not yet used +# These are not yet used and do not support multi-squeeze +GENERATE[keccak1600-c64x.S]=asm/keccak1600-c64x.pl GENERATE[keccak1600-avx2.S]=asm/keccak1600-avx2.pl GENERATE[keccak1600-avx512.S]=asm/keccak1600-avx512.pl GENERATE[keccak1600-avx512vl.S]=asm/keccak1600-avx512vl.pl GENERATE[keccak1600-mmx.S]=asm/keccak1600-mmx.pl GENERATE[keccak1600p8-ppc.S]=asm/keccak1600p8-ppc.pl + GENERATE[sha1-thumb.S]=asm/sha1-thumb.pl -- Gitee From a3c6ea71a473dfc284869d0dcf138286a7a4b645 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Mon, 20 Nov 2023 17:46:26 +0100 Subject: [PATCH 31/31] SHA3_squeeze(): The next argument is int Amend the assembler so it uses only 32bit value. Reviewed-by: Matt Caswell Reviewed-by: Shane Lontis Reviewed-by: Hugo Landau (Merged from https://github.com/openssl/openssl/pull/22750) Signed-off-by: fly2x --- crypto/sha/asm/keccak1600-armv8.pl | 2 +- crypto/sha/asm/keccak1600-ppc64.pl | 2 +- crypto/sha/asm/keccak1600-x86_64.pl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/sha/asm/keccak1600-armv8.pl b/crypto/sha/asm/keccak1600-armv8.pl index 72f8c3adb5..7566a7e3ec 100755 --- a/crypto/sha/asm/keccak1600-armv8.pl +++ b/crypto/sha/asm/keccak1600-armv8.pl @@ -483,7 +483,7 @@ SHA3_squeeze: mov $out,x1 mov $len,x2 mov $bsz,x3 - cmp x4, #0 // x4 = 'next' argument + cmp w4, #0 // w4 = 'next' argument bne .Lnext_block .Loop_squeeze: diff --git a/crypto/sha/asm/keccak1600-ppc64.pl b/crypto/sha/asm/keccak1600-ppc64.pl index fe7d6db20e..54f32e8c92 100755 --- a/crypto/sha/asm/keccak1600-ppc64.pl +++ b/crypto/sha/asm/keccak1600-ppc64.pl @@ -668,7 +668,7 @@ SHA3_squeeze: subi $out,r4,1 ; prepare for stbu mr $len,r5 mr $bsz,r6 - ${UCMP}i r7,0 ; r7 = 'next' argument + cmplwi r7,0 ; r7 = 'next' argument bne .Lnext_block b .Loop_squeeze diff --git a/crypto/sha/asm/keccak1600-x86_64.pl b/crypto/sha/asm/keccak1600-x86_64.pl index bddcaf8294..78aa5c64af 100755 --- a/crypto/sha/asm/keccak1600-x86_64.pl +++ b/crypto/sha/asm/keccak1600-x86_64.pl @@ -524,7 +524,7 @@ SHA3_squeeze: mov %rsi,$out mov %rdx,$len mov %rcx,$bsz - bt \$0,$next + bt \$0,${next}d jc .Lnext_block jmp .Loop_squeeze -- Gitee