diff --git a/AUTHORS.md b/AUTHORS.md index dc6b534b82..f9a2903050 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,6 +12,7 @@ Groups * OpenSSL Software Services, Inc. * OpenSSL Software Foundation, Inc. + * Google LLC Individuals ----------- diff --git a/apps/list.c b/apps/list.c index 944722e78f..36c8f24d34 100644 --- a/apps/list.c +++ b/apps/list.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include "apps.h" #include "app_params.h" @@ -776,6 +778,42 @@ static int list_tls_sigalg_caps(OSSL_PROVIDER *provider, void *cbdata) return 1; } +#if !defined(OPENSSL_NO_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) +static void list_tls_groups(int version, int all) +{ + SSL_CTX *ctx = NULL; + STACK_OF(OPENSSL_CSTRING) *groups; + size_t i, num; + + if ((groups = sk_OPENSSL_CSTRING_new_null()) == NULL) { + BIO_printf(bio_err, "ERROR: Memory allocation\n"); + return; + } + if ((ctx = SSL_CTX_new(TLS_method())) == NULL) { + BIO_printf(bio_err, "ERROR: Memory allocation\n"); + goto err; + } + if (!SSL_CTX_set_min_proto_version(ctx, version) + || !SSL_CTX_set_max_proto_version(ctx, version)) { + BIO_printf(bio_err, "ERROR: setting TLS protocol version\n"); + goto err; + } + if (!SSL_CTX_get0_implemented_groups(ctx, all, groups)) { + BIO_printf(bio_err, "ERROR: getting implemented TLS group list\n"); + goto err; + } + num = sk_OPENSSL_CSTRING_num(groups); + for (i = 0; i < num; ++i) { + BIO_printf(bio_out, "%s%c", sk_OPENSSL_CSTRING_value(groups, i), + (i < num - 1) ? ':' : '\n'); + } + err: + SSL_CTX_free(ctx); + sk_OPENSSL_CSTRING_free(groups); + return; +} +#endif + static void list_tls_signatures(void) { int tls_sigalg_listed = 0; @@ -1515,6 +1553,15 @@ typedef enum HELPLIST_CHOICE { OPT_TLS_SIGNATURE_ALGORITHMS, OPT_ASYM_CIPHER_ALGORITHMS, OPT_STORE_LOADERS, OPT_PROVIDER_INFO, OPT_OBJECTS, OPT_SELECT_NAME, +#if !defined(OPENSSL_NO_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) + OPT_ALL_TLS_GROUPS, OPT_TLS_GROUPS, +# if !defined(OPENSSL_NO_TLS1_2) + OPT_TLS1_2, +# endif +# if !defined(OPENSSL_NO_TLS1_3) + OPT_TLS1_3, +# endif +#endif #ifndef OPENSSL_NO_DEPRECATED_3_0 OPT_ENGINES, #endif @@ -1572,6 +1619,20 @@ const OPTIONS list_options[] = { "List of public key methods"}, {"store-loaders", OPT_STORE_LOADERS, '-', "List of store loaders"}, +#if !defined(OPENSSL_NO_TLS1_2) || !defined(OPENSSL_NO_TLS1_3) + {"tls-groups", OPT_TLS_GROUPS, '-', + "List implemented TLS key exchange 'groups'" }, + {"all-tls-groups", OPT_ALL_TLS_GROUPS, '-', + "List implemented TLS key exchange 'groups' and all aliases" }, +# ifndef OPENSSL_NO_TLS1_2 + {"tls1_2", OPT_TLS1_2, '-', + "When listing 'groups', list those compatible with TLS1.2"}, +# endif +# ifndef OPENSSL_NO_TLS1_3 + {"tls1_3", OPT_TLS1_3, '-', + "When listing 'groups', list those compatible with TLS1.3"}, +# endif +#endif {"providers", OPT_PROVIDER_INFO, '-', "List of provider information"}, #ifndef OPENSSL_NO_DEPRECATED_3_0 @@ -1594,6 +1655,14 @@ int list_main(int argc, char **argv) HELPLIST_CHOICE o; int one = 0, done = 0; int print_newline = 0; +#if !defined(OPENSSL_NO_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) + int all_tls_groups = 0; +# if !defined(OPENSSL_NO_TLS1_3) + unsigned int tls_version = TLS1_3_VERSION; +# else + unsigned int tls_version = TLS1_2_VERSION; +# endif +#endif struct { unsigned int commands:1; unsigned int all_algorithms:1; @@ -1612,6 +1681,7 @@ int list_main(int argc, char **argv) unsigned int tls_signature_algorithms:1; unsigned int keyexchange_algorithms:1; unsigned int kem_algorithms:1; + unsigned int tls_groups:1; unsigned int asym_cipher_algorithms:1; unsigned int pk_algorithms:1; unsigned int pk_method:1; @@ -1692,6 +1762,25 @@ opthelp: case OPT_KEM_ALGORITHMS: todo.kem_algorithms = 1; break; +#if !defined(OPENSSL_NO_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) + case OPT_TLS_GROUPS: + todo.tls_groups = 1; + break; + case OPT_ALL_TLS_GROUPS: + all_tls_groups = 1; + todo.tls_groups = 1; + break; +# if !defined(OPENSSL_NO_TLS1_2) + case OPT_TLS1_2: + tls_version = TLS1_2_VERSION; + break; +# endif +# if !defined(OPENSSL_NO_TLS1_3) + case OPT_TLS1_3: + tls_version = TLS1_3_VERSION; + break; +# endif +#endif case OPT_ASYM_CIPHER_ALGORITHMS: todo.asym_cipher_algorithms = 1; break; @@ -1811,6 +1900,10 @@ opthelp: MAYBE_ADD_NL(list_keyexchanges()); if (todo.kem_algorithms) MAYBE_ADD_NL(list_kems()); +#if !defined(OPENSSL_NO_TLS1_3) || !defined(OPENSSL_NO_TLS1_2) + if (todo.tls_groups) + MAYBE_ADD_NL(list_tls_groups(tls_version, all_tls_groups)); +#endif if (todo.pk_algorithms) MAYBE_ADD_NL(list_pkey()); if (todo.pk_method) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index decf2d6f2e..35f7480cec 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1141,6 +1141,8 @@ PROV_R_NOT_XOF_OR_INVALID_LENGTH:113:not xof or invalid length PROV_R_NO_INSTANCE_ALLOWED:242:no instance allowed PROV_R_NO_KEY_SET:114:no key set PROV_R_NO_PARAMETERS_SET:177:no parameters set +PROV_R_NULL_LENGTH_POINTER:247:null length pointer +PROV_R_NULL_OUTPUT_BUFFER:245:null output buffer PROV_R_ONESHOT_CALL_OUT_OF_ORDER:239:oneshot call out of order PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE:178:\ operation not supported for this keytype @@ -1176,9 +1178,11 @@ PROV_R_UNSUPPORTED_CEK_ALG:145:unsupported cek alg PROV_R_UNSUPPORTED_KEY_SIZE:153:unsupported key size PROV_R_UNSUPPORTED_MAC_TYPE:137:unsupported mac type PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS:152:unsupported number of rounds +PROV_R_UNSUPPORTED_SELECTION:248:unsupported selection PROV_R_UPDATE_CALL_OUT_OF_ORDER:240:update call out of order PROV_R_URI_AUTHORITY_UNSUPPORTED:223:uri authority unsupported PROV_R_VALUE_ERROR:138:value error +PROV_R_WRONG_CIPHERTEXT_SIZE:246:wrong ciphertext size PROV_R_WRONG_FINAL_BLOCK_LENGTH:107:wrong final block length PROV_R_WRONG_OUTPUT_BUFFER_SIZE:139:wrong output buffer size PROV_R_XOF_DIGESTS_NOT_ALLOWED:183:xof digests not allowed diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c index 1e15fb8d61..8960bf2f47 100644 --- a/crypto/ml_kem/ml_kem.c +++ b/crypto/ml_kem/ml_kem.c @@ -7,8 +7,6 @@ * https://www.openssl.org/source/license.html */ -/* Copyright (c) 2024, Google Inc. */ - #include #include #include diff --git a/doc/man1/openssl-list.pod.in b/doc/man1/openssl-list.pod.in index a1d74f8e51..602f663676 100644 --- a/doc/man1/openssl-list.pod.in +++ b/doc/man1/openssl-list.pod.in @@ -32,6 +32,10 @@ B [B<-key-managers>] [B<-key-exchange-algorithms>] [B<-kem-algorithms>] +[B<-tls-groups>] +[B<-all-tls-groups>] +[B<-tls1_2>] +[B<-tls1_3>] [B<-signature-algorithms>] [B<-tls-signature-algorithms>] [B<-asymcipher-algorithms>] @@ -191,6 +195,29 @@ Display a list of key exchange algorithms. Display a list of key encapsulation algorithms. +=item B<-tls-groups> + +Display a list of the IANA names of all available (implemented) TLS groups. +By default the listed groups are those compatible with TLS 1.3. + +=item B<-all-tls-groups> + +Display a list of the names of all available (implemented) TLS groups, +including any aliases. +Some groups are known under multiple names, for example, B is also +known as B. +By default the listed groups are those compatible with TLS 1.3. + +=item B<-tls1_2> + +When listing TLS groups, list those compatible with TLS 1.2 + +=item B<-tls1_3> + +When listing TLS groups, output those compatible with TLS 1.3. +TLS 1.3 is the current default protocol version, but the default version is +subject to change, so best to specify the version explicitly. + =item B<-signature-algorithms> Display a list of signature algorithms. diff --git a/doc/man1/openssl-s_client.pod.in b/doc/man1/openssl-s_client.pod.in index 8e685d3551..76b96d4dda 100644 --- a/doc/man1/openssl-s_client.pod.in +++ b/doc/man1/openssl-s_client.pod.in @@ -669,11 +669,14 @@ For example strings, see L Specifies the list of supported curves to be sent by the client. The curve is ultimately selected by the server. -The list of all supported groups includes named EC parameters as well as X25519 -and X448 or FFDHE groups, and may also include groups implemented in 3rd-party -providers. For a list of named EC parameters, use: +The list of available groups includes various built-in named EC curves, as well +as X25519 and X448, FFDHE groups, and any additional groups implemented in the +default or 3rd-party providers. +The commands below list the available groups for TLS 1.2 and TLS 1.3, +respectively: - $ openssl ecparam -list_curves + $ openssl list -tls1_2 -tls-groups + $ openssl list -tls1_3 -tls-groups =item B<-cipher> I diff --git a/doc/man1/openssl-s_server.pod.in b/doc/man1/openssl-s_server.pod.in index 0d8dd9bd0a..c4b95a3c49 100644 --- a/doc/man1/openssl-s_server.pod.in +++ b/doc/man1/openssl-s_server.pod.in @@ -675,11 +675,14 @@ Signature algorithms to support for client certificate authentication Specifies the elliptic curve to use. NOTE: this is single curve, not a list. -The list of all supported groups includes named EC parameters as well as X25519 -and X448 or FFDHE groups, and may also include groups implemented in 3rd-party -providers. For a list of named EC parameters, use: +The list of available groups includes various built-in named EC curves, as well +as X25519 and X448, FFDHE groups, and any additional groups implemented in the +default or 3rd-party providers. +The commands below list the available groups for TLS 1.2 and TLS 1.3, +respectively. - $ openssl ecparam -list_curves + $ openssl list -tls1_2 -tls-groups + $ openssl list -tls1_3 -tls-groups =item B<-cipher> I diff --git a/doc/man3/SSL_CONF_cmd.pod b/doc/man3/SSL_CONF_cmd.pod index d232c37a99..d6592b33a5 100644 --- a/doc/man3/SSL_CONF_cmd.pod +++ b/doc/man3/SSL_CONF_cmd.pod @@ -155,13 +155,36 @@ group to use. This setting affects groups used for signatures (in TLSv1.2 and earlier) and key exchange. In its simplest form the I argument is a colon separated list of -groups. Each group can be either the B name (e.g. B), some other -commonly used name where applicable (e.g. B, B) or an -OpenSSL OID name (e.g. B). Group names are case sensitive. The list -should be in order of preference with the most preferred group first. +groups. The preferred names are those listed in the IANA +L +registry. + +For some groups, OpenSSL supports additional aliases. +Such an alias could be a B name (e.g. B), an OpenSSL OID name +(e.g. B), or some other commonly used name. +Group names are case sensitive. +The list should be in order of preference with the most preferred group first. + The first group listed will also be used for the B sent by a client in a TLSv1.3 B. +The commands below list the IANA names for TLS 1.2 and TLS 1.3, +respectively: + + $ openssl list -tls1_2 -tls-groups + $ openssl list -tls1_3 -tls-groups + +The recommended groups (in order of decreasing performance) for TLS 1.3 are presently: + +B, +B, +B, +and +B. + +The stronger security margins of the last two, come at a significant +performance penalty. + An enriched alternative syntax, that enables clients to send multiple keyshares and allows servers to prioritise some groups over others, is described in L. @@ -169,28 +192,6 @@ Since TLS 1.2 has neither keyshares nor a hello retry mechanism, with TLS 1.2 the enriched syntax is ultimately equivalent to just a simple ordered list of groups, as with the simple form above. -The supported groups for B include: -B, -B, -B, -B, -B, -B, -B, -B, -B, -B, -B, -B, -B -B, -B, -and -B. - -Additional providers may make available further algorithms via the -TLS-GROUP capability. See L. - =item B<-curves> I This is a synonym for the B<-groups> command. @@ -450,30 +451,28 @@ signatures (in TLSv1.2 and earlier) and key exchange. The first group listed will also be used for the B sent by a client in a TLSv1.3 B. -The B argument is a colon separated list of groups. The group can be -either the B name (e.g. B), some other commonly used name where -applicable (e.g. B, B) or an OpenSSL OID name -(e.g. B). Group names are case sensitive. The list should be in -order of preference with the most preferred group first. +The B argument is a colon separated list of groups. The preferred +names are those listed in the IANA +L +registry. +For some groups, OpenSSL supports additional aliases. +Such an alias could be a B name (e.g. B), an OpenSSL OID name +(e.g. B), or some other commonly used name. +Group names are case sensitive. +The list should be in order of preference with the most preferred group first. -The supported groups for B include: -B, -B, -B, -B, -B, -B, -B, -B, -B, -B, -B, -B, -B -B, -B, -and -B. +The commands below list the available groups for TLS 1.2 and TLS 1.3, +respectively: + + $ openssl list -tls1_2 -tls-groups + $ openssl list -tls1_3 -tls-groups + +An enriched alternative syntax, that enables clients to send multiple keyshares +and allows servers to prioritise some groups over others, is described in +L. +Since TLS 1.2 has neither keyshares nor a hello retry mechanism, with TLS 1.2 +the enriched syntax is ultimately equivalent to just a simple ordered list of +groups, as with the simple form above. =item B @@ -853,7 +852,21 @@ added in OpenSSL 3.2. B was added in OpenSSL 3.3. -Support for B was added in OpenSSL 3.5. +OpenSSL 3.5 introduces support for post-quantum (PQ) TLS key exchange via the +B, B and B TLS groups. +These are based on the underlying B, B and +B algorithms from FIPS 203. + +OpenSSL 3.5 also introduces support for three I ECDH PQ key exchange +TLS I: B, B and +B. +They offer CPU performance comparable to the associated ECDH group, though at +the cost of significantly larger key exchange messages. +The third group, B is substantially more CPU-intensive, +largely as a result of the high CPU cost of ECDH for the underlying B +group. +Also its key exchange messages at close to 1700 bytes are larger than the +roughly 1200 bytes for the first two groups. =head1 COPYRIGHT diff --git a/doc/man3/SSL_CTX_set1_curves.pod b/doc/man3/SSL_CTX_set1_curves.pod index dd0930e8d4..b36466b386 100755 --- a/doc/man3/SSL_CTX_set1_curves.pod +++ b/doc/man3/SSL_CTX_set1_curves.pod @@ -6,7 +6,7 @@ SSL_CTX_set1_groups, SSL_CTX_set1_groups_list, SSL_set1_groups, SSL_set1_groups_list, SSL_get1_groups, SSL_get0_iana_groups, SSL_get_shared_group, SSL_get_negotiated_group, SSL_CTX_set1_curves, SSL_CTX_set1_curves_list, SSL_set1_curves, SSL_set1_curves_list, -SSL_get1_curves, SSL_get_shared_curve +SSL_get1_curves, SSL_get_shared_curve, SSL_CTX_get0_implemented_groups - EC supported curve functions =head1 SYNOPSIS @@ -33,6 +33,9 @@ SSL_get1_curves, SSL_get_shared_curve int SSL_get1_curves(SSL *ssl, int *curves); int SSL_get_shared_curve(SSL *s, int n); + int SSL_CTX_get0_implemented_groups(SSL_CTX *ctx, int all, + STACK_OF(OPENSSL_CSTRING) *names); + =head1 DESCRIPTION For all of the functions below that set the supported groups there must be at @@ -85,24 +88,11 @@ SSL_CTX_set1_groups_list() sets the supported groups for B to string I. In contrast to SSL_CTX_set1_groups(), the names of the groups, rather than their NIDs, are used. -The supported groups for B include: -B, -B, -B, -B, -B, -B, -B, -B, -B -B, -B, -B, -B, -B, -B, and -B. -Support for other groups may be added by external providers. +The commands below list the available groups for TLS 1.2 and TLS 1.3, +respectively: + + $ openssl list -tls1_2 -tls-groups + $ openssl list -tls1_3 -tls-groups Each group can be either the B name (e.g. B), some other commonly used name where applicable (e.g. B, B) or an OpenSSL OID name @@ -226,6 +216,19 @@ current, non-resumption, connection). This can be called by either client or server. If the NID for the shared group is unknown then the value is set to the bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the group. +SSL_CTX_get0_implemented_groups() populates a stack with the names of TLS +groups that are compatible with the TLS version of the B argument. +The returned names are references to internal constants and must not be +modified or freed. When B is nonzero, the returned list includes not +only the preferred IANA names of the groups, but also any associated aliases. +If the SSL_CTX is version-flexible, the groups will be those compatible +with any configured minimum and maximum protocol versions. +The B stack should be allocated by the caller and be empty, the +matching group names are appended to the provided stack. +The B<-tls-groups> and B<-all-tls-groups> options of the +L command output these lists for either +TLS 1.2 or TLS 1.3 (by default). + All these functions are implemented as macros. The curve functions are synonyms for the equivalently named group functions and @@ -242,8 +245,9 @@ consider using the SSL_CONF interface instead of manually parsing options. =head1 RETURN VALUES -SSL_CTX_set1_groups(), SSL_CTX_set1_groups_list(), SSL_set1_groups() and -SSL_set1_groups_list(), return 1 for success and 0 for failure. +SSL_CTX_set1_groups(), SSL_CTX_set1_groups_list(), SSL_set1_groups(), +SSL_set1_groups_list(), and SSL_CTX_get0_implemented_groups() return 1 for +success and 0 for failure. SSL_get1_groups() returns the number of groups, which may be zero. @@ -275,6 +279,8 @@ SSL_set1_groups_list() was added in OpenSSL 3.3. Support for B was added in OpenSSL 3.5. +B was first implemented in OpenSSL 3.5. + Earlier versions of this document described the list as a preference order. However, OpenSSL's behavior as a TLS 1.3 server is to consider I supported groups as comparable in security. diff --git a/doc/man7/ossl-guide-migration.pod b/doc/man7/ossl-guide-migration.pod index 7e365ed18f..15467c75e0 100644 --- a/doc/man7/ossl-guide-migration.pod +++ b/doc/man7/ossl-guide-migration.pod @@ -365,7 +365,7 @@ categories. See L. L and L now work for more key types. This includes RSA, DSA, ED25519, X25519, ED448 and X448. Previously (in 1.1.1) they would return -2. For key types that do not have -parameters then L will always return 1. +parameters L will always return 1. =head3 Other notable deprecations and changes diff --git a/include/crypto/ml_kem.h b/include/crypto/ml_kem.h index 0604b96763..fafaa98b72 100644 --- a/include/crypto/ml_kem.h +++ b/include/crypto/ml_kem.h @@ -7,8 +7,6 @@ * https://www.openssl.org/source/license.html */ -/* Copyright (c) 2024, Google Inc. */ - #ifndef OPENSSL_HEADER_ML_KEM_H # define OPENSSL_HEADER_ML_KEM_H # pragma once diff --git a/include/internal/tlsgroups.h b/include/internal/tlsgroups.h index 4662032bca..e307152c96 100644 --- a/include/internal/tlsgroups.h +++ b/include/internal/tlsgroups.h @@ -59,5 +59,8 @@ # define OSSL_TLS_GROUP_ID_mlkem512 0x0200 # define OSSL_TLS_GROUP_ID_mlkem768 0x0201 # define OSSL_TLS_GROUP_ID_mlkem1024 0x0202 +# define OSSL_TLS_GROUP_ID_SecP256r1MLKEM768 0x11EB +# define OSSL_TLS_GROUP_ID_X25519MLKEM768 0x11EC +# define OSSL_TLS_GROUP_ID_SecP384r1MLKEM1024 0x11ED #endif diff --git a/include/openssl/proverr.h b/include/openssl/proverr.h index d10b653152..1ac4f59f0d 100644 --- a/include/openssl/proverr.h +++ b/include/openssl/proverr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2025 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 @@ -119,6 +119,8 @@ # define PROV_R_NO_INSTANCE_ALLOWED 242 # define PROV_R_NO_KEY_SET 114 # define PROV_R_NO_PARAMETERS_SET 177 +# define PROV_R_NULL_LENGTH_POINTER 247 +# define PROV_R_NULL_OUTPUT_BUFFER 245 # define PROV_R_ONESHOT_CALL_OUT_OF_ORDER 239 # define PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE 178 # define PROV_R_OUTPUT_BUFFER_TOO_SMALL 106 @@ -150,9 +152,11 @@ # define PROV_R_UNSUPPORTED_KEY_SIZE 153 # define PROV_R_UNSUPPORTED_MAC_TYPE 137 # define PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS 152 +# define PROV_R_UNSUPPORTED_SELECTION 248 # define PROV_R_UPDATE_CALL_OUT_OF_ORDER 240 # define PROV_R_URI_AUTHORITY_UNSUPPORTED 223 # define PROV_R_VALUE_ERROR 138 +# define PROV_R_WRONG_CIPHERTEXT_SIZE 246 # define PROV_R_WRONG_FINAL_BLOCK_LENGTH 107 # define PROV_R_WRONG_OUTPUT_BUFFER_SIZE 139 # define PROV_R_XOF_DIGESTS_NOT_ALLOWED 183 diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index 7659917b5f..9036e23f7d 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -1336,6 +1336,7 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) # define SSL_CTRL_SET_RETRY_VERIFY 136 # define SSL_CTRL_GET_VERIFY_CERT_STORE 137 # define SSL_CTRL_GET_CHAIN_CERT_STORE 138 +# define SSL_CTRL_GET0_IMPLEMENTED_GROUPS 139 # define SSL_CERT_SET_FIRST 1 # define SSL_CERT_SET_NEXT 2 # define SSL_CERT_SET_SERVER 3 @@ -1444,6 +1445,9 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) SSL_CTX_ctrl(ctx,SSL_CTRL_SET_GROUPS,glistlen,(int *)(glist)) # define SSL_CTX_set1_groups_list(ctx, s) \ SSL_CTX_ctrl(ctx,SSL_CTRL_SET_GROUPS_LIST,0,(char *)(s)) +# define SSL_CTX_get0_implemented_groups(ctx, all, out) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET0_IMPLEMENTED_GROUPS, all, \ + (STACK_OF(OPENSSL_CSTRING) *)(out)) # define SSL_set1_groups(s, glist, glistlen) \ SSL_ctrl(s,SSL_CTRL_SET_GROUPS,glistlen,(char *)(glist)) # define SSL_set1_groups_list(s, str) \ diff --git a/providers/common/capabilities.c b/providers/common/capabilities.c index 3c2882601d..82ea4736f7 100644 --- a/providers/common/capabilities.c +++ b/providers/common/capabilities.c @@ -36,76 +36,62 @@ typedef struct tls_group_constants_st { int is_kem; /* Indicates utility as KEM */ } TLS_GROUP_CONSTANTS; +/* + * The indices of entries in this table must be independent of which TLS groups + * we do or not support. It just lists basic facts about the groups, and is + * used by (numeric slot number) reference in the "param_group_list" below. + * Therefore, there must be no #ifdefs in this table, the index of each entry + * must be independent of compile-time options. + * + * For the FFDHE groups, the security bit values are as given by + * BN_security_bits(). For the ML-KEM hybrids these are the ML-KEM security + * bits. + */ static const TLS_GROUP_CONSTANTS group_list[] = { - { OSSL_TLS_GROUP_ID_sect163k1, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect163r1, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect163r2, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect193r1, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect193r2, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect233k1, 112, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect233r1, 112, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect239k1, 112, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect283k1, 128, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect283r1, 128, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect409k1, 192, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect409r1, 192, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect571k1, 256, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_sect571r1, 256, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp160k1, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp160r1, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp160r2, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp192k1, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp192r1, 80, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp224k1, 112, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp224r1, 112, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp256k1, 128, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_secp256r1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, - { OSSL_TLS_GROUP_ID_secp384r1, 192, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, - { OSSL_TLS_GROUP_ID_secp521r1, 256, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, - { OSSL_TLS_GROUP_ID_brainpoolP256r1, 128, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_brainpoolP384r1, 192, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_brainpoolP512r1, 256, TLS1_VERSION, TLS1_2_VERSION, - DTLS1_VERSION, DTLS1_2_VERSION, 0 }, - { OSSL_TLS_GROUP_ID_x25519, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, - { OSSL_TLS_GROUP_ID_x448, 224, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, - { OSSL_TLS_GROUP_ID_brainpoolP256r1_tls13, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, - { OSSL_TLS_GROUP_ID_brainpoolP384r1_tls13, 192, TLS1_3_VERSION, 0, -1, -1, 0 }, - { OSSL_TLS_GROUP_ID_brainpoolP512r1_tls13, 256, TLS1_3_VERSION, 0, -1, -1, 0 }, - /* Security bit values as given by BN_security_bits() */ - { OSSL_TLS_GROUP_ID_ffdhe2048, 112, TLS1_3_VERSION, 0, -1, -1, 0 }, - { OSSL_TLS_GROUP_ID_ffdhe3072, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, - { OSSL_TLS_GROUP_ID_ffdhe4096, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, - { OSSL_TLS_GROUP_ID_ffdhe6144, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, - { OSSL_TLS_GROUP_ID_ffdhe8192, 192, TLS1_3_VERSION, 0, -1, -1, 0 }, -#ifndef OPENSSL_NO_ML_KEM - { OSSL_TLS_GROUP_ID_mlkem512, ML_KEM_512_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, - { OSSL_TLS_GROUP_ID_mlkem768, ML_KEM_768_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, - { OSSL_TLS_GROUP_ID_mlkem1024, ML_KEM_1024_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, -#endif + /* 0 */ { OSSL_TLS_GROUP_ID_sect163k1, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 1 */ { OSSL_TLS_GROUP_ID_sect163r1, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 2 */ { OSSL_TLS_GROUP_ID_sect163r2, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 3 */ { OSSL_TLS_GROUP_ID_sect193r1, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 4 */ { OSSL_TLS_GROUP_ID_sect193r2, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 5 */ { OSSL_TLS_GROUP_ID_sect233k1, 112, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 6 */ { OSSL_TLS_GROUP_ID_sect233r1, 112, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 7 */ { OSSL_TLS_GROUP_ID_sect239k1, 112, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 8 */ { OSSL_TLS_GROUP_ID_sect283k1, 128, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 9 */ { OSSL_TLS_GROUP_ID_sect283r1, 128, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 10 */ { OSSL_TLS_GROUP_ID_sect409k1, 192, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 11 */ { OSSL_TLS_GROUP_ID_sect409r1, 192, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 12 */ { OSSL_TLS_GROUP_ID_sect571k1, 256, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 13 */ { OSSL_TLS_GROUP_ID_sect571r1, 256, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 14 */ { OSSL_TLS_GROUP_ID_secp160k1, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 15 */ { OSSL_TLS_GROUP_ID_secp160r1, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 16 */ { OSSL_TLS_GROUP_ID_secp160r2, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 17 */ { OSSL_TLS_GROUP_ID_secp192k1, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 18 */ { OSSL_TLS_GROUP_ID_secp192r1, 80, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 19 */ { OSSL_TLS_GROUP_ID_secp224k1, 112, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 20 */ { OSSL_TLS_GROUP_ID_secp224r1, 112, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 21 */ { OSSL_TLS_GROUP_ID_secp256k1, 128, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 22 */ { OSSL_TLS_GROUP_ID_secp256r1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, + /* 23 */ { OSSL_TLS_GROUP_ID_secp384r1, 192, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, + /* 24 */ { OSSL_TLS_GROUP_ID_secp521r1, 256, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, + /* 25 */ { OSSL_TLS_GROUP_ID_brainpoolP256r1, 128, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 26 */ { OSSL_TLS_GROUP_ID_brainpoolP384r1, 192, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 27 */ { OSSL_TLS_GROUP_ID_brainpoolP512r1, 256, TLS1_VERSION, TLS1_2_VERSION, DTLS1_VERSION, DTLS1_2_VERSION, 0 }, + /* 28 */ { OSSL_TLS_GROUP_ID_x25519, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, + /* 29 */ { OSSL_TLS_GROUP_ID_x448, 224, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, + /* 30 */ { OSSL_TLS_GROUP_ID_brainpoolP256r1_tls13, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 31 */ { OSSL_TLS_GROUP_ID_brainpoolP384r1_tls13, 192, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 32 */ { OSSL_TLS_GROUP_ID_brainpoolP512r1_tls13, 256, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 33 */ { OSSL_TLS_GROUP_ID_ffdhe2048, 112, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 34 */ { OSSL_TLS_GROUP_ID_ffdhe3072, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 35 */ { OSSL_TLS_GROUP_ID_ffdhe4096, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 36 */ { OSSL_TLS_GROUP_ID_ffdhe6144, 128, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 37 */ { OSSL_TLS_GROUP_ID_ffdhe8192, 192, TLS1_3_VERSION, 0, -1, -1, 0 }, + /* 38 */ { OSSL_TLS_GROUP_ID_mlkem512, ML_KEM_512_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, + /* 39 */ { OSSL_TLS_GROUP_ID_mlkem768, ML_KEM_768_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, + /* 40 */ { OSSL_TLS_GROUP_ID_mlkem1024, ML_KEM_1024_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, + /* 41 */ { OSSL_TLS_GROUP_ID_X25519MLKEM768, ML_KEM_768_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, + /* 42 */ { OSSL_TLS_GROUP_ID_SecP256r1MLKEM768, ML_KEM_768_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, + /* 43 */ { OSSL_TLS_GROUP_ID_SecP384r1MLKEM1024, ML_KEM_1024_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 }, }; #define TLS_GROUP_ENTRY(tlsname, realname, algorithm, idx) \ @@ -136,6 +122,18 @@ static const TLS_GROUP_CONSTANTS group_list[] = { OSSL_PARAM_END \ } +/*- + * - The 4th field of each entry is an index into "group_list" above. + * + * - The 3rd field is the key management algorithm name. + + * - The 2nd field is the GROUP_NAME used with the provider, needed for + * providers that implement a family of related algorithms, but required + * non-null even when the provider implements just one. + * + * - The 1st field is the TLS group name used in SSL_CTX_set_group_list(), + * aliases repeat everything but the first field. + */ static const OSSL_PARAM param_group_list[][11] = { # ifndef OPENSSL_NO_EC # ifndef OPENSSL_NO_EC2M @@ -219,9 +217,16 @@ static const OSSL_PARAM param_group_list[][11] = { # endif # if !defined(OPENSSL_NO_ML_KEM) && !defined(FIPS_MODULE) /* https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 */ - TLS_GROUP_ENTRY("MLKEM512", "MLKEM512", "ML-KEM-512", 38), - TLS_GROUP_ENTRY("MLKEM768", "MLKEM768", "ML-KEM-768", 39), - TLS_GROUP_ENTRY("MLKEM1024", "MLKEM1024", "ML-KEM-1024", 40), + TLS_GROUP_ENTRY("MLKEM512", "", "ML-KEM-512", 38), + TLS_GROUP_ENTRY("MLKEM768", "", "ML-KEM-768", 39), + TLS_GROUP_ENTRY("MLKEM1024", "", "ML-KEM-1024", 40), +# endif +# if !defined(OPENSSL_NO_ML_KEM) && !defined(OPENSSL_NO_EC) +# if !defined(OPENSSL_NO_ECX) + TLS_GROUP_ENTRY("X25519MLKEM768", "", "X25519MLKEM768", 41), +# endif + TLS_GROUP_ENTRY("SecP256r1MLKEM768", "", "SecP256r1MLKEM768", 42), + TLS_GROUP_ENTRY("SecP384r1MLKEM1024", "", "SecP384r1MLKEM1024", 43), # endif }; #endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_ML_KEM) */ diff --git a/providers/common/include/prov/proverr.h b/providers/common/include/prov/proverr.h index 34247ed2f7..87a84e9c21 100644 --- a/providers/common/include/prov/proverr.h +++ b/providers/common/include/prov/proverr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c index df4bab0966..301c570789 100644 --- a/providers/common/provider_err.c +++ b/providers/common/provider_err.c @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2025 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 @@ -166,6 +166,10 @@ static const ERR_STRING_DATA PROV_str_reasons[] = { "no instance allowed"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_NO_KEY_SET), "no key set"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_NO_PARAMETERS_SET), "no parameters set"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_NULL_LENGTH_POINTER), + "null length pointer"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_NULL_OUTPUT_BUFFER), + "null output buffer"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_ONESHOT_CALL_OUT_OF_ORDER), "oneshot call out of order"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE), @@ -223,11 +227,15 @@ static const ERR_STRING_DATA PROV_str_reasons[] = { "unsupported mac type"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS), "unsupported number of rounds"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNSUPPORTED_SELECTION), + "unsupported selection"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UPDATE_CALL_OUT_OF_ORDER), "update call out of order"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_URI_AUTHORITY_UNSUPPORTED), "uri authority unsupported"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_VALUE_ERROR), "value error"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_WRONG_CIPHERTEXT_SIZE), + "wrong ciphertext size"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_WRONG_FINAL_BLOCK_LENGTH), "wrong final block length"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_WRONG_OUTPUT_BUFFER_SIZE), diff --git a/providers/defltprov.c b/providers/defltprov.c index 4f42cb61ba..f8b6cb4d34 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -486,6 +486,14 @@ static const OSSL_ALGORITHM deflt_asym_kem[] = { { PROV_NAMES_ML_KEM_512, "provider=default", ossl_ml_kem_asym_kem_functions }, { PROV_NAMES_ML_KEM_768, "provider=default", ossl_ml_kem_asym_kem_functions }, { PROV_NAMES_ML_KEM_1024, "provider=default", ossl_ml_kem_asym_kem_functions }, +# if !defined(OPENSSL_NO_ECX) + { "X25519MLKEM768", "provider=default", ossl_mlx_kem_asym_kem_functions }, + { "X448MLKEM1024", "provider=default", ossl_mlx_kem_asym_kem_functions }, +# endif +# if !defined(OPENSSL_NO_EC) + { "SecP256r1MLKEM768", "provider=default", ossl_mlx_kem_asym_kem_functions }, + { "SecP384r1MLKEM1024", "provider=default", ossl_mlx_kem_asym_kem_functions }, +# endif #endif { NULL, NULL, NULL } }; @@ -556,6 +564,18 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = { PROV_DESCS_ML_KEM_768 }, { PROV_NAMES_ML_KEM_1024, "provider=default", ossl_ml_kem_1024_keymgmt_functions, PROV_DESCS_ML_KEM_1024 }, +# if !defined(OPENSSL_NO_ECX) + { PROV_NAMES_X25519MLKEM768, "provider=default", ossl_mlx_x25519_kem_kmgmt_functions, + PROV_DESCS_X25519MLKEM768 }, + { PROV_NAMES_X448MLKEM1024, "provider=default", ossl_mlx_x448_kem_kmgmt_functions, + PROV_DESCS_X448MLKEM1024 }, +# endif +# if !defined(OPENSSL_NO_EC) + { PROV_NAMES_SecP256r1MLKEM768, "provider=default", ossl_mlx_p256_kem_kmgmt_functions, + PROV_DESCS_SecP256r1MLKEM768 }, + { PROV_NAMES_SecP384r1MLKEM1024, "provider=default", ossl_mlx_p384_kem_kmgmt_functions, + PROV_DESCS_SecP384r1MLKEM1024 }, +# endif #endif { NULL, NULL, NULL } }; diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 8c284a9b47..79ee0628c9 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -308,18 +308,20 @@ extern const OSSL_DISPATCH ossl_dhx_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_dsa_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_rsa_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_rsapss_keymgmt_functions[]; -#ifndef OPENSSL_NO_ECX +extern const OSSL_DISPATCH ossl_kdf_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_mac_legacy_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_cmac_legacy_keymgmt_functions[]; +#ifndef OPENSSL_NO_EC +extern const OSSL_DISPATCH ossl_ec_keymgmt_functions[]; +# ifndef OPENSSL_NO_ECX extern const OSSL_DISPATCH ossl_x25519_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_x448_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_ed25519_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_ed448_keymgmt_functions[]; -#endif -extern const OSSL_DISPATCH ossl_ec_keymgmt_functions[]; -extern const OSSL_DISPATCH ossl_kdf_keymgmt_functions[]; -extern const OSSL_DISPATCH ossl_mac_legacy_keymgmt_functions[]; -extern const OSSL_DISPATCH ossl_cmac_legacy_keymgmt_functions[]; -#ifndef OPENSSL_NO_SM2 +# endif +# ifndef OPENSSL_NO_SM2 extern const OSSL_DISPATCH ossl_sm2_keymgmt_functions[]; +# endif #endif extern const OSSL_DISPATCH ossl_ml_dsa_44_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_ml_dsa_65_keymgmt_functions[]; @@ -328,13 +330,25 @@ extern const OSSL_DISPATCH ossl_ml_dsa_87_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_ml_kem_512_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_ml_kem_768_keymgmt_functions[]; extern const OSSL_DISPATCH ossl_ml_kem_1024_keymgmt_functions[]; +# ifndef OPENSSL_NO_EC +# ifndef OPENSSL_NO_ECX +extern const OSSL_DISPATCH ossl_mlx_x25519_kem_kmgmt_functions[]; +extern const OSSL_DISPATCH ossl_mlx_x448_kem_kmgmt_functions[]; +# endif +extern const OSSL_DISPATCH ossl_mlx_p256_kem_kmgmt_functions[]; +extern const OSSL_DISPATCH ossl_mlx_p384_kem_kmgmt_functions[]; +# endif #endif /* Key Exchange */ extern const OSSL_DISPATCH ossl_dh_keyexch_functions[]; +#ifndef OPENSSL_NO_EC +extern const OSSL_DISPATCH ossl_ecdh_keyexch_functions[]; +# ifndef OPENSSL_NO_ECX extern const OSSL_DISPATCH ossl_x25519_keyexch_functions[]; extern const OSSL_DISPATCH ossl_x448_keyexch_functions[]; -extern const OSSL_DISPATCH ossl_ecdh_keyexch_functions[]; +# endif +#endif extern const OSSL_DISPATCH ossl_kdf_tls1_prf_keyexch_functions[]; extern const OSSL_DISPATCH ossl_kdf_hkdf_keyexch_functions[]; extern const OSSL_DISPATCH ossl_kdf_scrypt_keyexch_functions[]; @@ -403,9 +417,18 @@ extern const OSSL_DISPATCH ossl_sm2_asym_cipher_functions[]; /* Asym Key encapsulation */ extern const OSSL_DISPATCH ossl_rsa_asym_kem_functions[]; -extern const OSSL_DISPATCH ossl_ecx_asym_kem_functions[]; +#ifndef OPENSSL_NO_EC extern const OSSL_DISPATCH ossl_ec_asym_kem_functions[]; +# ifndef OPENSSL_NO_ECX +extern const OSSL_DISPATCH ossl_ecx_asym_kem_functions[]; +# endif +#endif +#ifndef OPENSSL_NO_ML_KEM extern const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[]; +# ifndef OPENSSL_NO_EC +extern const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[]; +# endif +#endif /* Encoders */ extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_der_encoder_functions[]; diff --git a/providers/implementations/include/prov/mlx_kem.h b/providers/implementations/include/prov/mlx_kem.h new file mode 100644 index 0000000000..0a6d7794dc --- /dev/null +++ b/providers/implementations/include/prov/mlx_kem.h @@ -0,0 +1,38 @@ +#ifndef OSSL_MLX_KEM_H +# define OSSL_MLX_KEM_H +# pragma once + +#include +#include +#include +#include + +typedef struct ecdh_vinfo_st { + const char *algorithm_name; + const char *group_name; + size_t pubkey_bytes; + size_t prvkey_bytes; + size_t shsec_bytes; + int ml_kem_slot; + int ml_kem_variant; +} ECDH_VINFO; + +typedef struct mlx_key_st { + OSSL_LIB_CTX *libctx; + char *propq; + const ML_KEM_VINFO *minfo; + const ECDH_VINFO *xinfo; + EVP_PKEY *mkey; + EVP_PKEY *xkey; + unsigned int state; +} MLX_KEY; + +#define MLX_HAVE_NOKEYS 0 +#define MLX_HAVE_PUBKEY 1 +#define MLX_HAVE_PRVKEY 2 + +/* Both key parts have whatever the ML-KEM component has */ +#define mlx_kem_have_pubkey(key) ((key)->state > 0) +#define mlx_kem_have_prvkey(key) ((key)->state > 1) + +#endif diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h index 0c27be5bd4..1bb38f95b6 100644 --- a/providers/implementations/include/prov/names.h +++ b/providers/implementations/include/prov/names.h @@ -396,3 +396,11 @@ #define PROV_DESCS_ML_KEM_768 "OpenSSL ML-KEM-768 implementation" #define PROV_NAMES_ML_KEM_1024 "ML-KEM-1024" #define PROV_DESCS_ML_KEM_1024 "OpenSSL ML-KEM-1024 implementation" +#define PROV_NAMES_X25519MLKEM768 "X25519MLKEM768" +#define PROV_DESCS_X25519MLKEM768 "X25519+ML-KEM-768 TLS hybrid implementation" +#define PROV_NAMES_X448MLKEM1024 "X448MLKEM1024" +#define PROV_DESCS_X448MLKEM1024 "X448+ML-KEM-1024 TLS hybrid implementation" +#define PROV_NAMES_SecP256r1MLKEM768 "SecP256r1MLKEM768" +#define PROV_DESCS_SecP256r1MLKEM768 "P-256+ML-KEM-768 TLS hybrid implementation" +#define PROV_NAMES_SecP384r1MLKEM1024 "SecP384r1MLKEM1024" +#define PROV_DESCS_SecP384r1MLKEM1024 "P-384+ML-KEM-1024 TLS hybrid implementation" diff --git a/providers/implementations/kem/build.info b/providers/implementations/kem/build.info index 7ecaa2d9f2..901ccd862a 100644 --- a/providers/implementations/kem/build.info +++ b/providers/implementations/kem/build.info @@ -5,6 +5,7 @@ $RSA_KEM_GOAL=../../libdefault.a ../../libfips.a $EC_KEM_GOAL=../../libdefault.a $TEMPLATE_KEM_GOAL=../../libtemplate.a $ML_KEM_GOAL=../../libdefault.a +$TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a SOURCE[$RSA_KEM_GOAL]=rsa_kem.c @@ -15,7 +16,11 @@ IF[{- !$disabled{ec} -}] ENDIF ENDIF -SOURCE[$TEMPLATE_KEM_GOAL]=template_kem.c IF[{- !$disabled{'ml-kem'} -}] - SOURCE[$ML_KEM_GOAL] = ml_kem.c + IF[{- !$disabled{ec} -}] + SOURCE[$TLS_ML_KEM_HYBRID_GOAL]=mlx_kem.c + ENDIF + SOURCE[$ML_KEM_GOAL] = ml_kem.c ENDIF + +SOURCE[$TEMPLATE_KEM_GOAL]=template_kem.c diff --git a/providers/implementations/kem/ml_kem.c b/providers/implementations/kem/ml_kem.c index cf3de31904..e112b9397d 100644 --- a/providers/implementations/kem/ml_kem.c +++ b/providers/implementations/kem/ml_kem.c @@ -176,9 +176,10 @@ static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen, goto end; } - /* For now tolerate newly-deprecated NULL length pointers. */ if (clen == NULL) { - clen = &encap_clen; + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null ciphertext input/output length pointer"); + goto end; } else if (*clen < encap_clen) { ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, "ciphertext buffer too small"); @@ -188,7 +189,9 @@ static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen, } if (slen == NULL) { - slen = &encap_slen; + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null shared secret input/output length pointer"); + goto end; } else if (*slen < encap_slen) { ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, "shared-secret buffer too small"); @@ -252,16 +255,14 @@ static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen, return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key); } -typedef void (*func_ptr_t)(void); - const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = { - { OSSL_FUNC_KEM_NEWCTX, (func_ptr_t) ml_kem_newctx }, - { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (func_ptr_t) ml_kem_encapsulate_init }, - { OSSL_FUNC_KEM_ENCAPSULATE, (func_ptr_t) ml_kem_encapsulate }, - { OSSL_FUNC_KEM_DECAPSULATE_INIT, (func_ptr_t) ml_kem_decapsulate_init }, - { OSSL_FUNC_KEM_DECAPSULATE, (func_ptr_t) ml_kem_decapsulate }, - { OSSL_FUNC_KEM_FREECTX, (func_ptr_t) ml_kem_freectx }, - { OSSL_FUNC_KEM_SET_CTX_PARAMS, (func_ptr_t) ml_kem_set_ctx_params }, - { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (func_ptr_t) ml_kem_settable_ctx_params }, + { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) ml_kem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) ml_kem_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) ml_kem_encapsulate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) ml_kem_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) ml_kem_decapsulate }, + { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) ml_kem_freectx }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) ml_kem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) ml_kem_settable_ctx_params }, OSSL_DISPATCH_END }; diff --git a/providers/implementations/kem/mlx_kem.c b/providers/implementations/kem/mlx_kem.c new file mode 100644 index 0000000000..2952adfaf5 --- /dev/null +++ b/providers/implementations/kem/mlx_kem.c @@ -0,0 +1,341 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "prov/implementations.h" +#include "prov/mlx_kem.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx; +static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx; +static OSSL_FUNC_kem_encapsulate_init_fn mlx_kem_encapsulate_init; +static OSSL_FUNC_kem_encapsulate_fn mlx_kem_encapsulate; +static OSSL_FUNC_kem_decapsulate_init_fn mlx_kem_decapsulate_init; +static OSSL_FUNC_kem_decapsulate_fn mlx_kem_decapsulate; +static OSSL_FUNC_kem_set_ctx_params_fn mlx_kem_set_ctx_params; +static OSSL_FUNC_kem_settable_ctx_params_fn mlx_kem_settable_ctx_params; + +typedef struct { + OSSL_LIB_CTX *libctx; + MLX_KEY *key; + int op; +} PROV_MLX_KEM_CTX; + +static void *mlx_kem_newctx(void *provctx) +{ + PROV_MLX_KEM_CTX *ctx; + + if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL) + return NULL; + + ctx->libctx = PROV_LIBCTX_OF(provctx); + ctx->key = NULL; + ctx->op = 0; + return ctx; +} + +static void mlx_kem_freectx(void *vctx) +{ + OPENSSL_free(vctx); +} + +static int mlx_kem_init(void *vctx, int op, void *key, + ossl_unused const OSSL_PARAM params[]) +{ + PROV_MLX_KEM_CTX *ctx = vctx; + + if (!ossl_prov_is_running()) + return 0; + ctx->key = key; + ctx->op = op; + return 1; +} + +static int +mlx_kem_encapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + + if (!mlx_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + return mlx_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params); +} + +static int +mlx_kem_decapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + + if (!mlx_kem_have_prvkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + return mlx_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params); +} + +static const OSSL_PARAM *mlx_kem_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM params[] = { OSSL_PARAM_END }; + + return params; +} + +static int +mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + return 1; +} + +static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen, + unsigned char *shsec, size_t *slen) +{ + MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *xkey = NULL; + size_t encap_clen; + size_t encap_slen; + uint8_t *cbuf; + uint8_t *sbuf; + int ml_kem_slot = key->xinfo->ml_kem_slot; + int ret = 0; + + if (!mlx_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto end; + } + encap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes; + encap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes; + + if (ctext == NULL) { + if (clen == NULL && slen == NULL) + return 0; + if (clen != NULL) + *clen = encap_clen; + if (slen != NULL) + *slen = encap_slen; + return 1; + } + if (shsec == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER, + "null shared-secret output buffer"); + return 0; + } + + if (clen == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null ciphertext input/output length pointer"); + return 0; + } else if (*clen < encap_clen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "ciphertext buffer too small"); + return 0; + } else { + *clen = encap_clen; + } + + if (slen == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null shared secret input/output length pointer"); + return 0; + } else if (*slen < encap_slen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "shared-secret buffer too small"); + return 0; + } else { + *slen = encap_slen; + } + + /* ML-KEM encapsulation */ + encap_clen = key->minfo->ctext_bytes; + encap_slen = ML_KEM_SHARED_SECRET_BYTES; + cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes; + sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq); + if (ctx == NULL + || EVP_PKEY_encapsulate_init(ctx, NULL) <= 0 + || EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0) + goto end; + if (encap_clen != key->minfo->ctext_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s ciphertext output size: %lu", + key->minfo->algorithm_name, (unsigned long) encap_clen); + goto end; + } + if (encap_slen != ML_KEM_SHARED_SECRET_BYTES) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->minfo->algorithm_name, (unsigned long) encap_slen); + goto end; + } + EVP_PKEY_CTX_free(ctx); + + /*- + * ECDHE encapsulation + * + * Generate own ephemeral private key and add its public key to ctext. + * + * Note, we could support a settable parameter that sets an extant ECDH + * keypair as the keys to use in encap, making it possible to reuse the + * same (TLS client) ECDHE keypair for both the classical EC keyshare and a + * corresponding ECDHE + ML-KEM keypair. But the TLS layer would then need + * know that this is a hybrid, and that it can partly reuse the same keys + * as another group for which a keyshare will be sent. Deferred until we + * support generating multiple keyshares, there's a workable keyshare + * prediction specification, and the optimisation is justified. + */ + cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes; + encap_clen = key->xinfo->pubkey_bytes; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq); + if (ctx == NULL + || EVP_PKEY_keygen_init(ctx) <= 0 + || EVP_PKEY_keygen(ctx, &xkey) <= 0 + || EVP_PKEY_get_octet_string_param(xkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, + cbuf, encap_clen, &encap_clen) <= 0) + goto end; + if (encap_clen != key->xinfo->pubkey_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s public key output size: %lu", + key->xinfo->algorithm_name, (unsigned long) encap_clen); + goto end; + } + EVP_PKEY_CTX_free(ctx); + + /* Derive the ECDH shared secret */ + encap_slen = key->xinfo->shsec_bytes; + sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq); + if (ctx == NULL + || EVP_PKEY_derive_init(ctx) <= 0 + || EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0 + || EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0) + goto end; + if (encap_slen != key->xinfo->shsec_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->xinfo->algorithm_name, (unsigned long) encap_slen); + goto end; + } + + ret = 1; + end: + EVP_PKEY_free(xkey); + EVP_PKEY_CTX_free(ctx); + return ret; +} + +static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen, + const uint8_t *ctext, size_t clen) +{ + MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *xkey = NULL; + const uint8_t *cbuf; + uint8_t *sbuf; + size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes; + size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes; + int ml_kem_slot = key->xinfo->ml_kem_slot; + int ret = 0; + + if (!mlx_kem_have_prvkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + if (shsec == NULL) { + if (slen == NULL) + return 0; + *slen = decap_slen; + return 1; + } + + /* For now tolerate newly-deprecated NULL length pointers. */ + if (slen == NULL) { + slen = &decap_slen; + } else if (*slen < decap_slen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "shared-secret buffer too small"); + return 0; + } else { + *slen = decap_slen; + } + if (clen != decap_clen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE, + "wrong decapsulation input ciphertext size: %lu", + (unsigned long) clen); + return 0; + } + + /* ML-KEM decapsulation */ + decap_clen = key->minfo->ctext_bytes; + decap_slen = ML_KEM_SHARED_SECRET_BYTES; + cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes; + sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq); + if (ctx == NULL + || EVP_PKEY_decapsulate_init(ctx, NULL) <= 0 + || EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0) + goto end; + if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->minfo->algorithm_name, (unsigned long) decap_slen); + goto end; + } + EVP_PKEY_CTX_free(ctx); + + /* ECDH decapsulation */ + decap_clen = key->xinfo->pubkey_bytes; + decap_slen = key->xinfo->shsec_bytes; + cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes; + sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq); + if (ctx == NULL + || (xkey = EVP_PKEY_new()) == NULL + || EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0 + || EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0 + || EVP_PKEY_derive_init(ctx) <= 0 + || EVP_PKEY_derive_set_peer(ctx, xkey) <= 0 + || EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0) + goto end; + if (decap_slen != key->xinfo->shsec_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->xinfo->algorithm_name, (unsigned long) decap_slen); + goto end; + } + + ret = 1; + end: + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(xkey); + return ret; +} + +const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) mlx_kem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) mlx_kem_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) mlx_kem_encapsulate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) mlx_kem_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) mlx_kem_decapsulate }, + { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) mlx_kem_freectx }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) mlx_kem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) mlx_kem_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/providers/implementations/keymgmt/build.info b/providers/implementations/keymgmt/build.info index 4698e15611..6abc310e16 100644 --- a/providers/implementations/keymgmt/build.info +++ b/providers/implementations/keymgmt/build.info @@ -11,6 +11,7 @@ $RSA_GOAL=../../libdefault.a ../../libfips.a $TEMPLATE_GOAL=../../libtemplate.a $ML_DSA_GOAL=../../libdefault.a ../../libfips.a $ML_KEM_GOAL=../../libdefault.a +$TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a IF[{- !$disabled{dh} -}] SOURCE[$DH_GOAL]=dh_kmgmt.c @@ -39,6 +40,13 @@ IF[{- !$disabled{ec} -}] ENDIF ENDIF +IF[{- !$disabled{'ml-kem'} -}] + IF[{- !$disabled{ec} -}] + SOURCE[$TLS_ML_KEM_HYBRID_GOAL]=mlx_kmgmt.c + ENDIF + SOURCE[$ML_KEM_GOAL]=ml_kem_kmgmt.c +ENDIF + SOURCE[$RSA_GOAL]=rsa_kmgmt.c SOURCE[$KDF_GOAL]=kdf_legacy_kmgmt.c @@ -50,7 +58,3 @@ SOURCE[$TEMPLATE_GOAL]=template_kmgmt.c IF[{- !$disabled{'ml-dsa'} -}] SOURCE[$ML_DSA_GOAL]=ml_dsa_kmgmt.c ENDIF - -IF[{- !$disabled{'ml-kem'} -}] - SOURCE[$ML_KEM_GOAL]=ml_kem_kmgmt.c -ENDIF diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.c b/providers/implementations/keymgmt/ml_kem_kmgmt.c index cda7cec963..4de7608268 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.c +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.c @@ -44,6 +44,9 @@ static OSSL_FUNC_keymgmt_import_types_fn ml_kem_imexport_types; static OSSL_FUNC_keymgmt_export_types_fn ml_kem_imexport_types; static OSSL_FUNC_keymgmt_dup_fn ml_kem_dup; +static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS + | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + typedef struct ml_kem_gen_ctx_st { OSSL_LIB_CTX *libctx; char *propq; @@ -401,9 +404,6 @@ static int ml_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[]) static void *ml_kem_gen_init(void *provctx, int selection, const OSSL_PARAM params[], int variant) { - static const int minimal = - OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS - | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; PROV_ML_KEM_GEN_CTX *gctx = NULL; /* @@ -411,7 +411,7 @@ static void *ml_kem_gen_init(void *provctx, int selection, * appropriate. */ if (!ossl_prov_is_running() - || (selection & minimal) == 0 + || (selection & minimal_selection) == 0 || (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL) return NULL; @@ -489,8 +489,6 @@ static void *ml_kem_dup(const void *vkey, int selection) return ossl_ml_kem_key_dup(key, selection); } -typedef void (*func_ptr_t)(void); - #define DECLARE_VARIANT(bits) \ static void *ml_kem_##bits##_new(void *provctx) \ { \ @@ -503,24 +501,24 @@ typedef void (*func_ptr_t)(void); return ml_kem_gen_init(provctx, selection, params, ML_KEM_##bits##_VARIANT); \ } \ const OSSL_DISPATCH ossl_ml_kem_##bits##_keymgmt_functions[] = { \ - { OSSL_FUNC_KEYMGMT_NEW, (func_ptr_t) ml_kem_##bits##_new }, \ - { OSSL_FUNC_KEYMGMT_FREE, (func_ptr_t) ossl_ml_kem_key_free }, \ - { OSSL_FUNC_KEYMGMT_GET_PARAMS, (func_ptr_t) ml_kem_get_params }, \ - { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (func_ptr_t) ml_kem_gettable_params }, \ - { OSSL_FUNC_KEYMGMT_SET_PARAMS, (func_ptr_t) ml_kem_set_params }, \ - { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (func_ptr_t) ml_kem_settable_params }, \ - { OSSL_FUNC_KEYMGMT_HAS, (func_ptr_t) ml_kem_has }, \ - { OSSL_FUNC_KEYMGMT_MATCH, (func_ptr_t) ml_kem_match }, \ - { OSSL_FUNC_KEYMGMT_GEN_INIT, (func_ptr_t) ml_kem_##bits##_gen_init }, \ - { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (func_ptr_t) ml_kem_gen_set_params }, \ - { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (func_ptr_t) ml_kem_gen_settable_params }, \ - { OSSL_FUNC_KEYMGMT_GEN, (func_ptr_t) ml_kem_gen }, \ - { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (func_ptr_t) ml_kem_gen_cleanup }, \ - { OSSL_FUNC_KEYMGMT_DUP, (func_ptr_t) ml_kem_dup }, \ - { OSSL_FUNC_KEYMGMT_IMPORT, (func_ptr_t) ml_kem_import }, \ - { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (func_ptr_t) ml_kem_imexport_types }, \ - { OSSL_FUNC_KEYMGMT_EXPORT, (func_ptr_t) ml_kem_export }, \ - { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (func_ptr_t) ml_kem_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) ml_kem_##bits##_new }, \ + { OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) ossl_ml_kem_key_free }, \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) ml_kem_get_params }, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) ml_kem_gettable_params }, \ + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) ml_kem_set_params }, \ + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) ml_kem_settable_params }, \ + { OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) ml_kem_has }, \ + { OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) ml_kem_match }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) ml_kem_##bits##_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) ml_kem_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) ml_kem_gen_settable_params }, \ + { OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) ml_kem_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) ml_kem_gen_cleanup }, \ + { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) ml_kem_dup }, \ + { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) ml_kem_import }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) ml_kem_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) ml_kem_export }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) ml_kem_imexport_types }, \ OSSL_DISPATCH_END \ } DECLARE_VARIANT(512); diff --git a/providers/implementations/keymgmt/mlx_kmgmt.c b/providers/implementations/keymgmt/mlx_kmgmt.c new file mode 100644 index 0000000000..d0dce6202c --- /dev/null +++ b/providers/implementations/keymgmt/mlx_kmgmt.c @@ -0,0 +1,804 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal/nelem.h" +#include "internal/param_build_set.h" +#include "prov/implementations.h" +#include "prov/mlx_kem.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/securitycheck.h" + +static OSSL_FUNC_keymgmt_gen_fn mlx_kem_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn mlx_kem_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn mlx_kem_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn mlx_kem_gen_settable_params; +static OSSL_FUNC_keymgmt_get_params_fn mlx_kem_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn mlx_kem_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn mlx_kem_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn mlx_kem_settable_params; +static OSSL_FUNC_keymgmt_has_fn mlx_kem_has; +static OSSL_FUNC_keymgmt_match_fn mlx_kem_match; +static OSSL_FUNC_keymgmt_import_fn mlx_kem_import; +static OSSL_FUNC_keymgmt_export_fn mlx_kem_export; +static OSSL_FUNC_keymgmt_import_types_fn mlx_kem_imexport_types; +static OSSL_FUNC_keymgmt_export_types_fn mlx_kem_imexport_types; +static OSSL_FUNC_keymgmt_dup_fn mlx_kem_dup; + +static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS + | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + +/* Must match DECLARE_DISPATCH invocations at the end of the file */ +static const ECDH_VINFO hybrid_vtable[] = { + { "EC", "P-256", 65, 32, 32, 1, ML_KEM_768_VARIANT }, + { "EC", "P-384", 97, 48, 48, 1, ML_KEM_1024_VARIANT }, +#if !defined(OPENSSL_NO_ECX) + { "X25519", NULL, 32, 32, 32, 0, ML_KEM_768_VARIANT }, + { "X448", NULL, 56, 56, 56, 0, ML_KEM_1024_VARIANT }, +#endif +}; + +typedef struct mlx_kem_gen_ctx_st { + OSSL_LIB_CTX *libctx; + char *propq; + int selection; + unsigned int variant; +} PROV_ML_KEM_GEN_CTX; + +static void mlx_kem_key_free(void *vkey) +{ + MLX_KEY *key = vkey; + + if (key == NULL) + return; + OPENSSL_free(key->propq); + EVP_PKEY_free(key->mkey); + EVP_PKEY_free(key->xkey); + OPENSSL_free(key); +} + +/* Takes ownership of propq */ +static void * +mlx_kem_key_new(unsigned int v, OSSL_LIB_CTX *libctx, char *propq) +{ + MLX_KEY *key = NULL; + unsigned int ml_kem_variant; + + if (!ossl_prov_is_running() + || v >= OSSL_NELEM(hybrid_vtable) + || (key = OPENSSL_malloc(sizeof(*key))) == NULL) + goto err; + + ml_kem_variant = hybrid_vtable[v].ml_kem_variant; + key->libctx = libctx; + key->minfo = ossl_ml_kem_get_vinfo(ml_kem_variant); + key->xinfo = &hybrid_vtable[v]; + key->xkey = key->mkey = NULL; + key->state = MLX_HAVE_NOKEYS; + key->propq = propq; + return key; + + err: + OPENSSL_free(propq); + return NULL; +} + + +static int mlx_kem_has(const void *vkey, int selection) +{ + const MLX_KEY *key = vkey; + + /* A NULL key MUST fail to have anything */ + if (!ossl_prov_is_running() || key == NULL) + return 0; + + switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) { + case 0: + return 1; + case OSSL_KEYMGMT_SELECT_PUBLIC_KEY: + return mlx_kem_have_pubkey(key); + default: + return mlx_kem_have_prvkey(key); + } +} + +static int mlx_kem_match(const void *vkey1, const void *vkey2, int selection) +{ + const MLX_KEY *key1 = vkey1; + const MLX_KEY *key2 = vkey2; + int have_pub1 = mlx_kem_have_pubkey(key1); + int have_pub2 = mlx_kem_have_pubkey(key2); + + if (!ossl_prov_is_running()) + return 0; + + /* Compare domain parameters */ + if (key1->xinfo != key2->xinfo) + return 0; + + if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR)) + return 1; + + if (have_pub1 ^ have_pub2) + return 0; + + /* As in other providers, equal when both have no key material. */ + if (!have_pub1) + return 1; + + return EVP_PKEY_eq(key1->mkey, key2->mkey) + && EVP_PKEY_eq(key1->xkey, key2->xkey); +} + +typedef struct export_cb_arg_st { + const char *algorithm_name; + uint8_t *pubenc; + uint8_t *prvenc; + int pubcount; + int prvcount; + size_t puboff; + size_t prvoff; + size_t publen; + size_t prvlen; +} EXPORT_CB_ARG; + +/* Copy any exported key material into its storage slot */ +static int export_sub_cb(const OSSL_PARAM *params, void *varg) +{ + EXPORT_CB_ARG *sub_arg = varg; + const OSSL_PARAM *p = NULL; + size_t len; + + /* + * The caller will decide whether anything essential is missing, but, if + * some key material was returned, it should have the right (parameter) + * data type and length. + */ + if (ossl_param_is_empty(params)) + return 1; + if (sub_arg->pubenc != NULL + && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL) { + void *pub = sub_arg->pubenc + sub_arg->puboff; + + if (OSSL_PARAM_get_octet_string(p, &pub, sub_arg->publen, &len) != 1) + return 0; + if (len != sub_arg->publen) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "Unexpected %s public key length %lu != %lu", + sub_arg->algorithm_name, (unsigned long) len, + sub_arg->publen); + return 0; + } + ++sub_arg->pubcount; + } + if (sub_arg->prvenc != NULL + && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL) { + void *prv = sub_arg->prvenc + sub_arg->prvoff; + + if (OSSL_PARAM_get_octet_string(p, &prv, sub_arg->prvlen, &len) != 1) + return 0; + if (len != sub_arg->prvlen) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "Unexpected %s private key length %lu != %lu", + sub_arg->algorithm_name, (unsigned long) len, + (unsigned long) sub_arg->publen); + return 0; + } + ++sub_arg->prvcount; + } + return 1; +} + +static int +export_sub(EXPORT_CB_ARG *sub_arg, int selection, MLX_KEY *key) +{ + int slot; + + /* + * The caller is responsible for initialising only the pubenc and prvenc + * pointer fields, the rest are set here or in the callback. + */ + sub_arg->pubcount = 0; + sub_arg->prvcount = 0; + + for (slot = 0; slot < 2; ++slot) { + int ml_kem_slot = key->xinfo->ml_kem_slot; + EVP_PKEY *pkey; + + /* Export the parts of each component into its storage slot */ + if (slot == ml_kem_slot) { + pkey = key->mkey; + sub_arg->algorithm_name = key->minfo->algorithm_name; + sub_arg->puboff = slot * key->xinfo->pubkey_bytes; + sub_arg->prvoff = slot * key->xinfo->prvkey_bytes; + sub_arg->publen = key->minfo->pubkey_bytes; + sub_arg->prvlen = key->minfo->prvkey_bytes; + } else { + pkey = key->xkey; + sub_arg->algorithm_name = key->xinfo->algorithm_name; + sub_arg->puboff = (1 - ml_kem_slot) * key->minfo->pubkey_bytes; + sub_arg->prvoff = (1 - ml_kem_slot) * key->minfo->prvkey_bytes; + sub_arg->publen = key->xinfo->pubkey_bytes; + sub_arg->prvlen = key->xinfo->prvkey_bytes; + } + if (!EVP_PKEY_export(pkey, selection, export_sub_cb, (void *)sub_arg)) + return 0; + } + return 1; +} + +static int mlx_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + MLX_KEY *key = vkey; + OSSL_PARAM_BLD *tmpl = NULL; + OSSL_PARAM *params = NULL; + size_t publen; + size_t prvlen; + int ret = 0; + EXPORT_CB_ARG sub_arg; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + /* Fail when no key material has yet been provided */ + if (!mlx_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes; + prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes; + memset(&sub_arg, 0, sizeof(sub_arg)); + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + sub_arg.pubenc = OPENSSL_malloc(publen); + if (sub_arg.pubenc == NULL) + goto err; + } + + if (mlx_kem_have_prvkey(key) + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + /* + * Allocated on the secure heap if configured, this is detected in + * ossl_param_build_set_octet_string(), which will then also use the + * secure heap. + */ + sub_arg.prvenc = OPENSSL_secure_zalloc(prvlen); + if (sub_arg.prvenc == NULL) + goto err; + } + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + goto err; + + /* Extract sub-component key material */ + if (!export_sub(&sub_arg, selection, key)) + goto err; + + if (sub_arg.pubenc != NULL && sub_arg.pubcount == 2 + && !ossl_param_build_set_octet_string( + tmpl, NULL, OSSL_PKEY_PARAM_PUB_KEY, sub_arg.pubenc, publen)) + goto err; + + if (sub_arg.prvenc != NULL && sub_arg.prvcount == 2 + && !ossl_param_build_set_octet_string( + tmpl, NULL, OSSL_PKEY_PARAM_PRIV_KEY, sub_arg.prvenc, prvlen)) + goto err; + + params = OSSL_PARAM_BLD_to_param(tmpl); + if (params == NULL) + goto err; + + ret = param_cb(params, cbarg); + OSSL_PARAM_free(params); + +err: + OSSL_PARAM_BLD_free(tmpl); + OPENSSL_secure_clear_free(sub_arg.prvenc, prvlen); + OPENSSL_free(sub_arg.pubenc); + return ret; +} + +static const OSSL_PARAM *mlx_kem_imexport_types(int selection) +{ + static const OSSL_PARAM key_types[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_END + }; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return key_types; + return NULL; +} + +static int +load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname, + int selection, MLX_KEY *key, int slot, const uint8_t *in, + int mbytes, int xbytes) +{ + EVP_PKEY_CTX *ctx; + EVP_PKEY **ppkey; + OSSL_PARAM parr[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; + const char *alg; + char *group = NULL; + size_t off, len; + void *val; + int ml_kem_slot = key->xinfo->ml_kem_slot; + int ret = 0; + + if (slot == ml_kem_slot) { + alg = key->minfo->algorithm_name; + ppkey = &key->mkey; + off = slot * xbytes; + len = mbytes; + } else { + alg = key->xinfo->algorithm_name; + group = (char *) key->xinfo->group_name; + ppkey = &key->xkey; + off = (1 - ml_kem_slot) * mbytes; + len = xbytes; + } + val = (void *)(in + off); + + if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, propq)) == NULL + || EVP_PKEY_fromdata_init(ctx) <= 0) + goto err; + parr[0] = OSSL_PARAM_construct_octet_string(pname, val, len); + if (group != NULL) + parr[1] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + group, 0); + if (EVP_PKEY_fromdata(ctx, ppkey, selection, parr) > 0) + ret = 1; + + err: + EVP_PKEY_CTX_free(ctx); + return ret; +} + +static int +load_keys(MLX_KEY *key, + const uint8_t *pubenc, size_t publen, + const uint8_t *prvenc, size_t prvlen) +{ + int slot; + + for (slot = 0; slot < 2; ++slot) { + if (prvlen) { + /* Ignore public keys when private provided */ + if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PRIV_KEY, + minimal_selection, key, slot, prvenc, + key->minfo->prvkey_bytes, key->xinfo->prvkey_bytes)) + goto err; + } else if (publen) { + /* Absent private key data, import public keys */ + if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PUB_KEY, + minimal_selection, key, slot, pubenc, + key->minfo->pubkey_bytes, key->xinfo->pubkey_bytes)) + goto err; + } + } + key->state = prvlen ? MLX_HAVE_PRVKEY : MLX_HAVE_PUBKEY; + return 1; + + err: + EVP_PKEY_free(key->mkey); + EVP_PKEY_free(key->xkey); + key->xkey = key->mkey = NULL; + key->state = MLX_HAVE_NOKEYS; + return 0; +} + +static int mlx_kem_key_fromdata(MLX_KEY *key, + const OSSL_PARAM params[], + int include_private) +{ + const OSSL_PARAM *param_prv_key = NULL, *param_pub_key; + const void *pubenc = NULL, *prvenc = NULL; + size_t pubkey_bytes, prvkey_bytes; + size_t publen = 0, prvlen = 0; + + /* Invalid attempt to mutate a key, what is the right error to report? */ + if (key == NULL || mlx_kem_have_pubkey(key)) + return 0; + pubkey_bytes = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes; + prvkey_bytes = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes; + + /* What does the caller want to set? */ + param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); + if (param_pub_key != NULL && + OSSL_PARAM_get_octet_string_ptr(param_pub_key, &pubenc, &publen) != 1) + return 0; + if (include_private) + param_prv_key = OSSL_PARAM_locate_const(params, + OSSL_PKEY_PARAM_PRIV_KEY); + if (param_prv_key != NULL && + OSSL_PARAM_get_octet_string_ptr(param_prv_key, &prvenc, &prvlen) != 1) + return 0; + + /* The caller MUST specify at least one of the public or private keys. */ + if (publen == 0 && prvlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + /* + * When a pubkey is provided, its length MUST be correct, if a private key + * is also provided, the public key will be otherwise ignored. We could + * look for a matching encoded block, but unclear this is useful. + */ + if (publen != 0 && publen != pubkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (prvlen != 0 && prvlen != prvkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + + return load_keys(key, pubenc, publen, prvenc, prvlen); +} + +static int mlx_kem_import(void *vkey, int selection, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + int include_private; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + return mlx_kem_key_fromdata(key, params, include_private); +} + +static const OSSL_PARAM *mlx_kem_gettable_params(void *provctx) +{ + static const OSSL_PARAM arr[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_END + }; + + return arr; +} + +/* + * It is assumed the key is guaranteed non-NULL here, and is from this provider + */ +static int mlx_kem_get_params(void *vkey, OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + OSSL_PARAM *p, *pub, *prv = NULL; + EXPORT_CB_ARG sub_arg; + int selection; + size_t publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes; + size_t prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes; + + /* The reported "bit" count is those of the ML-KEM key */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, key->minfo->bits)) + return 0; + + /* The reported security bits are those of the ML-KEM key */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, key->minfo->secbits)) + return 0; + + /* The ciphertext sizes are additive */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, key->minfo->ctext_bytes + key->xinfo->pubkey_bytes)) + return 0; + + if (!mlx_kem_have_pubkey(key)) + return 1; + + memset(&sub_arg, 0, sizeof(sub_arg)); + pub = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (pub != NULL) { + if (pub->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + pub->return_size = publen; + if (pub->data == NULL) { + pub = NULL; + } else if (pub->data_size < publen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "public key output buffer too short: %lu < %lu", + (unsigned long) pub->data_size, + (unsigned long) publen); + return 0; + } else { + sub_arg.pubenc = pub->data; + } + } + if (mlx_kem_have_prvkey(key)) { + prv = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (prv != NULL) { + if (prv->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + prv->return_size = prvlen; + if (prv->data == NULL) { + prv = NULL; + } else if (prv->data_size < prvlen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "private key output buffer too short: %lu < %lu", + (unsigned long) prv->data_size, + (unsigned long) prvlen); + return 0; + } else { + sub_arg.prvenc = prv->data; + } + } + } + if (pub == NULL && prv == NULL) + return 1; + + selection = prv == NULL ? 0 : OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + selection |= pub == NULL ? 0 : OSSL_KEYMGMT_SELECT_PUBLIC_KEY; + if (key->xinfo->group_name != NULL) + selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS; + + /* Extract sub-component key material */ + if (!export_sub(&sub_arg, selection, key)) + return 0; + + if ((pub != NULL && sub_arg.pubcount != 2) + || (prv != NULL && sub_arg.prvcount != 2)) + return 0; + + return 1; +} + +static const OSSL_PARAM *mlx_kem_settable_params(void *provctx) +{ + static const OSSL_PARAM arr[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END + }; + + return arr; +} + +static int mlx_kem_set_params(void *vkey, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + const OSSL_PARAM *p; + const void *pubenc = NULL; + size_t publen = 0; + + if (ossl_param_is_empty(params)) + return 1; + + /* Only one settable parameter is supported */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p == NULL) + return 1; + + /* Key mutation is reportedly generally not allowed */ + if (mlx_kem_have_pubkey(key)) { + ERR_raise_data(ERR_LIB_PROV, + PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE, + "keys cannot be mutated"); + return 0; + } + /* An unlikely failure mode is the parameter having some unexpected type */ + if (!OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + OPENSSL_free(key->propq); + key->propq = NULL; + if (!OSSL_PARAM_get_utf8_string(p, &key->propq, 0)) + return 0; + } + + if (publen != key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + + return load_keys(key, pubenc, publen, NULL, 0); +} + +static int mlx_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[]) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + const OSSL_PARAM *p; + + if (gctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->propq); + if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL) + return 0; + } + return 1; +} + +static void *mlx_kem_gen_init(int v, OSSL_LIB_CTX *libctx, int selection, + const OSSL_PARAM params[]) +{ + PROV_ML_KEM_GEN_CTX *gctx = NULL; + + /* + * We can only generate private keys, check that the selection is + * appropriate. + */ + if (!ossl_prov_is_running() + || (selection & minimal_selection) == 0 + || (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL) + return NULL; + + gctx->variant = v; + gctx->libctx = libctx; + gctx->selection = selection; + if (mlx_kem_gen_set_params(gctx, params)) + return gctx; + + mlx_kem_gen_cleanup(gctx); + return NULL; +} + +static const OSSL_PARAM *mlx_kem_gen_settable_params(ossl_unused void *vgctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + + return settable; +} + +static void *mlx_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + MLX_KEY *key; + char *propq = gctx->propq; + + if (gctx == NULL + || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + return NULL; + + /* Lose ownership of propq */ + gctx->propq = NULL; + if ((key = mlx_kem_key_new(gctx->variant, gctx->libctx, propq)) == NULL) + return NULL; + + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + /* For now, using the same "propq" for all components */ + key->mkey = EVP_PKEY_Q_keygen(key->libctx, key->propq, + key->minfo->algorithm_name); + key->xkey = EVP_PKEY_Q_keygen(key->libctx, key->propq, + key->xinfo->algorithm_name, + key->xinfo->group_name); + if (key->mkey != NULL && key->xkey != NULL) { + key->state = MLX_HAVE_PRVKEY; + return key; + } + + mlx_kem_key_free(key); + return NULL; +} + +static void mlx_kem_gen_cleanup(void *vgctx) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + + if (gctx == NULL) + return; + OPENSSL_free(gctx->propq); + OPENSSL_free(gctx); +} + +static void *mlx_kem_dup(const void *vkey, int selection) +{ + const MLX_KEY *key = vkey; + MLX_KEY *ret; + + if (!ossl_prov_is_running() + || (ret = OPENSSL_memdup(key, sizeof(*ret))) == NULL) + return NULL; + + switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) { + case 0: + ret->xkey = ret->mkey = NULL; + return ret; + case OSSL_KEYMGMT_SELECT_KEYPAIR: + ret->mkey = EVP_PKEY_dup(key->mkey); + ret->xkey = EVP_PKEY_dup(key->xkey); + if (ret->xkey != NULL && ret->mkey != NULL) + return ret; + break; + default: + ERR_raise_data(ERR_LIB_PROV, PROV_R_UNSUPPORTED_SELECTION, + "duplication of partial key material not supported"); + break; + } + + mlx_kem_key_free(ret); + return NULL; +} + +#define DECLARE_DISPATCH(name, variant) \ + static OSSL_FUNC_keymgmt_new_fn mlx_##name##_kem_new; \ + static void *mlx_##name##_kem_new(void *provctx) \ + { \ + OSSL_LIB_CTX *libctx; \ + \ + libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \ + return mlx_kem_key_new(variant, libctx, NULL); \ + } \ + static OSSL_FUNC_keymgmt_gen_init_fn mlx_##name##_kem_gen_init; \ + static void *mlx_##name##_kem_gen_init(void *provctx, int selection, \ + const OSSL_PARAM params[]) \ + { \ + OSSL_LIB_CTX *libctx; \ + \ + libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \ + return mlx_kem_gen_init(variant, libctx, selection, params); \ + } \ + const OSSL_DISPATCH ossl_mlx_##name##_kem_kmgmt_functions[] = { \ + { OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) mlx_##name##_kem_new }, \ + { OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) mlx_kem_key_free }, \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) mlx_kem_get_params }, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gettable_params }, \ + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) mlx_kem_set_params }, \ + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_settable_params }, \ + { OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) mlx_kem_has }, \ + { OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) mlx_kem_match }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) mlx_##name##_kem_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) mlx_kem_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gen_settable_params }, \ + { OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) mlx_kem_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) mlx_kem_gen_cleanup }, \ + { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) mlx_kem_dup }, \ + { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) mlx_kem_import }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) mlx_kem_export }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \ + OSSL_DISPATCH_END \ + } +/* See |hybrid_vtable| above */ +DECLARE_DISPATCH(p256, 0); +DECLARE_DISPATCH(p384, 1); +#if !defined(OPENSSL_NO_ECX) +DECLARE_DISPATCH(x25519, 2); +DECLARE_DISPATCH(x448, 3); +#endif diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index dadc59dbb3..5e7f89847d 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -4079,6 +4079,12 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) &ctx->ext.tuples_len, parg); + case SSL_CTRL_GET0_IMPLEMENTED_GROUPS: + return tls1_get0_implemented_groups(ctx->min_proto_version, + ctx->max_proto_version, + ctx->group_list, + ctx->group_list_len, larg, parg); + case SSL_CTRL_SET_SIGALGS: return tls1_set_sigalgs(ctx->cert, parg, larg, 0); diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 45d898bc55..28dcec8b96 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -2839,6 +2839,11 @@ __owur int tls1_group_id2nid(uint16_t group_id, int include_unknown); __owur uint16_t tls1_nid2group_id(int nid); __owur int tls1_check_group_id(SSL_CONNECTION *s, uint16_t group_id, int check_own_curves); +__owur int tls1_get0_implemented_groups(int min_proto_version, + int max_proto_version, + TLS_GROUP_INFO *grps, + size_t num, long all, + STACK_OF(OPENSSL_CSTRING) *out); __owur uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch); __owur int tls1_set_groups(uint16_t **grpext, size_t *grpextlen, uint16_t **ksext, size_t *ksextlen, diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 5fc12c5b3b..011ddc51d9 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -977,6 +977,81 @@ static int tls1_in_list(uint16_t id, const uint16_t *list, size_t listlen) return 0; } +typedef struct { + TLS_GROUP_INFO *grp; + size_t ix; +} TLS_GROUP_IX; + +DEFINE_STACK_OF(TLS_GROUP_IX) + +static void free_wrapper(TLS_GROUP_IX *a) +{ + OPENSSL_free(a); +} + +static int tls_group_ix_cmp(const TLS_GROUP_IX *const *a, + const TLS_GROUP_IX *const *b) +{ + int idcmpab = (*a)->grp->group_id < (*b)->grp->group_id; + int idcmpba = (*b)->grp->group_id < (*a)->grp->group_id; + int ixcmpab = (*a)->ix < (*b)->ix; + int ixcmpba = (*b)->ix < (*a)->ix; + + /* Ascending by group id */ + if (idcmpab != idcmpba) + return (idcmpba - idcmpab); + /* Ascending by original appearance index */ + return ixcmpba - ixcmpab; +} + +int tls1_get0_implemented_groups(int min_proto_version, int max_proto_version, + TLS_GROUP_INFO *grps, size_t num, long all, + STACK_OF(OPENSSL_CSTRING) *out) +{ + STACK_OF(TLS_GROUP_IX) *collect = NULL; + TLS_GROUP_IX *gix; + uint16_t id = 0; + int ret = 0; + size_t ix; + + if ((collect = sk_TLS_GROUP_IX_new(tls_group_ix_cmp)) == NULL) + return 0; + + if (grps == NULL || out == NULL) + return 0; + for (ix = 0; ix < num; ++ix, ++grps) { + if (grps->mintls > 0 && max_proto_version > 0 + && grps->mintls > max_proto_version) + continue; + if (grps->maxtls > 0 && min_proto_version > 0 + && grps->maxtls < min_proto_version) + continue; + + if ((gix = OPENSSL_malloc(sizeof(*gix))) == NULL) + goto end; + gix->grp = grps; + gix->ix = ix; + if (sk_TLS_GROUP_IX_push(collect, gix) <= 0) + goto end; + } + + sk_TLS_GROUP_IX_sort(collect); + num = sk_TLS_GROUP_IX_num(collect); + for (ix = 0; ix < num; ++ix) { + gix = sk_TLS_GROUP_IX_value(collect, ix); + if (!all && gix->grp->group_id == id) + continue; + id = gix->grp->group_id; + if (sk_OPENSSL_CSTRING_push(out, gix->grp->tlsname) <= 0) + goto end; + } + return 1; + + end: + sk_TLS_GROUP_IX_pop_free(collect, free_wrapper); + return ret; +} + /*- * For nmatch >= 0, return the id of the |nmatch|th shared group or 0 * if there is no match. diff --git a/test/sslapitest.c b/test/sslapitest.c index faed3e74d5..26b87fcbf3 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -4938,9 +4938,12 @@ static int test_ciphersuite_change(void) * Test 13 = Test MLKEM512 * Test 14 = Test MLKEM768 * Test 15 = Test MLKEM1024 - * Test 16 = Test all ML-KEM with TLSv1.2 client and server - * Test 17 = Test all FFDHE with TLSv1.2 client and server - * Test 18 = Test all ECDHE with TLSv1.2 client and server + * Test 16 = Test X25519MLKEM768 + * Test 17 = Test SecP256r1MLKEM768 + * Test 18 = Test SecP384r1MLKEM1024 + * Test 19 = Test all ML-KEM with TLSv1.2 client and server + * Test 20 = Test all FFDHE with TLSv1.2 client and server + * Test 21 = Test all ECDHE with TLSv1.2 client and server */ # ifndef OPENSSL_NO_EC static int ecdhe_kexch_groups[] = {NID_X9_62_prime256v1, NID_secp384r1, @@ -4970,7 +4973,7 @@ static int test_key_exchange(int idx) switch (idx) { # ifndef OPENSSL_NO_EC # ifndef OPENSSL_NO_TLS1_2 - case 18: + case 21: max_version = TLS1_2_VERSION; # endif /* Fall through */ @@ -5008,7 +5011,7 @@ static int test_key_exchange(int idx) # endif # ifndef OPENSSL_NO_DH # ifndef OPENSSL_NO_TLS1_2 - case 17: + case 20: max_version = TLS1_2_VERSION; kexch_name0 = "ffdhe2048"; # endif @@ -5041,7 +5044,7 @@ static int test_key_exchange(int idx) # endif # ifndef OPENSSL_NO_ML_KEM # if !defined(OPENSSL_NO_TLS1_2) - case 16: + case 19: max_version = TLS1_2_VERSION; # if !defined(OPENSSL_NO_EC) /* Set at least one EC group so the handshake completes */ @@ -5083,6 +5086,25 @@ static int test_key_exchange(int idx) kexch_name0 = "MLKEM1024"; kexch_names = kexch_name0; break; +# ifndef OPENSSL_NO_EC +# ifndef OPENSSL_NO_ECX + case 16: + kexch_groups = NULL; + kexch_name0 = "X25519MLKEM768"; + kexch_names = kexch_name0; + break; +# endif + case 17: + kexch_groups = NULL; + kexch_name0 = "SecP256r1MLKEM768"; + kexch_names = kexch_name0; + break; + case 18: + kexch_groups = NULL; + kexch_name0 = "SecP384r1MLKEM1024"; + kexch_names = kexch_name0; + break; +# endif # endif default: /* We're skipping this test */ @@ -5090,7 +5112,7 @@ static int test_key_exchange(int idx) } /* ML-KEM not yet supported in the FIPS module */ - if (is_fips && idx >= 12 && idx <= 16) { + if (is_fips && idx >= 12 && idx <= 19) { testresult = 1; goto end; }; @@ -5144,13 +5166,14 @@ static int test_key_exchange(int idx) goto end; /* - * If Handshake succeeds the negotiated kexch alg should be the first one in - * configured, except in the case of FFDHE and ML-KEM groups (idx == 17, 18), - * which are TLSv1.3 only so we expect no shared group to exist. + * If the handshake succeeds the negotiated kexch alg should be the first + * one in configured, except in the case of "all" FFDHE and "all" ML-KEM + * groups (idx == 19, 20), which are TLSv1.3 only so we expect no shared + * group to exist. */ shared_group0 = SSL_get_shared_group(serverssl, 0); switch (idx) { - case 16: + case 19: # if !defined(OPENSSL_NO_EC) /* MLKEM + TLS 1.2 and no DH => "secp526r1" */ if (!TEST_int_eq(shared_group0, NID_X9_62_prime256v1)) @@ -5158,7 +5181,7 @@ static int test_key_exchange(int idx) break; # endif /* Fall through */ - case 17: + case 20: if (!TEST_int_eq(shared_group0, 0)) goto end; break; @@ -12961,7 +12984,7 @@ int setup_tests(void) #endif /* OSSL_NO_USABLE_TLS1_3 */ # ifndef OPENSSL_NO_TLS1_2 /* Test with both TLSv1.3 and 1.2 versions */ - ADD_ALL_TESTS(test_key_exchange, 18); + ADD_ALL_TESTS(test_key_exchange, 21); # if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_DH) ADD_ALL_TESTS(test_negotiated_group, 4 * (OSSL_NELEM(ecdhe_kexch_groups) @@ -12969,7 +12992,7 @@ int setup_tests(void) # endif # else /* Test with only TLSv1.3 versions */ - ADD_ALL_TESTS(test_key_exchange, 15); + ADD_ALL_TESTS(test_key_exchange, 18); # endif ADD_ALL_TESTS(test_custom_exts, 6); ADD_TEST(test_stateless); diff --git a/util/other.syms b/util/other.syms index 896592875a..5ae03f4e1a 100644 --- a/util/other.syms +++ b/util/other.syms @@ -542,6 +542,7 @@ SSL_CTX_disable_ct define SSL_CTX_generate_session_ticket_fn define SSL_CTX_get0_chain_certs define SSL_CTX_get0_chain_cert_store define +SSL_CTX_get0_implemented_groups define SSL_CTX_get0_verify_cert_store define SSL_CTX_get_default_read_ahead define SSL_CTX_get_extra_chain_certs define