mirror of
https://github.com/openssl/openssl.git
synced 2025-03-31 20:10:45 +08:00
Add support for multiple key shares
This PR is the implementation of concluded discussion that occurred in a draft PR #25605. This changes were mainly authored by @martinschmatz with some contribution from myself. It addresses issue #21633 This extends the group list definition to support a more complex definition while still retaining backward compatibility with the simple form of colon separated groups. Details of the agreed format and expected behaviour can be found in #25605 and in the documentation changes. Signed-off-by: Dave Kelsey <d_kelsey@uk.ibm.com> Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Neil Horman <nhorman@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26445)
This commit is contained in:
parent
78991c9e37
commit
d69c014608
@ -30,6 +30,13 @@ OpenSSL 3.5
|
||||
|
||||
### Changes between 3.4 and 3.5 [xx XXX xxxx]
|
||||
|
||||
* For TLSv1.3: Add capability for a client to send multiple key shares. Extend the scope of
|
||||
`SSL_OP_CIPHER_SERVER_PREFERENCE` to cover server-side key exchange group selection.
|
||||
Extend the server-side key exchange group selection algorithm and related group list syntax
|
||||
to support multiple group priorities, e.g. to prioritize (hybrid-)KEMs.
|
||||
|
||||
*David Kelsey*, *Martin Schmatz*
|
||||
|
||||
* A new random generation API has been introduced which modifies all
|
||||
of the L<RAND_bytes(3)> family of calls so they are routed through a
|
||||
specific named provider instead of being resolved via the normal DRBG
|
||||
|
@ -152,14 +152,22 @@ value set for B<-sigalgs> will be used instead.
|
||||
This sets the supported groups. For clients, the groups are sent using
|
||||
the supported groups extension. For servers, it is used to determine which
|
||||
group to use. This setting affects groups used for signatures (in TLSv1.2
|
||||
and earlier) and key exchange. The first group listed will also be used
|
||||
for the B<key_share> sent by a client in a TLSv1.3 B<ClientHello>.
|
||||
and earlier) and key exchange.
|
||||
|
||||
The B<groups> argument is a colon separated list of groups. The group can
|
||||
be either the B<NIST> name (e.g. B<P-256>), some other commonly used name
|
||||
where applicable (e.g. B<X25519>, B<ffdhe2048>) or an OpenSSL OID name
|
||||
(e.g. B<prime256v1>). Group names are case sensitive. The list should be
|
||||
in order of preference with the most preferred group first.
|
||||
In its simplest form the I<groups> argument is a colon separated list of
|
||||
groups. Each group can be either the B<NIST> name (e.g. B<P-256>), some other
|
||||
commonly used name where applicable (e.g. B<X25519>, B<ffdhe2048>) or an
|
||||
OpenSSL OID name (e.g. B<prime256v1>). 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<key_share> sent by a client
|
||||
in a TLSv1.3 B<ClientHello>.
|
||||
|
||||
An enriched alternative syntax, that enables clients to send multiple keyshares
|
||||
and allows servers to prioritise some groups over others, is described in
|
||||
L<SSL_CTX_set1_groups_list(3)>.
|
||||
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.
|
||||
|
||||
Groups for B<TLSv1.3> in the default provider are B<P-256>, B<P-384>,
|
||||
B<P-521>, B<X25519>, B<X448>, B<ffdhe2048>, B<ffdhe3072>, B<ffdhe4096>,
|
||||
|
111
doc/man3/SSL_CTX_set1_curves.pod
Normal file → Executable file
111
doc/man3/SSL_CTX_set1_curves.pod
Normal file → Executable file
@ -51,41 +51,116 @@ B<NID_brainpoolP512r1tls13>, B<NID_ffdhe2048>, B<NID_ffdhe3072>,
|
||||
B<NID_ffdhe4096>, B<NID_ffdhe6144> and B<NID_ffdhe8192>.
|
||||
OpenSSL will use this array in different ways depending on TLS role and version:
|
||||
|
||||
=over 4
|
||||
|
||||
=item For a TLS client, the groups are used directly in the supported groups
|
||||
For a TLS client, the groups are used directly in the supported groups
|
||||
extension. The extension's preference order, to be evaluated by the server, is
|
||||
determined by the order of the elements in the array.
|
||||
|
||||
=item For a TLS 1.2 server, the groups determine the selected group. If
|
||||
For a TLS 1.2 server, the groups determine the selected group. If
|
||||
B<SSL_OP_CIPHER_SERVER_PREFERENCE> is set, the order of the elements in the
|
||||
array determines the selected group. Otherwise, the order is ignored and the
|
||||
client's order determines the selection.
|
||||
|
||||
=item For a TLS 1.3 server, the groups determine the selected group, but
|
||||
For a TLS 1.3 server, the groups determine the selected group, but
|
||||
selection is more complex. A TLS 1.3 client sends both a group list as well as a
|
||||
predicted subset of groups. Choosing a group outside the predicted subset incurs
|
||||
an extra roundtrip. However, in some situations, the most preferred group may
|
||||
not be predicted. OpenSSL considers all supported groups to be comparable in
|
||||
security and prioritizes avoiding roundtrips above either client or server
|
||||
not be predicted. OpenSSL considers all supported groups in I<clist> to be comparable
|
||||
in security and prioritizes avoiding roundtrips above either client or server
|
||||
preference order. If an application uses an external provider to extend OpenSSL
|
||||
with, e.g., a post-quantum algorithm, this behavior may allow a network attacker
|
||||
to downgrade connections to a weaker algorithm.
|
||||
|
||||
=back
|
||||
to downgrade connections to a weaker algorithm. It is therefore recommended
|
||||
to use SSL_CTX_set1_groups_list() with the ability to specify group tuples.
|
||||
|
||||
SSL_CTX_set1_groups_list() sets the supported groups for B<ctx> to
|
||||
string B<list>. The string is a colon separated list of group names, for example
|
||||
"P-521:P-384:P-256:X25519:ffdhe2048". The groups are used as in
|
||||
SSL_CTX_set1_groups(), described above. Currently supported groups for
|
||||
string I<list>. In contrast to SSL_CTX_set1_groups(), the names of the
|
||||
groups, rather than their NIDs, are used. Currently supported groups for
|
||||
B<TLSv1.3> are B<P-256>, B<P-384>, B<P-521>, B<X25519>, B<X448>,
|
||||
B<brainpoolP256r1tls13>, B<brainpoolP384r1tls13>, B<brainpoolP512r1tls13>,
|
||||
B<ffdhe2048>, B<ffdhe3072>, B<ffdhe4096>, B<ffdhe6144> and B<ffdhe8192>. Support
|
||||
for other groups may be added by external providers, however note the discussion
|
||||
on TLS 1.3 selection criteria above. If a group name is preceded with the C<?>
|
||||
character, it will be ignored if an implementation is missing. The "DEFAULT"
|
||||
group name is used to select the default selection of groups. Group names that
|
||||
are preceded with the C<-> character will be removed from the selected groups.
|
||||
for other groups may be added by external providers.
|
||||
Each group can be either the B<NIST> name (e.g. B<P-256>), some other commonly
|
||||
used name where applicable (e.g. B<X25519>, B<ffdhe2048>) or an OpenSSL OID name
|
||||
(e.g. B<prime256v1>). Group names are case sensitive. The preferred group names
|
||||
are those defined by IANA for TLS parameters.
|
||||
|
||||
The I<list> can be used to define several group tuples of comparable security
|
||||
levels, and can specify which key shares should be sent by a client.
|
||||
The specified list elements can optionally be ignored, if not implemented
|
||||
(listing unknown groups otherwise results in error).
|
||||
It is also possible to specify the built-in default set of groups, and to explicitly
|
||||
remove a group from that list.
|
||||
|
||||
In its simplest form, the string I<list> is just a colon separated list
|
||||
of group names, for example "P-521:P-384:P-256:X25519:ffdhe2048". The first
|
||||
group listed will also be used for the B<key_share> sent by a client in a
|
||||
TLSv1.3 B<ClientHello>. For servers note the discussion above. The list should
|
||||
be in order of preference with the most preferred group first.
|
||||
|
||||
Group tuples of comparable security are defined by replacing any group separator
|
||||
C<:> with a tuple separator C</>. Keyshares to be sent by a client are specified
|
||||
by prepending a C<*> to the group name, while any C<*> will be ignored by a
|
||||
server. The following string I<list> for example defines three tuples when
|
||||
used on the server-side, and triggers the generation of three key shares
|
||||
when used on the client-side: P-521:*P-256/*P-384/*X25519:P-384:ffdhe2048.
|
||||
|
||||
If a group name is preceded with the C<?> character, it will be ignored if an
|
||||
implementation is missing. If a group name is preceded with the C<-> character, it
|
||||
will be removed from the list of groups if present (including not sending a
|
||||
key share for this group), ignored otherwise. The pseudo group name
|
||||
C<DEFAULT> can be used to select the OpenSSL built-in default list of groups.
|
||||
|
||||
For a TLS 1.3 client, all the groups in the string I<list> are added to the
|
||||
supported groups extension of a C<ClientHello>, in the order in which they are listed,
|
||||
thereby interpreting tuple separators as group separators. The extension's
|
||||
preference order, to be evaluated by the server, is determined by the
|
||||
order of the elements in the array, see below.
|
||||
|
||||
If a group name is preceded by C<*>, a key share will be sent for this group.
|
||||
When preceding C<DEFAULT> with C<*>, a key share will be sent for the first group
|
||||
of the OpenSSL built-in default list of groups. If no C<*> is used anywhere in the list,
|
||||
a single key share for the leftmost valid group is sent. A maximum of 4 key shares
|
||||
are supported. Example: "P-521:*P-256/*P-384" will add P-521, P-256 and P-384 to the
|
||||
supported groups extension in a C<ClientHello> and will send key shares for P-256 and P-384.
|
||||
|
||||
For a TLS 1.3 server, the groups in the string I<list> will be used to determine which group
|
||||
is used for the key agreement. The preference order of the group tuples is determined
|
||||
by the order of the tuples in the array, and the preference order of the groups within
|
||||
a group tuple is determined by the order of the groups in the tuple. Server preference
|
||||
can be enforced by setting B<SSL_OP_CIPHER_SERVER_PREFERENCE> using
|
||||
B<SSL_set_options> (default: client preference).
|
||||
|
||||
The server will select the group to be used for a key agreement using the following
|
||||
pseudo-code algorithm:
|
||||
|
||||
FOR each group tuple
|
||||
IF client preference (= default)
|
||||
FOR each client key-share group
|
||||
IF current key-share group is also part of current group tuple: SH, return success
|
||||
FOR each client supported groups
|
||||
IF current supported group is also part of current group tuple: HRR, return success
|
||||
ELSE (= server preference = with SSL_OP_CIPHER_SERVER_PREFERENCE option set)
|
||||
FOR each group in current tuple
|
||||
IF current group is also part of client key-share groups: SH, return success
|
||||
FOR each group in current tuple
|
||||
IF current group is also part of client supported groups: HRR, return success
|
||||
return failure
|
||||
|
||||
with : SH: Server hello with current group
|
||||
HRR: Server retry request with current group
|
||||
|
||||
Hence, if a client supports a group in a server group tuple, but does not send a key
|
||||
share for this group, a Hello Retry Request (HRR) is triggered, asking the client
|
||||
to send a new Hello message with a more preferred keyshare. For example:
|
||||
Assume the server I<list> is "P-521:P-256/P-384/X25519:ffdhe2048".
|
||||
If a client I<list> is "P-521:*P-384" when connecting to such a server, meaning that
|
||||
the client supports C<P-521> but does not send a key share for this group to the server,
|
||||
and the client supports C<P-384> including key share for this group, a HRR will be
|
||||
triggered for C<P-521> despite the availability of a key share for P-384, which
|
||||
overlaps with a lower priority server-side tuple.
|
||||
|
||||
A group name can optionally be preceded by any of C<*>, C<?> or C<->, in any order, with
|
||||
the exception that only C<*> is allowed to precede C<DEFAULT>. Separator characters
|
||||
C<:> and C</> are only allowed inside the I<list> and not at the very beginning or end.
|
||||
|
||||
SSL_set1_groups() and SSL_set1_groups_list() are similar except they set
|
||||
supported groups for the SSL structure B<ssl>.
|
||||
|
71
ssl/s3_lib.c
71
ssl/s3_lib.c
@ -3403,6 +3403,7 @@ int ssl3_new(SSL *s)
|
||||
void ssl3_free(SSL *s)
|
||||
{
|
||||
SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
|
||||
size_t i;
|
||||
|
||||
if (sc == NULL)
|
||||
return;
|
||||
@ -3411,8 +3412,21 @@ void ssl3_free(SSL *s)
|
||||
|
||||
EVP_PKEY_free(sc->s3.peer_tmp);
|
||||
sc->s3.peer_tmp = NULL;
|
||||
EVP_PKEY_free(sc->s3.tmp.pkey);
|
||||
sc->s3.tmp.pkey = NULL;
|
||||
|
||||
for (i = 0; i < sc->s3.tmp.num_ks_pkey; i++)
|
||||
if (sc->s3.tmp.ks_pkey[i] != NULL) {
|
||||
if (sc->s3.tmp.pkey == sc->s3.tmp.ks_pkey[i])
|
||||
sc->s3.tmp.pkey = NULL;
|
||||
|
||||
EVP_PKEY_free(sc->s3.tmp.ks_pkey[i]);
|
||||
sc->s3.tmp.ks_pkey[i] = NULL;
|
||||
}
|
||||
sc->s3.tmp.num_ks_pkey = 0;
|
||||
|
||||
if (sc->s3.tmp.pkey != NULL) {
|
||||
EVP_PKEY_free(sc->s3.tmp.pkey);
|
||||
sc->s3.tmp.pkey = NULL;
|
||||
}
|
||||
|
||||
ssl_evp_cipher_free(sc->s3.tmp.new_sym_enc);
|
||||
ssl_evp_md_free(sc->s3.tmp.new_hash);
|
||||
@ -3442,6 +3456,7 @@ int ssl3_clear(SSL *s)
|
||||
{
|
||||
SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
|
||||
int flags;
|
||||
size_t i;
|
||||
|
||||
if (sc == NULL)
|
||||
return 0;
|
||||
@ -3455,9 +3470,23 @@ int ssl3_clear(SSL *s)
|
||||
OPENSSL_free(sc->s3.tmp.peer_cert_sigalgs);
|
||||
OPENSSL_free(sc->s3.tmp.valid_flags);
|
||||
|
||||
EVP_PKEY_free(sc->s3.tmp.pkey);
|
||||
EVP_PKEY_free(sc->s3.peer_tmp);
|
||||
|
||||
for (i = 0; i < sc->s3.tmp.num_ks_pkey; i++)
|
||||
if (sc->s3.tmp.ks_pkey[i] != NULL) {
|
||||
if (sc->s3.tmp.pkey == sc->s3.tmp.ks_pkey[i])
|
||||
sc->s3.tmp.pkey = NULL;
|
||||
|
||||
EVP_PKEY_free(sc->s3.tmp.ks_pkey[i]);
|
||||
sc->s3.tmp.ks_pkey[i] = NULL;
|
||||
}
|
||||
sc->s3.tmp.num_ks_pkey = 0;
|
||||
|
||||
if (sc->s3.tmp.pkey != NULL) {
|
||||
EVP_PKEY_free(sc->s3.tmp.pkey);
|
||||
sc->s3.tmp.pkey = NULL;
|
||||
}
|
||||
|
||||
ssl3_free_digest_list(sc);
|
||||
|
||||
OPENSSL_free(sc->s3.alpn_selected);
|
||||
@ -3561,6 +3590,10 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
|
||||
}
|
||||
return ssl_set_tmp_ecdh_groups(&sc->ext.supportedgroups,
|
||||
&sc->ext.supportedgroups_len,
|
||||
&sc->ext.keyshares,
|
||||
&sc->ext.keyshares_len,
|
||||
&sc->ext.tuples,
|
||||
&sc->ext.tuples_len,
|
||||
parg);
|
||||
}
|
||||
#endif /* !OPENSSL_NO_DEPRECATED_3_0 */
|
||||
@ -3713,11 +3746,22 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
|
||||
|
||||
case SSL_CTRL_SET_GROUPS:
|
||||
return tls1_set_groups(&sc->ext.supportedgroups,
|
||||
&sc->ext.supportedgroups_len, parg, larg);
|
||||
&sc->ext.supportedgroups_len,
|
||||
&sc->ext.keyshares,
|
||||
&sc->ext.keyshares_len,
|
||||
&sc->ext.tuples,
|
||||
&sc->ext.tuples_len,
|
||||
parg, larg);
|
||||
|
||||
case SSL_CTRL_SET_GROUPS_LIST:
|
||||
return tls1_set_groups_list(s->ctx, &sc->ext.supportedgroups,
|
||||
&sc->ext.supportedgroups_len, parg);
|
||||
return tls1_set_groups_list(s->ctx,
|
||||
&sc->ext.supportedgroups,
|
||||
&sc->ext.supportedgroups_len,
|
||||
&sc->ext.keyshares,
|
||||
&sc->ext.keyshares_len,
|
||||
&sc->ext.tuples,
|
||||
&sc->ext.tuples_len,
|
||||
parg);
|
||||
|
||||
case SSL_CTRL_GET_SHARED_GROUP:
|
||||
{
|
||||
@ -3913,6 +3957,10 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
|
||||
}
|
||||
return ssl_set_tmp_ecdh_groups(&ctx->ext.supportedgroups,
|
||||
&ctx->ext.supportedgroups_len,
|
||||
&ctx->ext.keyshares,
|
||||
&ctx->ext.keyshares_len,
|
||||
&ctx->ext.tuples,
|
||||
&ctx->ext.tuples_len,
|
||||
parg);
|
||||
}
|
||||
#endif /* !OPENSSL_NO_DEPRECATED_3_0 */
|
||||
@ -4014,11 +4062,20 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
|
||||
case SSL_CTRL_SET_GROUPS:
|
||||
return tls1_set_groups(&ctx->ext.supportedgroups,
|
||||
&ctx->ext.supportedgroups_len,
|
||||
&ctx->ext.keyshares,
|
||||
&ctx->ext.keyshares_len,
|
||||
&ctx->ext.tuples,
|
||||
&ctx->ext.tuples_len,
|
||||
parg, larg);
|
||||
|
||||
case SSL_CTRL_SET_GROUPS_LIST:
|
||||
return tls1_set_groups_list(ctx, &ctx->ext.supportedgroups,
|
||||
return tls1_set_groups_list(ctx,
|
||||
&ctx->ext.supportedgroups,
|
||||
&ctx->ext.supportedgroups_len,
|
||||
&ctx->ext.keyshares,
|
||||
&ctx->ext.keyshares_len,
|
||||
&ctx->ext.tuples,
|
||||
&ctx->ext.tuples_len,
|
||||
parg);
|
||||
|
||||
case SSL_CTRL_SET_SIGALGS:
|
||||
|
@ -847,6 +847,26 @@ SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, SSL *user_ssl,
|
||||
}
|
||||
s->ext.supportedgroups_len = ctx->ext.supportedgroups_len;
|
||||
}
|
||||
if (ctx->ext.keyshares != NULL) {
|
||||
s->ext.keyshares =
|
||||
OPENSSL_memdup(ctx->ext.keyshares,
|
||||
ctx->ext.keyshares_len * sizeof(*ctx->ext.keyshares));
|
||||
if (s->ext.keyshares == NULL) {
|
||||
s->ext.keyshares_len = 0;
|
||||
goto err;
|
||||
}
|
||||
s->ext.keyshares_len = ctx->ext.keyshares_len;
|
||||
}
|
||||
if (ctx->ext.tuples != NULL) {
|
||||
s->ext.tuples =
|
||||
OPENSSL_memdup(ctx->ext.tuples,
|
||||
ctx->ext.tuples_len * sizeof(*ctx->ext.tuples));
|
||||
if (s->ext.tuples == NULL) {
|
||||
s->ext.tuples_len = 0;
|
||||
goto err;
|
||||
}
|
||||
s->ext.tuples_len = ctx->ext.tuples_len;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
s->ext.npn = NULL;
|
||||
@ -1445,6 +1465,8 @@ void ossl_ssl_connection_free(SSL *ssl)
|
||||
OPENSSL_free(s->ext.ecpointformats);
|
||||
OPENSSL_free(s->ext.peer_ecpointformats);
|
||||
OPENSSL_free(s->ext.supportedgroups);
|
||||
OPENSSL_free(s->ext.keyshares);
|
||||
OPENSSL_free(s->ext.tuples);
|
||||
OPENSSL_free(s->ext.peer_supportedgroups);
|
||||
sk_X509_EXTENSION_pop_free(s->ext.ocsp.exts, X509_EXTENSION_free);
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
@ -3065,11 +3087,12 @@ static int ssl_tsan_load(SSL_CTX *ctx, TSAN_QUALIFIER int *stat)
|
||||
long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
|
||||
{
|
||||
long l;
|
||||
/* For some cases with ctx == NULL perform syntax checks */
|
||||
|
||||
/* For some cases with ctx == NULL or larg == 1 perform syntax checks */
|
||||
if (cmd == SSL_CTRL_SET_GROUPS_LIST && larg == 1)
|
||||
return tls1_set_groups_list(ctx, NULL, NULL, NULL, NULL, NULL, NULL, parg);
|
||||
if (ctx == NULL) {
|
||||
switch (cmd) {
|
||||
case SSL_CTRL_SET_GROUPS_LIST:
|
||||
return tls1_set_groups_list(ctx, NULL, NULL, parg);
|
||||
case SSL_CTRL_SET_SIGALGS_LIST:
|
||||
case SSL_CTRL_SET_CLIENT_SIGALGS_LIST:
|
||||
return tls1_set_sigalgs_list(ctx, NULL, parg, 0);
|
||||
@ -4327,6 +4350,8 @@ void SSL_CTX_free(SSL_CTX *a)
|
||||
|
||||
OPENSSL_free(a->ext.ecpointformats);
|
||||
OPENSSL_free(a->ext.supportedgroups);
|
||||
OPENSSL_free(a->ext.keyshares);
|
||||
OPENSSL_free(a->ext.tuples);
|
||||
OPENSSL_free(a->ext.supported_groups_default);
|
||||
OPENSSL_free(a->ext.alpn);
|
||||
OPENSSL_secure_free(a->ext.secure);
|
||||
|
@ -787,6 +787,11 @@ typedef struct {
|
||||
|
||||
# define TLS_GROUP_FFDHE_FOR_TLS1_3 (TLS_GROUP_FFDHE|TLS_GROUP_ONLY_FOR_TLS1_3)
|
||||
|
||||
/* We limit the number of key shares sent */
|
||||
# ifndef OPENSSL_CLIENT_MAX_KEY_SHARES
|
||||
# define OPENSSL_CLIENT_MAX_KEY_SHARES 4
|
||||
# endif
|
||||
|
||||
struct ssl_ctx_st {
|
||||
OSSL_LIB_CTX *libctx;
|
||||
|
||||
@ -1016,6 +1021,12 @@ struct ssl_ctx_st {
|
||||
size_t supportedgroups_len;
|
||||
uint16_t *supportedgroups;
|
||||
|
||||
size_t keyshares_len;
|
||||
uint16_t *keyshares;
|
||||
|
||||
size_t tuples_len; /* Number of group tuples */
|
||||
size_t *tuples; /* Number of groups in each group tuple */
|
||||
|
||||
uint16_t *supported_groups_default;
|
||||
size_t supported_groups_default_len;
|
||||
/*
|
||||
@ -1315,6 +1326,10 @@ struct ssl_connection_st {
|
||||
/* used to hold the new cipher we are going to use */
|
||||
const SSL_CIPHER *new_cipher;
|
||||
EVP_PKEY *pkey; /* holds short lived key exchange key */
|
||||
/* holds the array of short lived key exchange key (pointers) */
|
||||
EVP_PKEY *ks_pkey[OPENSSL_CLIENT_MAX_KEY_SHARES];
|
||||
uint16_t ks_group_id[OPENSSL_CLIENT_MAX_KEY_SHARES]; /* The IDs of the keyshare keys */
|
||||
size_t num_ks_pkey; /* how many keyshares are there */
|
||||
/* used for certificate requests */
|
||||
int cert_req;
|
||||
/* Certificate types in certificate request message. */
|
||||
@ -1433,7 +1448,8 @@ struct ssl_connection_st {
|
||||
/* The group_id for the key exchange key */
|
||||
uint16_t group_id;
|
||||
EVP_PKEY *peer_tmp;
|
||||
|
||||
/* The cached group_id candidate for the key exchange key */
|
||||
uint16_t group_id_candidate;
|
||||
} s3;
|
||||
|
||||
struct dtls1_state_st *d1; /* DTLSv1 variables */
|
||||
@ -1597,21 +1613,28 @@ struct ssl_connection_st {
|
||||
int ticket_expected;
|
||||
/* TLS 1.3 tickets requested by the application. */
|
||||
int extra_tickets_expected;
|
||||
|
||||
/* our list */
|
||||
size_t ecpointformats_len;
|
||||
/* our list */
|
||||
unsigned char *ecpointformats;
|
||||
|
||||
size_t peer_ecpointformats_len;
|
||||
/* peer's list */
|
||||
size_t peer_ecpointformats_len;
|
||||
unsigned char *peer_ecpointformats;
|
||||
size_t supportedgroups_len;
|
||||
/* our list */
|
||||
uint16_t *supportedgroups;
|
||||
|
||||
/* our list */
|
||||
size_t supportedgroups_len;
|
||||
uint16_t *supportedgroups;
|
||||
/* peer's list */
|
||||
size_t peer_supportedgroups_len;
|
||||
/* peer's list */
|
||||
uint16_t *peer_supportedgroups;
|
||||
|
||||
/* key shares */
|
||||
size_t keyshares_len;
|
||||
uint16_t *keyshares;
|
||||
/* supported groups tuples */
|
||||
size_t tuples_len;
|
||||
size_t *tuples;
|
||||
|
||||
/* TLS Session Ticket extension override */
|
||||
TLS_SESSION_TICKET_EXT *session_ticket;
|
||||
/* TLS Session Ticket extension callback */
|
||||
@ -2594,6 +2617,8 @@ __owur int ssl_encapsulate(SSL_CONNECTION *s, EVP_PKEY *pubkey,
|
||||
int gensecret);
|
||||
__owur EVP_PKEY *ssl_dh_to_pkey(DH *dh);
|
||||
__owur int ssl_set_tmp_ecdh_groups(uint16_t **pext, size_t *pextlen,
|
||||
uint16_t **ksext, size_t *ksextlen,
|
||||
size_t **tplext, size_t *tplextlen,
|
||||
void *key);
|
||||
__owur unsigned int ssl_get_max_send_fragment(const SSL_CONNECTION *sc);
|
||||
__owur unsigned int ssl_get_split_send_fragment(const SSL_CONNECTION *sc);
|
||||
@ -2793,9 +2818,14 @@ __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 uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch);
|
||||
__owur int tls1_set_groups(uint16_t **pext, size_t *pextlen,
|
||||
__owur int tls1_set_groups(uint16_t **grpext, size_t *grpextlen,
|
||||
uint16_t **ksext, size_t *ksextlen,
|
||||
size_t **tplext, size_t *tplextlen,
|
||||
int *curves, size_t ncurves);
|
||||
__owur int tls1_set_groups_list(SSL_CTX *ctx, uint16_t **pext, size_t *pextlen,
|
||||
__owur int tls1_set_groups_list(SSL_CTX *ctx,
|
||||
uint16_t **grpext, size_t *grpextlen,
|
||||
uint16_t **ksext, size_t *ksextlen,
|
||||
size_t **tplext, size_t *tplextlen,
|
||||
const char *str);
|
||||
__owur EVP_PKEY *ssl_generate_pkey_group(SSL_CONNECTION *s, uint16_t id);
|
||||
__owur int tls_valid_group(SSL_CONNECTION *s, uint16_t group_id, int minversion,
|
||||
@ -2808,6 +2838,10 @@ __owur int tls1_check_ec_tmp_key(SSL_CONNECTION *s, unsigned long id);
|
||||
__owur int tls_group_allowed(SSL_CONNECTION *s, uint16_t curve, int op);
|
||||
void tls1_get_supported_groups(SSL_CONNECTION *s, const uint16_t **pgroups,
|
||||
size_t *pgroupslen);
|
||||
void tls1_get_requested_keyshare_groups(SSL_CONNECTION *s, const uint16_t **pgroups,
|
||||
size_t *pgroupslen);
|
||||
void tls1_get_group_tuples(SSL_CONNECTION *s, const size_t **ptuples,
|
||||
size_t *ptupleslen);
|
||||
|
||||
__owur int tls1_set_server_sigalgs(SSL_CONNECTION *s);
|
||||
|
||||
|
@ -1448,36 +1448,12 @@ static int final_key_share(SSL_CONNECTION *s, unsigned int context, int sent)
|
||||
/* No suitable key_share */
|
||||
if (s->hello_retry_request == SSL_HRR_NONE && sent
|
||||
&& (!s->hit
|
||||
|| (s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE)
|
||||
!= 0)) {
|
||||
const uint16_t *pgroups, *clntgroups;
|
||||
size_t num_groups, clnt_num_groups, i;
|
||||
unsigned int group_id = 0;
|
||||
|| (s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE) != 0)) {
|
||||
|
||||
/* Check if a shared group exists */
|
||||
|
||||
/* Get the clients list of supported groups. */
|
||||
tls1_get_peer_groups(s, &clntgroups, &clnt_num_groups);
|
||||
tls1_get_supported_groups(s, &pgroups, &num_groups);
|
||||
|
||||
/*
|
||||
* Find the first group we allow that is also in client's list
|
||||
*/
|
||||
for (i = 0; i < num_groups; i++) {
|
||||
group_id = pgroups[i];
|
||||
|
||||
if (check_in_list(s, group_id, clntgroups, clnt_num_groups,
|
||||
1)
|
||||
&& tls_group_allowed(s, group_id,
|
||||
SSL_SECOP_CURVE_SUPPORTED)
|
||||
&& tls_valid_group(s, group_id, TLS1_3_VERSION,
|
||||
TLS1_3_VERSION, 0, NULL))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < num_groups) {
|
||||
/* Did we detect group overlap in tls_parse_ctos_key_share ? */
|
||||
if (s->s3.group_id_candidate != 0) {
|
||||
/* A shared group exists so send a HelloRetryRequest */
|
||||
s->s3.group_id = group_id;
|
||||
s->s3.group_id = s->s3.group_id_candidate;
|
||||
s->hello_retry_request = SSL_HRR_PENDING;
|
||||
return 1;
|
||||
}
|
||||
|
@ -626,13 +626,13 @@ EXT_RETURN tls_construct_ctos_psk_kex_modes(SSL_CONNECTION *s, WPACKET *pkt,
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
static int add_key_share(SSL_CONNECTION *s, WPACKET *pkt, unsigned int curve_id)
|
||||
static int add_key_share(SSL_CONNECTION *s, WPACKET *pkt, unsigned int group_id, size_t loop_num)
|
||||
{
|
||||
unsigned char *encoded_point = NULL;
|
||||
unsigned char *encoded_pubkey = NULL;
|
||||
EVP_PKEY *key_share_key = NULL;
|
||||
size_t encodedlen;
|
||||
|
||||
if (s->s3.tmp.pkey != NULL) {
|
||||
if (s->s3.tmp.pkey != NULL && loop_num == 0) {
|
||||
if (!ossl_assert(s->hello_retry_request == SSL_HRR_PENDING)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||
return 0;
|
||||
@ -642,7 +642,7 @@ static int add_key_share(SSL_CONNECTION *s, WPACKET *pkt, unsigned int curve_id)
|
||||
*/
|
||||
key_share_key = s->s3.tmp.pkey;
|
||||
} else {
|
||||
key_share_key = ssl_generate_pkey_group(s, curve_id);
|
||||
key_share_key = ssl_generate_pkey_group(s, group_id);
|
||||
if (key_share_key == NULL) {
|
||||
/* SSLfatal() already called */
|
||||
return 0;
|
||||
@ -651,33 +651,36 @@ static int add_key_share(SSL_CONNECTION *s, WPACKET *pkt, unsigned int curve_id)
|
||||
|
||||
/* Encode the public key. */
|
||||
encodedlen = EVP_PKEY_get1_encoded_public_key(key_share_key,
|
||||
&encoded_point);
|
||||
&encoded_pubkey);
|
||||
if (encodedlen == 0) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EC_LIB);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Create KeyShareEntry */
|
||||
if (!WPACKET_put_bytes_u16(pkt, curve_id)
|
||||
|| !WPACKET_sub_memcpy_u16(pkt, encoded_point, encodedlen)) {
|
||||
if (!WPACKET_put_bytes_u16(pkt, group_id)
|
||||
|| !WPACKET_sub_memcpy_u16(pkt, encoded_pubkey, encodedlen)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* When changing to send more than one key_share we're
|
||||
* going to need to be able to save more than one EVP_PKEY. For now
|
||||
* we reuse the existing tmp.pkey
|
||||
*/
|
||||
s->s3.tmp.pkey = key_share_key;
|
||||
s->s3.group_id = curve_id;
|
||||
OPENSSL_free(encoded_point);
|
||||
/* For backward compatibility, we use the first valid group to add a key share */
|
||||
if (loop_num == 0) {
|
||||
s->s3.tmp.pkey = key_share_key;
|
||||
s->s3.group_id = group_id;
|
||||
}
|
||||
/* We ensure in t1_lib.c that the loop number does not exceed OPENSSL_CLIENT_MAX_KEY_SHARES */
|
||||
s->s3.tmp.ks_pkey[loop_num] = key_share_key;
|
||||
s->s3.tmp.ks_group_id[loop_num] = group_id;
|
||||
s->s3.tmp.num_ks_pkey++;
|
||||
|
||||
OPENSSL_free(encoded_pubkey);
|
||||
|
||||
return 1;
|
||||
err:
|
||||
if (s->s3.tmp.pkey == NULL)
|
||||
EVP_PKEY_free(key_share_key);
|
||||
OPENSSL_free(encoded_point);
|
||||
OPENSSL_free(encoded_pubkey);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -689,48 +692,68 @@ EXT_RETURN tls_construct_ctos_key_share(SSL_CONNECTION *s, WPACKET *pkt,
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
size_t i, num_groups = 0;
|
||||
const uint16_t *pgroups = NULL;
|
||||
uint16_t curve_id = 0;
|
||||
uint16_t group_id = 0;
|
||||
int add_only_one = 0;
|
||||
size_t valid_keyshare = 0;
|
||||
|
||||
/* key_share extension */
|
||||
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share)
|
||||
/* Extension data sub-packet */
|
||||
/* Extension data sub-packet */
|
||||
|| !WPACKET_start_sub_packet_u16(pkt)
|
||||
/* KeyShare list sub-packet */
|
||||
/* KeyShare list sub-packet */
|
||||
|| !WPACKET_start_sub_packet_u16(pkt)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||
return EXT_RETURN_FAIL;
|
||||
}
|
||||
|
||||
tls1_get_supported_groups(s, &pgroups, &num_groups);
|
||||
|
||||
/*
|
||||
* Make the number of key_shares sent configurable. For
|
||||
* now, we just send one
|
||||
*/
|
||||
if (s->s3.group_id != 0) {
|
||||
curve_id = s->s3.group_id;
|
||||
} else {
|
||||
for (i = 0; i < num_groups; i++) {
|
||||
if (!tls_group_allowed(s, pgroups[i], SSL_SECOP_CURVE_SUPPORTED))
|
||||
continue;
|
||||
|
||||
if (!tls_valid_group(s, pgroups[i], TLS1_3_VERSION, TLS1_3_VERSION,
|
||||
0, NULL))
|
||||
continue;
|
||||
|
||||
curve_id = pgroups[i];
|
||||
break;
|
||||
}
|
||||
tls1_get_requested_keyshare_groups(s, &pgroups, &num_groups);
|
||||
if (num_groups == 1 && pgroups[0] == 0) { /* Indication that no * prefix was used */
|
||||
tls1_get_supported_groups(s, &pgroups, &num_groups);
|
||||
add_only_one = 1;
|
||||
}
|
||||
|
||||
if (curve_id == 0) {
|
||||
/* If neither the default nor the keyshares have any entry --> fatal */
|
||||
if (num_groups == 0) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_KEY_SHARE);
|
||||
return EXT_RETURN_FAIL;
|
||||
}
|
||||
|
||||
if (!add_key_share(s, pkt, curve_id)) {
|
||||
/* SSLfatal() already called */
|
||||
return EXT_RETURN_FAIL;
|
||||
/* Add key shares */
|
||||
|
||||
s->s3.tmp.num_ks_pkey = 0;
|
||||
|
||||
if (s->s3.group_id != 0) {
|
||||
group_id = s->s3.group_id;
|
||||
if (!add_key_share(s, pkt, group_id, 0)) {
|
||||
/* SSLfatal() already called */
|
||||
return EXT_RETURN_FAIL;
|
||||
}
|
||||
} else {
|
||||
if (s->ext.supportedgroups == NULL) /* use default */
|
||||
add_only_one = 1;
|
||||
|
||||
for (i = 0; i < num_groups; i++) {
|
||||
if (!tls_group_allowed(s, pgroups[i], SSL_SECOP_CURVE_SUPPORTED))
|
||||
continue;
|
||||
if (!tls_valid_group(s, pgroups[i], TLS1_3_VERSION, TLS1_3_VERSION,
|
||||
0, NULL))
|
||||
continue;
|
||||
|
||||
group_id = pgroups[i];
|
||||
|
||||
if (group_id == 0) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_KEY_SHARE);
|
||||
return EXT_RETURN_FAIL;
|
||||
}
|
||||
if (!add_key_share(s, pkt, group_id, valid_keyshare)) {
|
||||
/* SSLfatal() already called */
|
||||
return EXT_RETURN_FAIL;
|
||||
}
|
||||
if (add_only_one)
|
||||
break;
|
||||
|
||||
valid_keyshare++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) {
|
||||
@ -1836,6 +1859,8 @@ int tls_parse_stoc_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
PACKET encoded_pt;
|
||||
EVP_PKEY *ckey = s->s3.tmp.pkey, *skey = NULL;
|
||||
const TLS_GROUP_INFO *ginf = NULL;
|
||||
uint16_t valid_ks_id = 0;
|
||||
size_t i;
|
||||
|
||||
/* Sanity check */
|
||||
if (ckey == NULL || s->s3.peer_tmp != NULL) {
|
||||
@ -1843,6 +1868,7 @@ int tls_parse_stoc_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Which group ID does the server want -> group_id */
|
||||
if (!PACKET_get_net_2(pkt, &group_id)) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
return 0;
|
||||
@ -1850,7 +1876,7 @@ int tls_parse_stoc_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
|
||||
if ((context & SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) != 0) {
|
||||
const uint16_t *pgroups = NULL;
|
||||
size_t i, num_groups;
|
||||
size_t num_groups;
|
||||
|
||||
if (PACKET_remaining(pkt) != 0) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
@ -1861,9 +1887,11 @@ int tls_parse_stoc_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
* It is an error if the HelloRetryRequest wants a key_share that we
|
||||
* already sent in the first ClientHello
|
||||
*/
|
||||
if (group_id == s->s3.group_id) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
|
||||
return 0;
|
||||
for (i = 0; i < s->s3.tmp.num_ks_pkey; i++) {
|
||||
if (s->s3.tmp.ks_group_id[i] == group_id) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate the selected group is one we support */
|
||||
@ -1880,13 +1908,36 @@ int tls_parse_stoc_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Memorize which groupID the server wants */
|
||||
s->s3.group_id = group_id;
|
||||
EVP_PKEY_free(s->s3.tmp.pkey);
|
||||
|
||||
/* The initial keyshares are obsolete now, hence free memory */
|
||||
for (i = 0; i < s->s3.tmp.num_ks_pkey; i++) {
|
||||
if (s->s3.tmp.ks_pkey[i] != NULL) {
|
||||
EVP_PKEY_free(s->s3.tmp.ks_pkey[i]);
|
||||
s->s3.tmp.ks_pkey[i] = NULL;
|
||||
}
|
||||
}
|
||||
s->s3.tmp.num_ks_pkey = 0;
|
||||
s->s3.tmp.pkey = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (group_id != s->s3.group_id) {
|
||||
/*
|
||||
* check that the group requested by the server is one we've
|
||||
* sent a key share for, and if so: memorize which one
|
||||
*/
|
||||
for (i = 0; i < s->s3.tmp.num_ks_pkey; i++) {
|
||||
if (s->s3.tmp.ks_group_id[i] == group_id) {
|
||||
valid_ks_id = group_id;
|
||||
ckey = s->s3.tmp.ks_pkey[i];
|
||||
s->s3.group_id = group_id;
|
||||
s->s3.tmp.pkey = ckey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid_ks_id == 0) {
|
||||
/*
|
||||
* This isn't for the group that we sent in the original
|
||||
* key_share!
|
||||
|
@ -599,19 +599,230 @@ int tls_parse_ctos_psk_kex_modes(SSL_CONNECTION *s, PACKET *pkt,
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a key_share extension received in the ClientHello. |pkt| contains
|
||||
* the raw PACKET data for the extension. Returns 1 on success or 0 on failure.
|
||||
* Use function tls_parse_ctos_key_share with helper functions extract_keyshares,
|
||||
* check_overlap and tls_accept_ksgroup to parse the key_share extension(s)
|
||||
* received in the ClientHello and to select the group used of the key exchange
|
||||
*/
|
||||
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
/*
|
||||
* Accept a key share group by setting the related variables in s->s3 and
|
||||
* by generating a pubkey for this group
|
||||
*/
|
||||
static int tls_accept_ksgroup(SSL_CONNECTION *s, uint16_t ksgroup, PACKET *encoded_pubkey)
|
||||
{
|
||||
/* Accept the key share group */
|
||||
s->s3.group_id = ksgroup;
|
||||
s->s3.group_id_candidate = ksgroup;
|
||||
/* Cache the selected group ID in the SSL_SESSION */
|
||||
s->session->kex_group = ksgroup;
|
||||
if ((s->s3.peer_tmp = ssl_generate_param_group(s, ksgroup)) == NULL) {
|
||||
SSLfatal(s,
|
||||
SSL_AD_INTERNAL_ERROR,
|
||||
SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS);
|
||||
return 0;
|
||||
}
|
||||
if (tls13_set_encoded_pub_key(s->s3.peer_tmp,
|
||||
PACKET_data(encoded_pubkey),
|
||||
PACKET_remaining(encoded_pubkey)) <= 0) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_ECPOINT);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# define GROUPLIST_INCREMENT 32 /* Memory allocation chunk size (nominally 64 Bytes chunks) */
|
||||
|
||||
typedef enum KS_EXTRACTION_RESULT {
|
||||
EXTRACTION_FAILURE,
|
||||
EXTRACTION_SUCCESS,
|
||||
EXTRACTION_SUCCESS_HRR
|
||||
} KS_EXTRACTION_RESULT;
|
||||
|
||||
static KS_EXTRACTION_RESULT extract_keyshares(SSL_CONNECTION *s, PACKET *key_share_list,
|
||||
const uint16_t *clntgroups, size_t clnt_num_groups,
|
||||
const uint16_t *srvrgroups, size_t srvr_num_groups,
|
||||
uint16_t **keyshares_arr, PACKET **encoded_pubkey_arr,
|
||||
size_t *keyshares_cnt, size_t *keyshares_max)
|
||||
{
|
||||
PACKET encoded_pubkey;
|
||||
size_t key_share_pos = 0;
|
||||
size_t previous_key_share_pos = 0;
|
||||
unsigned int group_id = 0;
|
||||
|
||||
/* Prepare memory to hold the extracted key share groups and related pubkeys */
|
||||
*keyshares_arr = OPENSSL_malloc(*keyshares_max * sizeof(**keyshares_arr));
|
||||
if (*keyshares_arr == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||
goto failure;
|
||||
}
|
||||
*encoded_pubkey_arr = OPENSSL_malloc(*keyshares_max * sizeof(**encoded_pubkey_arr));
|
||||
if (*encoded_pubkey_arr == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
while (PACKET_remaining(key_share_list) > 0) {
|
||||
/* Get the group_id for the current share and its encoded_pubkey */
|
||||
if (!PACKET_get_net_2(key_share_list, &group_id)
|
||||
|| !PACKET_get_length_prefixed_2(key_share_list, &encoded_pubkey)
|
||||
|| PACKET_remaining(&encoded_pubkey) == 0) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we sent an HRR then the key_share sent back MUST be for the group
|
||||
* we requested, and must be the only key_share sent.
|
||||
*/
|
||||
if (s->s3.group_id != 0
|
||||
&& (group_id != s->s3.group_id
|
||||
|| PACKET_remaining(key_share_list) != 0)) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if this share is in supported_groups sent from client
|
||||
* and that the key shares are in the same sequence as the supported_groups
|
||||
*/
|
||||
if (!check_in_list(s, group_id, clntgroups, clnt_num_groups, 0, &key_share_pos)
|
||||
|| key_share_pos < previous_key_share_pos) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
|
||||
goto failure;
|
||||
}
|
||||
previous_key_share_pos = key_share_pos;
|
||||
|
||||
if (s->s3.group_id != 0) {
|
||||
/*
|
||||
* We have sent a HRR, and the key share we got back is
|
||||
* the one we expected and is the only key share and is
|
||||
* in the list of supported_groups (checked
|
||||
* above already), hence we accept this key share group
|
||||
*/
|
||||
if (!tls_accept_ksgroup(s, s->s3.group_id, &encoded_pubkey))
|
||||
goto failure; /* SSLfatal already called */
|
||||
/* We have selected a key share group via HRR, hence we're done here */
|
||||
return EXTRACTION_SUCCESS_HRR;
|
||||
}
|
||||
|
||||
/*
|
||||
* We tolerate but ignore a group id that we don't think is
|
||||
* suitable for TLSv1.3 or which is not supported by the server
|
||||
*/
|
||||
if (!check_in_list(s, group_id, srvrgroups, srvr_num_groups, 1, NULL)
|
||||
|| !tls_group_allowed(s, group_id, SSL_SECOP_CURVE_SUPPORTED)
|
||||
|| !tls_valid_group(s, group_id, TLS1_3_VERSION, TLS1_3_VERSION,
|
||||
0, NULL)) {
|
||||
/* Share not suitable or not supported, check next share */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Memorize this key share group ID and its encoded point */
|
||||
(*keyshares_arr)[*keyshares_cnt] = group_id;
|
||||
(*encoded_pubkey_arr)[(*keyshares_cnt)++] = encoded_pubkey;
|
||||
|
||||
/*
|
||||
* Memory management (remark: While limiting the client to only allow
|
||||
* a maximum of OPENSSL_CLIENT_MAX_KEY_SHARES to be sent, the server can
|
||||
* handle any number of key shares)
|
||||
*/
|
||||
if (*keyshares_cnt == *keyshares_max) {
|
||||
PACKET *tmp_pkt;
|
||||
uint16_t *tmp =
|
||||
OPENSSL_realloc(*keyshares_arr,
|
||||
(*keyshares_max + GROUPLIST_INCREMENT) * sizeof(**keyshares_arr));
|
||||
|
||||
if (tmp == NULL)
|
||||
goto failure;
|
||||
*keyshares_arr = tmp;
|
||||
tmp_pkt =
|
||||
OPENSSL_realloc(*encoded_pubkey_arr,
|
||||
(*keyshares_max + GROUPLIST_INCREMENT) *
|
||||
sizeof(**encoded_pubkey_arr));
|
||||
if (tmp_pkt == NULL)
|
||||
goto failure;
|
||||
*encoded_pubkey_arr = tmp_pkt;
|
||||
*keyshares_max += GROUPLIST_INCREMENT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return EXTRACTION_SUCCESS;
|
||||
|
||||
failure:
|
||||
/* Fatal error -> free any allocated memory and return 0 */
|
||||
OPENSSL_free(*keyshares_arr);
|
||||
OPENSSL_free(*encoded_pubkey_arr);
|
||||
return EXTRACTION_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For each group in the priority list of groups, check if that group is
|
||||
* also present in the secondary list; if so, select the first overlap and
|
||||
* assign to selected_group and also set the related index in the candidate group list,
|
||||
* or set selected_group to 0 if no overlap
|
||||
*/
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
static void check_overlap(SSL_CONNECTION *s,
|
||||
const uint16_t *prio_groups, size_t prio_num_groups,
|
||||
const uint16_t *candidate_groups, size_t candidate_num_groups,
|
||||
int *prio_group_idx, int *candidate_group_idx,
|
||||
uint16_t *selected_group)
|
||||
{
|
||||
uint16_t current_group;
|
||||
size_t group_idx = prio_num_groups;
|
||||
size_t new_group_idx = 0;
|
||||
|
||||
*candidate_group_idx = 0;
|
||||
*prio_group_idx = 0;
|
||||
*selected_group = 0;
|
||||
|
||||
for (current_group = 0; current_group < candidate_num_groups; current_group++) {
|
||||
if (!check_in_list(s, candidate_groups[current_group], prio_groups,
|
||||
prio_num_groups, 1, &new_group_idx)
|
||||
|| !tls_group_allowed(s, candidate_groups[current_group],
|
||||
SSL_SECOP_CURVE_SUPPORTED)
|
||||
|| !tls_valid_group(s, candidate_groups[current_group], TLS1_3_VERSION,
|
||||
TLS1_3_VERSION, 0, NULL))
|
||||
/* No overlap or group not suitable, check next group */
|
||||
continue;
|
||||
|
||||
/*
|
||||
* is the found new_group_idx earlier in the priority list than
|
||||
* initial or last group_idx?
|
||||
*/
|
||||
if (new_group_idx < group_idx) {
|
||||
group_idx = new_group_idx;
|
||||
*candidate_group_idx = current_group;
|
||||
*prio_group_idx = group_idx;
|
||||
*selected_group = prio_groups[group_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int tls_parse_ctos_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
unsigned int context, X509 *x, size_t chainidx)
|
||||
{
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
unsigned int group_id;
|
||||
PACKET key_share_list, encoded_pt;
|
||||
PACKET key_share_list;
|
||||
const uint16_t *clntgroups, *srvrgroups;
|
||||
size_t clnt_num_groups, srvr_num_groups;
|
||||
int found = 0;
|
||||
const size_t *srvrtuples;
|
||||
uint16_t *first_group_in_tuple;
|
||||
size_t clnt_num_groups, srvr_num_groups, srvr_num_tuples;
|
||||
PACKET *encoded_pubkey_arr = NULL;
|
||||
uint16_t *keyshares_arr = NULL;
|
||||
size_t keyshares_cnt = 0;
|
||||
size_t keyshares_max = GROUPLIST_INCREMENT;
|
||||
/* We conservatively assume that we did not find a suitable group */
|
||||
uint16_t group_id_candidate = 0;
|
||||
KS_EXTRACTION_RESULT ks_extraction_result;
|
||||
size_t current_tuple;
|
||||
int ret = 0;
|
||||
|
||||
s->s3.group_id_candidate = 0;
|
||||
if (s->hit && (s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE) == 0)
|
||||
return 1;
|
||||
|
||||
@ -626,10 +837,12 @@ int tls_parse_ctos_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get our list of supported groups */
|
||||
/* Get list of server supported groups and the group tuples */
|
||||
tls1_get_supported_groups(s, &srvrgroups, &srvr_num_groups);
|
||||
tls1_get_group_tuples(s, &srvrtuples, &srvr_num_tuples);
|
||||
/* Get the clients list of supported groups. */
|
||||
tls1_get_peer_groups(s, &clntgroups, &clnt_num_groups);
|
||||
|
||||
if (clnt_num_groups == 0) {
|
||||
/*
|
||||
* This can only happen if the supported_groups extension was not sent,
|
||||
@ -651,70 +864,119 @@ int tls_parse_ctos_key_share(SSL_CONNECTION *s, PACKET *pkt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (PACKET_remaining(&key_share_list) > 0) {
|
||||
if (!PACKET_get_net_2(&key_share_list, &group_id)
|
||||
|| !PACKET_get_length_prefixed_2(&key_share_list, &encoded_pt)
|
||||
|| PACKET_remaining(&encoded_pt) == 0) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
|
||||
return 0;
|
||||
/* We parse the key share extension and memorize the entries (after some checks) */
|
||||
ks_extraction_result = extract_keyshares(s,
|
||||
&key_share_list,
|
||||
clntgroups, clnt_num_groups,
|
||||
srvrgroups, srvr_num_groups,
|
||||
&keyshares_arr, &encoded_pubkey_arr,
|
||||
&keyshares_cnt, &keyshares_max);
|
||||
|
||||
if (ks_extraction_result == EXTRACTION_FAILURE) /* Fatal error during tests */
|
||||
return 0; /* Memory already freed and SSLfatal already called */
|
||||
if (ks_extraction_result == EXTRACTION_SUCCESS_HRR) /* Successful HRR */
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* We now have the folowing lists available to make a decision for
|
||||
* which group the server should use for key exchange :
|
||||
* From client: clntgroups[clnt_num_groups],
|
||||
* keyshares_arr[keyshares_cnt], encoded_pubkey_arr[keyshares_cnt]
|
||||
* From server: srvrgroups[srvr_num_groups], srvrtuples[srvr_num_tuples]
|
||||
*
|
||||
* Group selection algorithm:
|
||||
* For all tuples do:
|
||||
* key share group(s) overlapping with current tuple?
|
||||
* --> Yes: accept group_id for SH
|
||||
* --> No: is any of the client supported_groups overlapping with current tuple?
|
||||
* --> Yes: memorize group_id for HRR, break
|
||||
* --> No: continue to check next tuple
|
||||
*
|
||||
* Remark: Selection priority different for client- or server-preference
|
||||
*/
|
||||
first_group_in_tuple = (uint16_t *)srvrgroups;
|
||||
for (current_tuple = 0; current_tuple < srvr_num_tuples; current_tuple++) {
|
||||
size_t number_of_groups_in_tuple = srvrtuples[current_tuple];
|
||||
int prio_group_idx = 0, candidate_group_idx = 0;
|
||||
|
||||
/* Server or client preference ? */
|
||||
if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
|
||||
/* Server preference */
|
||||
/* Is there overlap with a key share group? */
|
||||
check_overlap(s,
|
||||
first_group_in_tuple, number_of_groups_in_tuple,
|
||||
keyshares_arr, keyshares_cnt,
|
||||
&prio_group_idx, &candidate_group_idx,
|
||||
&group_id_candidate);
|
||||
if (group_id_candidate > 0) { /* Overlap found -> accept the key share group */
|
||||
if (!tls_accept_ksgroup(s, group_id_candidate,
|
||||
&encoded_pubkey_arr[candidate_group_idx]))
|
||||
goto err; /* SSLfatal already called */
|
||||
/* We have all info for a SH, hence we're done here */
|
||||
goto end;
|
||||
} else {
|
||||
/*
|
||||
* There's no overlap with a key share, but is there at least a client
|
||||
* supported_group overlapping with the current tuple?
|
||||
*/
|
||||
check_overlap(s,
|
||||
first_group_in_tuple, number_of_groups_in_tuple,
|
||||
clntgroups, clnt_num_groups,
|
||||
&prio_group_idx, &candidate_group_idx,
|
||||
&group_id_candidate);
|
||||
if (group_id_candidate > 0) {
|
||||
/*
|
||||
* We did not have a key share overlap, but at least the supported
|
||||
* groups overlap hence we can stop searching
|
||||
* (and report group_id_candidate 'upward' for HRR)
|
||||
*/
|
||||
s->s3.group_id_candidate = group_id_candidate;
|
||||
goto end;
|
||||
} else {
|
||||
/*
|
||||
* Neither key share nor supported_groups overlap current
|
||||
* tuple, hence we try the next tuple
|
||||
*/
|
||||
first_group_in_tuple = &first_group_in_tuple[number_of_groups_in_tuple];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
} else { /* We have client preference */
|
||||
check_overlap(s,
|
||||
keyshares_arr, keyshares_cnt,
|
||||
first_group_in_tuple, number_of_groups_in_tuple,
|
||||
&prio_group_idx, &candidate_group_idx,
|
||||
&group_id_candidate);
|
||||
if (group_id_candidate > 0) {
|
||||
if (!tls_accept_ksgroup(s, group_id_candidate, &encoded_pubkey_arr[prio_group_idx]))
|
||||
goto err;
|
||||
goto end;
|
||||
} else {
|
||||
check_overlap(s,
|
||||
clntgroups, clnt_num_groups,
|
||||
first_group_in_tuple, number_of_groups_in_tuple,
|
||||
&prio_group_idx, &candidate_group_idx,
|
||||
&group_id_candidate);
|
||||
if (group_id_candidate > 0) {
|
||||
s->s3.group_id_candidate = group_id_candidate;
|
||||
goto end;
|
||||
} else {
|
||||
first_group_in_tuple = &first_group_in_tuple[number_of_groups_in_tuple];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we already found a suitable key_share we loop through the
|
||||
* rest to verify the structure, but don't process them.
|
||||
*/
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If we sent an HRR then the key_share sent back MUST be for the group
|
||||
* we requested, and must be the only key_share sent.
|
||||
*/
|
||||
if (s->s3.group_id != 0
|
||||
&& (group_id != s->s3.group_id
|
||||
|| PACKET_remaining(&key_share_list) != 0)) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if this share is in supported_groups sent from client */
|
||||
if (!check_in_list(s, group_id, clntgroups, clnt_num_groups, 0)) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_KEY_SHARE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if this share is for a group we can use */
|
||||
if (!check_in_list(s, group_id, srvrgroups, srvr_num_groups, 1)
|
||||
|| !tls_group_allowed(s, group_id, SSL_SECOP_CURVE_SUPPORTED)
|
||||
/*
|
||||
* We tolerate but ignore a group id that we don't think is
|
||||
* suitable for TLSv1.3
|
||||
*/
|
||||
|| !tls_valid_group(s, group_id, TLS1_3_VERSION, TLS1_3_VERSION,
|
||||
0, NULL)) {
|
||||
/* Share not suitable */
|
||||
continue;
|
||||
}
|
||||
|
||||
s->s3.group_id = group_id;
|
||||
/* Cache the selected group ID in the SSL_SESSION */
|
||||
s->session->kex_group = group_id;
|
||||
|
||||
if ((s->s3.peer_tmp = ssl_generate_param_group(s, group_id)) == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tls13_set_encoded_pub_key(s->s3.peer_tmp,
|
||||
PACKET_data(&encoded_pt),
|
||||
PACKET_remaining(&encoded_pt)) <= 0) {
|
||||
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_ECPOINT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
}
|
||||
|
||||
end:
|
||||
ret = 1;
|
||||
|
||||
err:
|
||||
OPENSSL_free(keyshares_arr);
|
||||
OPENSSL_free(encoded_pubkey_arr);
|
||||
return ret;
|
||||
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
@ -1641,8 +1903,8 @@ EXT_RETURN tls_construct_stoc_key_share(SSL_CONNECTION *s, WPACKET *pkt,
|
||||
size_t chainidx)
|
||||
{
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
unsigned char *encodedPoint;
|
||||
size_t encoded_pt_len = 0;
|
||||
unsigned char *encoded_pubkey;
|
||||
size_t encoded_pubkey_len = 0;
|
||||
EVP_PKEY *ckey = s->s3.peer_tmp, *skey = NULL;
|
||||
const TLS_GROUP_INFO *ginf = NULL;
|
||||
|
||||
@ -1703,21 +1965,21 @@ EXT_RETURN tls_construct_stoc_key_share(SSL_CONNECTION *s, WPACKET *pkt,
|
||||
}
|
||||
|
||||
/* Generate encoding of server key */
|
||||
encoded_pt_len = EVP_PKEY_get1_encoded_public_key(skey, &encodedPoint);
|
||||
if (encoded_pt_len == 0) {
|
||||
encoded_pubkey_len = EVP_PKEY_get1_encoded_public_key(skey, &encoded_pubkey);
|
||||
if (encoded_pubkey_len == 0) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EC_LIB);
|
||||
EVP_PKEY_free(skey);
|
||||
return EXT_RETURN_FAIL;
|
||||
}
|
||||
|
||||
if (!WPACKET_sub_memcpy_u16(pkt, encodedPoint, encoded_pt_len)
|
||||
if (!WPACKET_sub_memcpy_u16(pkt, encoded_pubkey, encoded_pubkey_len)
|
||||
|| !WPACKET_close(pkt)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||
EVP_PKEY_free(skey);
|
||||
OPENSSL_free(encodedPoint);
|
||||
OPENSSL_free(encoded_pubkey);
|
||||
return EXT_RETURN_FAIL;
|
||||
}
|
||||
OPENSSL_free(encodedPoint);
|
||||
OPENSSL_free(encoded_pubkey);
|
||||
|
||||
/*
|
||||
* This causes the crypto state to be updated based on the derived keys
|
||||
|
@ -2575,10 +2575,11 @@ int ssl_set_client_hello_version(SSL_CONNECTION *s)
|
||||
* Checks a list of |groups| to determine if the |group_id| is in it. If it is
|
||||
* and |checkallow| is 1 then additionally check if the group is allowed to be
|
||||
* used. Returns 1 if the group is in the list (and allowed if |checkallow| is
|
||||
* 1) or 0 otherwise.
|
||||
* 1) or 0 otherwise. If provided a pointer it will also return the position
|
||||
* where the group was found.
|
||||
*/
|
||||
int check_in_list(SSL_CONNECTION *s, uint16_t group_id, const uint16_t *groups,
|
||||
size_t num_groups, int checkallow)
|
||||
size_t num_groups, int checkallow, size_t *pos)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
@ -2591,6 +2592,8 @@ int check_in_list(SSL_CONNECTION *s, uint16_t group_id, const uint16_t *groups,
|
||||
if (group_id == group
|
||||
&& (!checkallow
|
||||
|| tls_group_allowed(s, group, SSL_SECOP_CURVE_CHECK))) {
|
||||
if (pos != NULL)
|
||||
*pos = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ typedef CON_FUNC_RETURN (*confunc_f) (SSL_CONNECTION *s, WPACKET *pkt);
|
||||
|
||||
int ssl3_take_mac(SSL_CONNECTION *s);
|
||||
int check_in_list(SSL_CONNECTION *s, uint16_t group_id, const uint16_t *groups,
|
||||
size_t num_groups, int checkallow);
|
||||
size_t num_groups, int checkallow, size_t *pos);
|
||||
int create_synthetic_message_hash(SSL_CONNECTION *s,
|
||||
const unsigned char *hashval,
|
||||
size_t hashlen, const unsigned char *hrr,
|
||||
|
674
ssl/t1_lib.c
674
ssl/t1_lib.c
@ -218,11 +218,19 @@ static const uint16_t supported_groups_default[] = {
|
||||
OSSL_TLS_GROUP_ID_ffdhe8192, /* ffdhe8192 (0x104) */
|
||||
};
|
||||
|
||||
/* Group list string of the built-in pseudo group DEFAULT */
|
||||
#define DEFAULT_GROUP_NAME "DEFAULT"
|
||||
#define DEFAULT_GROUP_LIST "X25519:secp256r1:X448:secp521r1:secp384r1:GC256A:GC256B:GC256C:GC256D:GC512A:GC512B:GC512C:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192",
|
||||
|
||||
static const uint16_t suiteb_curves[] = {
|
||||
OSSL_TLS_GROUP_ID_secp256r1,
|
||||
OSSL_TLS_GROUP_ID_secp384r1,
|
||||
};
|
||||
|
||||
/* Group list string of the built-in pseudo group DEFAULT_SUITE_B */
|
||||
#define SUITE_B_GROUP_NAME "DEFAULT_SUITE_B"
|
||||
#define SUITE_B_GROUP_LIST "secp256r1:secp384r1",
|
||||
|
||||
struct provider_ctx_data_st {
|
||||
SSL_CTX *ctx;
|
||||
OSSL_PROVIDER *provider;
|
||||
@ -415,6 +423,25 @@ int ssl_load_groups(SSL_CTX *ctx)
|
||||
num_deflt_grps * sizeof(tmp_supp_groups[0]));
|
||||
ctx->ext.supported_groups_default_len = num_deflt_grps;
|
||||
|
||||
/*
|
||||
* Default groups have no explicit key share nor a tuple,
|
||||
* hence we'll generate a key share for the first group and
|
||||
* define one big tuple consisting of all default groups
|
||||
*/
|
||||
if (ctx->ext.keyshares == NULL)
|
||||
ctx->ext.keyshares = OPENSSL_malloc(sizeof(*ctx->ext.keyshares));
|
||||
if (ctx->ext.keyshares == NULL)
|
||||
return 0;
|
||||
ctx->ext.keyshares_len = 1;
|
||||
ctx->ext.keyshares[0] = 0;
|
||||
|
||||
if (ctx->ext.tuples == NULL)
|
||||
ctx->ext.tuples = OPENSSL_malloc(sizeof(*ctx->ext.tuples));
|
||||
if (ctx->ext.tuples == NULL)
|
||||
return 0;
|
||||
ctx->ext.tuples_len = 1;
|
||||
ctx->ext.tuples[0] = ctx->ext.supported_groups_default_len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -843,6 +870,47 @@ void tls1_get_supported_groups(SSL_CONNECTION *s, const uint16_t **pgroups,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some comments for the function below:
|
||||
* s->ext.supportedgroups == NULL means legacy syntax (no [*,/,-]) from built-in group array.
|
||||
* In this case, we need to send exactly one key share, which MUST be the first (leftmost)
|
||||
* eligible group from the legacy list. Therefore, we provide the entire list of supported
|
||||
* groups in this case.
|
||||
*
|
||||
* A 'flag' to indicate legacy syntax is created by setting the number of key shares to 1,
|
||||
* but the groupID to 0.
|
||||
* The 'flag' is checked right at the beginning in tls_construct_ctos_key_share and either
|
||||
* the "list of requested key share groups" is used, or the "list of supported groups" in
|
||||
* combination with setting add_only_one = 1 is applied.
|
||||
*/
|
||||
void tls1_get_requested_keyshare_groups(SSL_CONNECTION *s, const uint16_t **pgroups,
|
||||
size_t *pgroupslen)
|
||||
{
|
||||
SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
|
||||
|
||||
if (s->ext.supportedgroups == NULL) {
|
||||
*pgroups = sctx->ext.supported_groups_default;
|
||||
*pgroupslen = sctx->ext.supported_groups_default_len;
|
||||
} else {
|
||||
*pgroups = s->ext.keyshares;
|
||||
*pgroupslen = s->ext.keyshares_len;
|
||||
}
|
||||
}
|
||||
|
||||
void tls1_get_group_tuples(SSL_CONNECTION *s, const size_t **ptuples,
|
||||
size_t *ptupleslen)
|
||||
{
|
||||
SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
|
||||
|
||||
if (s->ext.supportedgroups == NULL) {
|
||||
*ptuples = sctx->ext.tuples;
|
||||
*ptupleslen = sctx->ext.tuples_len;
|
||||
} else {
|
||||
*ptuples = s->ext.tuples;
|
||||
*ptupleslen = s->ext.tuples_len;
|
||||
}
|
||||
}
|
||||
|
||||
int tls_valid_group(SSL_CONNECTION *s, uint16_t group_id,
|
||||
int minversion, int maxversion,
|
||||
int isec, int *okfortls13)
|
||||
@ -989,10 +1057,13 @@ uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tls1_set_groups(uint16_t **pext, size_t *pextlen,
|
||||
int tls1_set_groups(uint16_t **grpext, size_t *grpextlen,
|
||||
uint16_t **ksext, size_t *ksextlen,
|
||||
size_t **tplext, size_t *tplextlen,
|
||||
int *groups, size_t ngroups)
|
||||
{
|
||||
uint16_t *glist;
|
||||
uint16_t *glist = NULL, *kslist = NULL;
|
||||
size_t *tpllist = NULL;
|
||||
size_t i;
|
||||
/*
|
||||
* Bitmap of groups included to detect duplicates: two variables are added
|
||||
@ -1007,7 +1078,11 @@ int tls1_set_groups(uint16_t **pext, size_t *pextlen,
|
||||
return 0;
|
||||
}
|
||||
if ((glist = OPENSSL_malloc(ngroups * sizeof(*glist))) == NULL)
|
||||
return 0;
|
||||
goto err;
|
||||
if ((kslist = OPENSSL_malloc(1 * sizeof(*kslist))) == NULL)
|
||||
goto err;
|
||||
if ((tpllist = OPENSSL_malloc(1 * sizeof(*tpllist))) == NULL)
|
||||
goto err;
|
||||
for (i = 0; i < ngroups; i++) {
|
||||
unsigned long idmask;
|
||||
uint16_t id;
|
||||
@ -1021,167 +1096,596 @@ int tls1_set_groups(uint16_t **pext, size_t *pextlen,
|
||||
*dup_list |= idmask;
|
||||
glist[i] = id;
|
||||
}
|
||||
OPENSSL_free(*pext);
|
||||
*pext = glist;
|
||||
*pextlen = ngroups;
|
||||
OPENSSL_free(*grpext);
|
||||
OPENSSL_free(*ksext);
|
||||
OPENSSL_free(*tplext);
|
||||
*grpext = glist;
|
||||
*grpextlen = ngroups;
|
||||
kslist[0] = glist[0];
|
||||
*ksext = kslist;
|
||||
*ksextlen = 1;
|
||||
tpllist[0] = ngroups;
|
||||
*tplext = tpllist;
|
||||
*tplextlen = 1;
|
||||
return 1;
|
||||
err:
|
||||
OPENSSL_free(glist);
|
||||
OPENSSL_free(kslist);
|
||||
OPENSSL_free(tpllist);
|
||||
return 0;
|
||||
}
|
||||
|
||||
# define GROUPLIST_INCREMENT 40
|
||||
# define GROUP_NAME_BUFFER_LENGTH 64
|
||||
/*
|
||||
* Definition of DEFAULT[_XYZ] pseudo group names.
|
||||
* A pseudo group name is actually a full list of groups, including prefixes
|
||||
* and or tuple delimiters. It can be hierarchically defined (for potential future use).
|
||||
* IMPORTANT REMARK: For ease of use, in the built-in lists of groups, unknown groups or
|
||||
* groups not backed by a provider will always silently be ignored, even without '?' prefix
|
||||
*/
|
||||
typedef struct {
|
||||
const char *list_name; /* The name of this pseudo group */
|
||||
const char *group_string; /* The group string of this pseudo group */
|
||||
} default_group_string_st; /* (can include '?', '*'. '-', '/' as needed) */
|
||||
|
||||
/* Built-in pseudo group-names must start with a (D or d) */
|
||||
static const char *DEFAULT_GROUPNAME_FIRST_CHARACTER = "D";
|
||||
|
||||
/* The list of all built-in pseudo-group-name structures */
|
||||
static const default_group_string_st default_group_strings[] = {
|
||||
{DEFAULT_GROUP_NAME, DEFAULT_GROUP_LIST},
|
||||
{SUITE_B_GROUP_NAME, SUITE_B_GROUP_LIST}
|
||||
};
|
||||
|
||||
/*
|
||||
* Some GOST names are not resolved by tls1_group_name2id,
|
||||
* hence we'll check for those manually
|
||||
*/
|
||||
typedef struct {
|
||||
const char *group_name;
|
||||
uint16_t groupID;
|
||||
} name2id_st;
|
||||
static const name2id_st name2id_arr[] = {
|
||||
{"GC256A", OSSL_TLS_GROUP_ID_gc256A },
|
||||
{"GC256B", OSSL_TLS_GROUP_ID_gc256B },
|
||||
{"GC256C", OSSL_TLS_GROUP_ID_gc256C },
|
||||
{"GC256D", OSSL_TLS_GROUP_ID_gc256D },
|
||||
{"GC512A", OSSL_TLS_GROUP_ID_gc512A },
|
||||
{"GC512B", OSSL_TLS_GROUP_ID_gc512B },
|
||||
{"GC512C", OSSL_TLS_GROUP_ID_gc512C },
|
||||
};
|
||||
|
||||
/*
|
||||
* Group list management:
|
||||
* We establish three lists along with their related size counters:
|
||||
* 1) List of (unique) groups
|
||||
* 2) List of number of groups per group-priority-tuple
|
||||
* 3) List of (unique) key share groups
|
||||
*/
|
||||
#define GROUPLIST_INCREMENT 32 /* Memory allocation chunk size (64 Bytes chunks ~= cache line) */
|
||||
#define GROUP_NAME_BUFFER_LENGTH 64 /* Max length of a group name */
|
||||
|
||||
/*
|
||||
* Preparation of the prefix used to indicate the desire to send a key share,
|
||||
* the characters used as separators between groups or tuples of groups, the
|
||||
* character to indicate that an unknown group should be ignored, and the
|
||||
* character to indicate that a group should be deleted from a list
|
||||
*/
|
||||
#ifndef TUPLE_DELIMITER_CHARACTER
|
||||
/* The prefix characters to indicate group tuple boundaries */
|
||||
# define TUPLE_DELIMITER_CHARACTER '/'
|
||||
#endif
|
||||
#ifndef GROUP_DELIMITER_CHARACTER
|
||||
/* The prefix characters to indicate group tuple boundaries */
|
||||
# define GROUP_DELIMITER_CHARACTER ':'
|
||||
#endif
|
||||
#ifndef IGNORE_UNKNOWN_GROUP_CHARACTER
|
||||
/* The prefix character to ignore unknown groups */
|
||||
# define IGNORE_UNKNOWN_GROUP_CHARACTER '?'
|
||||
#endif
|
||||
#ifndef KEY_SHARE_INDICATOR_CHARACTER
|
||||
/* The prefix character to trigger a key share addition */
|
||||
# define KEY_SHARE_INDICATOR_CHARACTER '*'
|
||||
#endif
|
||||
#ifndef REMOVE_GROUP_INDICATOR_CHARACTER
|
||||
/* The prefix character to trigger a key share removal */
|
||||
# define REMOVE_GROUP_INDICATOR_CHARACTER '-'
|
||||
#endif
|
||||
static const char prefixes[] = {TUPLE_DELIMITER_CHARACTER,
|
||||
GROUP_DELIMITER_CHARACTER,
|
||||
IGNORE_UNKNOWN_GROUP_CHARACTER,
|
||||
KEY_SHARE_INDICATOR_CHARACTER,
|
||||
REMOVE_GROUP_INDICATOR_CHARACTER,
|
||||
'\0'};
|
||||
|
||||
/*
|
||||
* High-level description of how group strings are analyzed:
|
||||
* A first call back function (tuple_cb) is used to process group tuples, and a
|
||||
* second callback function (gid_cb) is used to process the groups inside a tuple.
|
||||
* Those callback functions are (indirectly) called by CONF_parse_list with
|
||||
* different separators (nominally ':' or '/'), a variable based on gid_cb_st
|
||||
* is used to keep track of the parsing results between the various calls
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
SSL_CTX *ctx;
|
||||
size_t gidcnt;
|
||||
size_t gidmax;
|
||||
uint16_t *gid_arr;
|
||||
/* Variables to hold the three lists (groups, requested keyshares, tuple structure) */
|
||||
size_t gidmax; /* The memory allocation chunk size for the group IDs */
|
||||
size_t gidcnt; /* Number of groups */
|
||||
uint16_t *gid_arr; /* The IDs of the supported groups (flat list) */
|
||||
size_t tplmax; /* The memory allocation chunk size for the tuple counters */
|
||||
size_t tplcnt; /* Number of tuples */
|
||||
size_t *tuplcnt_arr; /* The number of groups inside a tuple */
|
||||
size_t ksidmax; /* The memory allocation chunk size */
|
||||
size_t ksidcnt; /* Number of key shares */
|
||||
uint16_t *ksid_arr; /* The IDs of the key share groups (flat list) */
|
||||
/* Variable to keep state between execution of callback or helper functions */
|
||||
size_t tuple_mode; /* Keeps track whether tuple_cb called from 'the top' or from gid_cb */
|
||||
int ignore_unknown_default; /* Flag such that unknown groups for DEFAULT[_XYZ] are ignored */
|
||||
} gid_cb_st;
|
||||
|
||||
/* Forward declaration of tuple callback function */
|
||||
static int tuple_cb(const char *tuple, int len, void *arg);
|
||||
|
||||
/*
|
||||
* Extract and process the individual groups (and their prefixes if present)
|
||||
* present in a tuple. Note: The argument 'elem' is a NON-\0-terminated string
|
||||
* and must be appended by a \0 if used as \0-terminated string
|
||||
*/
|
||||
static int gid_cb(const char *elem, int len, void *arg)
|
||||
{
|
||||
gid_cb_st *garg = arg;
|
||||
size_t i;
|
||||
size_t i, j, k;
|
||||
uint16_t gid = 0;
|
||||
char etmp[GROUP_NAME_BUFFER_LENGTH];
|
||||
int ignore_unknown = 0;
|
||||
int remove_group = 0;
|
||||
int found_group = 0;
|
||||
int add_default_groups = 0;
|
||||
size_t groups_to_add = 0;
|
||||
char etmp[GROUP_NAME_BUFFER_LENGTH];
|
||||
int retval = 1; /* We assume success */
|
||||
char *current_prefix;
|
||||
int ignore_unknown = 0;
|
||||
int add_keyshare = 0;
|
||||
int remove_group = 0;
|
||||
size_t restored_prefix_index = 0;
|
||||
char *restored_default_group_string;
|
||||
int continue_while_loop = 1;
|
||||
|
||||
if (elem == NULL)
|
||||
/* Sanity checks */
|
||||
if (garg == NULL || elem == NULL || len <= 0) {
|
||||
ERR_raise(ERR_LIB_CONF, CONF_R_VARIABLE_HAS_NO_VALUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (((elem[0] == '-' && !remove_group) || (elem[0] == '?' && !ignore_unknown))
|
||||
&& len > 0) {
|
||||
if (elem[0] == '-') {
|
||||
/* Check the possible prefixes (remark: Leading and trailing spaces already cleared) */
|
||||
while (continue_while_loop && len > 0
|
||||
&& ((current_prefix = strchr(prefixes, elem[0])) != NULL
|
||||
|| OPENSSL_strncasecmp(current_prefix = (char *)DEFAULT_GROUPNAME_FIRST_CHARACTER, elem, 1) == 0)) {
|
||||
|
||||
switch (*current_prefix) {
|
||||
case TUPLE_DELIMITER_CHARACTER:
|
||||
/* tuple delimiter not allowed here -> syntax error */
|
||||
return -1;
|
||||
break;
|
||||
case GROUP_DELIMITER_CHARACTER:
|
||||
return -1; /* Not a valid prefix for a single group name-> syntax error */
|
||||
break;
|
||||
case KEY_SHARE_INDICATOR_CHARACTER:
|
||||
if (add_keyshare)
|
||||
return -1; /* Only single key share prefix allowed -> syntax error */
|
||||
add_keyshare = 1;
|
||||
++elem;
|
||||
--len;
|
||||
break;
|
||||
case REMOVE_GROUP_INDICATOR_CHARACTER:
|
||||
if (remove_group)
|
||||
return -1; /* Only single remove group prefix allowed -> syntax error */
|
||||
remove_group = 1;
|
||||
++elem;
|
||||
--len;
|
||||
}
|
||||
if (elem[0] == '?') {
|
||||
break;
|
||||
case IGNORE_UNKNOWN_GROUP_CHARACTER:
|
||||
if (ignore_unknown)
|
||||
return -1; /* Only single ? allowed -> syntax error */
|
||||
ignore_unknown = 1;
|
||||
++elem;
|
||||
--len;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Check whether a DEFAULT[_XYZ] 'pseudo group' (= a built-in
|
||||
* list of groups) should be added
|
||||
*/
|
||||
for (i = 0; i < OSSL_NELEM(default_group_strings); i++) {
|
||||
if ((size_t)len == (strlen(default_group_strings[i].list_name))
|
||||
&& OPENSSL_strncasecmp(default_group_strings[i].list_name, elem, len) == 0) {
|
||||
/*
|
||||
* We're asked to insert an entire list of groups from a
|
||||
* DEFAULT[_XYZ] 'pseudo group' which we do by
|
||||
* recursively calling this function (indirectly via
|
||||
* CONF_parse_list and tuple_cb); essentially, we treat a DEFAULT
|
||||
* group string like a tuple which is appended to the current tuple
|
||||
* rather then starting a new tuple. Variable tuple_mode is the flag which
|
||||
* controls append tuple vs start new tuple.
|
||||
*/
|
||||
|
||||
if (ignore_unknown || remove_group)
|
||||
return -1; /* removal or ignore not allowed here -> syntax error */
|
||||
|
||||
/*
|
||||
* First, we restore any keyshare prefix in a new zero-terminated string
|
||||
* (if not already present)
|
||||
*/
|
||||
restored_default_group_string = OPENSSL_malloc((1 /* max prefix length */ +
|
||||
strlen(default_group_strings[i].group_string) +
|
||||
1 /* \0 */) * sizeof(char));
|
||||
if (restored_default_group_string == NULL)
|
||||
return 0;
|
||||
if (add_keyshare
|
||||
/* Remark: we tolerate a duplicated keyshare indicator here */
|
||||
&& default_group_strings[i].group_string[0]
|
||||
!= KEY_SHARE_INDICATOR_CHARACTER)
|
||||
restored_default_group_string[restored_prefix_index++] =
|
||||
KEY_SHARE_INDICATOR_CHARACTER;
|
||||
|
||||
memcpy(restored_default_group_string + restored_prefix_index,
|
||||
default_group_strings[i].group_string,
|
||||
strlen(default_group_strings[i].group_string));
|
||||
restored_default_group_string[strlen(default_group_strings[i].group_string) +
|
||||
restored_prefix_index] = '\0';
|
||||
/* We execute the recursive call */
|
||||
garg->ignore_unknown_default = 1; /* We ignore unknown groups for DEFAULT_XYZ */
|
||||
/* we enforce group mode (= append tuple) for DEFAULT_XYZ group lists */
|
||||
garg->tuple_mode = 0;
|
||||
/* We use the tuple_cb callback to process the pseudo group tuple */
|
||||
retval = CONF_parse_list(restored_default_group_string,
|
||||
TUPLE_DELIMITER_CHARACTER, 1, tuple_cb, garg);
|
||||
garg->tuple_mode = 1; /* next call to tuple_cb will again start new tuple */
|
||||
garg->ignore_unknown_default = 0; /* reset to original value */
|
||||
/* We don't need the \0-terminated string anymore */
|
||||
OPENSSL_free(restored_default_group_string);
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we reached this point, a group name started with a 'd' or 'D', but no request
|
||||
* for a DEFAULT[_XYZ] 'pseudo group' was detected, hence processing of the group
|
||||
* name can continue as usual (= the while loop checking prefixes can end)
|
||||
*/
|
||||
continue_while_loop = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == strlen("DEFAULT") && OPENSSL_strncasecmp("DEFAULT", elem, len) == 0)
|
||||
add_default_groups = 1;
|
||||
if (len == 0)
|
||||
return -1; /* Seems we have prefxes without a group name -> syntax error */
|
||||
|
||||
if (add_default_groups)
|
||||
groups_to_add = garg->ctx->ext.supported_groups_default_len;
|
||||
else if (!remove_group)
|
||||
groups_to_add = 1;
|
||||
if (garg->ignore_unknown_default == 1) /* Always ignore unknown groups for DEFAULT[_XYZ] */
|
||||
ignore_unknown = 1;
|
||||
|
||||
if (groups_to_add > garg->gidmax - garg->gidcnt) {
|
||||
size_t list_increment = groups_to_add > GROUPLIST_INCREMENT ? groups_to_add
|
||||
: GROUPLIST_INCREMENT;
|
||||
/* Memory management in case more groups are present compared to initial allocation */
|
||||
if (garg->gidcnt == garg->gidmax) {
|
||||
uint16_t *tmp =
|
||||
OPENSSL_realloc(garg->gid_arr,
|
||||
(garg->gidmax + list_increment) * sizeof(*garg->gid_arr));
|
||||
(garg->gidmax + GROUPLIST_INCREMENT) * sizeof(*garg->gid_arr));
|
||||
|
||||
if (tmp == NULL)
|
||||
return 0;
|
||||
|
||||
garg->gidmax += list_increment;
|
||||
garg->gidmax += GROUPLIST_INCREMENT;
|
||||
garg->gid_arr = tmp;
|
||||
}
|
||||
/* Memory management for key share groups */
|
||||
if (garg->ksidcnt == garg->ksidmax) {
|
||||
uint16_t *tmp =
|
||||
OPENSSL_realloc(garg->ksid_arr,
|
||||
(garg->ksidmax + GROUPLIST_INCREMENT) * sizeof(*garg->ksid_arr));
|
||||
|
||||
if (add_default_groups) {
|
||||
size_t j;
|
||||
|
||||
for (j = 0; j < garg->ctx->ext.supported_groups_default_len; j++) {
|
||||
gid = garg->ctx->ext.supported_groups_default[j];
|
||||
found_group = 0;
|
||||
|
||||
for (i = 0; i < garg->gidcnt; i++) {
|
||||
if (garg->gid_arr[i] == gid) {
|
||||
found_group = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_group)
|
||||
garg->gid_arr[garg->gidcnt++] = gid;
|
||||
}
|
||||
|
||||
return 1;
|
||||
if (tmp == NULL)
|
||||
return 0;
|
||||
garg->ksidmax += GROUPLIST_INCREMENT;
|
||||
garg->ksid_arr = tmp;
|
||||
}
|
||||
|
||||
if (len > (int) (sizeof(etmp) - 1))
|
||||
return 0;
|
||||
if (len > (int)(sizeof(etmp) - 1))
|
||||
return -1; /* group name to long -> syntax error */
|
||||
|
||||
/*
|
||||
* Prepare addition or removal of a single group by converting
|
||||
* a group name into its groupID equivalent
|
||||
*/
|
||||
|
||||
/* Create a \0-terminated string and get the gid for this group if possible */
|
||||
memcpy(etmp, elem, len);
|
||||
etmp[len] = 0;
|
||||
|
||||
/* Get the groupID */
|
||||
gid = tls1_group_name2id(garg->ctx, etmp);
|
||||
/*
|
||||
* Handle the case where no valid groupID was returned
|
||||
* e.g. for an unknown group, which we'd ignore (only) if relevant prefix was set
|
||||
*/
|
||||
if (gid == 0) {
|
||||
/* Unknown group - ignore, if ignore_unknown */
|
||||
return ignore_unknown;
|
||||
/* Is it one of the GOST groups ? */
|
||||
for (i = 0; i < OSSL_NELEM(name2id_arr); i++) {
|
||||
if (strcmp(etmp, name2id_arr[i].group_name) == 0) {
|
||||
gid = name2id_arr[i].groupID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gid == 0) { /* still not found */
|
||||
/* Unknown group - ignore if ignore_unknown; trigger error otherwise */
|
||||
retval = ignore_unknown;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < garg->gidcnt; i++)
|
||||
if (garg->gid_arr[i] == gid) {
|
||||
/* Make sure that at least one provider is supporting this groupID */
|
||||
found_group = 0;
|
||||
for (j = 0; j < garg->ctx->group_list_len; j++)
|
||||
if (garg->ctx->group_list[j].group_id == gid) {
|
||||
found_group = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found_group && remove_group) {
|
||||
size_t j;
|
||||
/*
|
||||
* No provider supports this group - ignore if
|
||||
* ignore_unknown; trigger error otherwise
|
||||
*/
|
||||
if (found_group == 0) {
|
||||
retval = ignore_unknown;
|
||||
goto done;
|
||||
}
|
||||
/* Remove group (and keyshare) from anywhere in the list if present, ignore if not present */
|
||||
if (remove_group) {
|
||||
/* Is the current group specified anywhere in the entire list so far? */
|
||||
found_group = 0;
|
||||
for (i = 0; i < garg->gidcnt; i++)
|
||||
if (garg->gid_arr[i] == gid) {
|
||||
found_group = 1;
|
||||
break;
|
||||
}
|
||||
/* The group to remove is at position i in the list of (zero indexed) groups */
|
||||
if (found_group) {
|
||||
/* We remove that group from its position (which is at i)... */
|
||||
for (j = i; j < (garg->gidcnt - 1); j++)
|
||||
garg->gid_arr[j] = garg->gid_arr[j + 1]; /* ...shift remaining groups left ... */
|
||||
garg->gidcnt--; /* ..and update the book keeping for the number of groups */
|
||||
|
||||
for (j = i + 1; j < garg->gidcnt; j++)
|
||||
garg->gid_arr[j - 1] = garg->gid_arr[j];
|
||||
/*
|
||||
* We also must update the number of groups either in a previous tuple (which we
|
||||
* must identify and check whether it becomes empty due to the deletion) or in
|
||||
* the current tuple, pending where the deleted group resides
|
||||
*/
|
||||
k = 0;
|
||||
for (j = 0; j < garg->tplcnt; j++) {
|
||||
k += garg->tuplcnt_arr[j];
|
||||
/* Remark: i is zero-indexed, k is one-indexed */
|
||||
if (k > i) { /* remove from one of the previous tuples */
|
||||
garg->tuplcnt_arr[j]--;
|
||||
break; /* We took care not to have group duplicates, hence we can stop here */
|
||||
}
|
||||
}
|
||||
if (k <= i) /* remove from current tuple */
|
||||
garg->tuplcnt_arr[j]--;
|
||||
|
||||
garg->gidcnt--;
|
||||
/* We also remove the group from the list of keyshares (if present) */
|
||||
found_group = 0;
|
||||
for (i = 0; i < garg->ksidcnt; i++)
|
||||
if (garg->ksid_arr[i] == gid) {
|
||||
found_group = 1;
|
||||
break;
|
||||
}
|
||||
if (found_group) {
|
||||
/* Found, hence we remove that keyshare from its position (which is at i)... */
|
||||
for (j = i; j < (garg->ksidcnt - 1); j++)
|
||||
garg->ksid_arr[j] = garg->ksid_arr[j + 1]; /* shift remaining key shares */
|
||||
/* ... and update the book keeping */
|
||||
garg->ksidcnt--;
|
||||
}
|
||||
}
|
||||
} else { /* Processing addition of a single new group */
|
||||
|
||||
/* Check for duplicates */
|
||||
for (i = 0; i < garg->gidcnt; i++)
|
||||
if (garg->gid_arr[i] == gid) {
|
||||
/* Duplicate group anywhere in the list of groups - ignore */
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Add the current group to the 'flat' list of groups */
|
||||
garg->gid_arr[garg->gidcnt++] = gid;
|
||||
/* and update the book keeping for the number of groups in current tuple */
|
||||
garg->tuplcnt_arr[garg->tplcnt]++;
|
||||
|
||||
/* We memorize if needed that we want to add a key share for the current group */
|
||||
if (add_keyshare)
|
||||
garg->ksid_arr[garg->ksidcnt++] = gid;
|
||||
}
|
||||
|
||||
if (!found_group && !remove_group)
|
||||
garg->gid_arr[garg->gidcnt++] = gid;
|
||||
|
||||
return 1;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Set groups based on a colon separated list */
|
||||
int tls1_set_groups_list(SSL_CTX *ctx, uint16_t **pext, size_t *pextlen,
|
||||
/* Extract and process a tuple of groups */
|
||||
static int tuple_cb(const char *tuple, int len, void *arg)
|
||||
{
|
||||
gid_cb_st *garg = arg;
|
||||
int retval = 1; /* We assume success */
|
||||
char *restored_tuple_string;
|
||||
|
||||
/* Sanity checks */
|
||||
if (garg == NULL || tuple == NULL || len <= 0) {
|
||||
ERR_raise(ERR_LIB_CONF, CONF_R_VARIABLE_HAS_NO_VALUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Memory management for tuples */
|
||||
if (garg->tplcnt == garg->tplmax) {
|
||||
size_t *tmp =
|
||||
OPENSSL_realloc(garg->tuplcnt_arr,
|
||||
(garg->tplmax + GROUPLIST_INCREMENT) * sizeof(*garg->tuplcnt_arr));
|
||||
|
||||
if (tmp == NULL)
|
||||
return 0;
|
||||
garg->tplmax += GROUPLIST_INCREMENT;
|
||||
garg->tuplcnt_arr = tmp;
|
||||
}
|
||||
|
||||
/* Convert to \0-terminated string */
|
||||
restored_tuple_string = OPENSSL_malloc((len + 1 /* \0 */) * sizeof(char));
|
||||
if (restored_tuple_string == NULL)
|
||||
return 0;
|
||||
memcpy(restored_tuple_string, tuple, len);
|
||||
restored_tuple_string[len] = '\0';
|
||||
|
||||
/* Analyze group list of this tuple */
|
||||
retval = CONF_parse_list(restored_tuple_string, GROUP_DELIMITER_CHARACTER, 1, gid_cb, arg);
|
||||
|
||||
/* We don't need the \o-terminated string anymore */
|
||||
OPENSSL_free(restored_tuple_string);
|
||||
|
||||
if (garg->tuplcnt_arr[garg->tplcnt] > 0) { /* Some valid groups are present in current tuple... */
|
||||
if (garg->tuple_mode) {
|
||||
/* We 'close' the tuple */
|
||||
garg->tplcnt++;
|
||||
garg->tuplcnt_arr[garg->tplcnt] = 0; /* Next tuple is initialized to be empty */
|
||||
garg->tuple_mode = 1; /* next call will start a tuple (unless overridden in gid_cb) */
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set groups and prepare generation of keyshares based on a string of groupnames,
|
||||
* names separated by the group or the tuple delimiter, with per-group prefixes to
|
||||
* (1) add a key share for this group, (2) ignore the group if unkown to the current
|
||||
* context, (3) delete a previous occurrence of the group in the current tuple.
|
||||
*
|
||||
* The list parsing is done in two hierachical steps: The top-level step extracts the
|
||||
* string of a tuple using tuple_cb, while the next lower step uses gid_cb to
|
||||
* parse and process the groups inside a tuple
|
||||
*/
|
||||
int tls1_set_groups_list(SSL_CTX *ctx,
|
||||
uint16_t **grpext, size_t *grpextlen,
|
||||
uint16_t **ksext, size_t *ksextlen,
|
||||
size_t **tplext, size_t *tplextlen,
|
||||
const char *str)
|
||||
{
|
||||
size_t i, j;
|
||||
int ret = 0, parse_ret = 0;
|
||||
gid_cb_st gcb;
|
||||
uint16_t *tmparr;
|
||||
int ret = 0;
|
||||
|
||||
/* Sanity check */
|
||||
if (ctx == NULL) {
|
||||
ERR_raise(ERR_LIB_CONF, CONF_R_VARIABLE_HAS_NO_VALUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gcb.tuple_mode = 1; /* We prepare to collect the first tuple */
|
||||
gcb.ignore_unknown_default = 0;
|
||||
gcb.gidcnt = 0;
|
||||
gcb.ksidcnt = 0;
|
||||
gcb.tplcnt = 0;
|
||||
gcb.gidmax = GROUPLIST_INCREMENT;
|
||||
gcb.tplmax = GROUPLIST_INCREMENT;
|
||||
gcb.ksidmax = GROUPLIST_INCREMENT;
|
||||
gcb.ctx = ctx;
|
||||
|
||||
/* Prepare initial chunks of memory for groups, tuples and keyshares groupIDs */
|
||||
gcb.gid_arr = OPENSSL_malloc(gcb.gidmax * sizeof(*gcb.gid_arr));
|
||||
if (gcb.gid_arr == NULL)
|
||||
return 0;
|
||||
gcb.ctx = ctx;
|
||||
if (!CONF_parse_list(str, ':', 1, gid_cb, &gcb))
|
||||
goto end;
|
||||
gcb.tuplcnt_arr = OPENSSL_malloc(gcb.tplmax * sizeof(*gcb.tuplcnt_arr));
|
||||
if (gcb.tuplcnt_arr == NULL)
|
||||
goto end;
|
||||
gcb.tuplcnt_arr[0] = 0;
|
||||
gcb.ksid_arr = OPENSSL_malloc(gcb.ksidmax * sizeof(*gcb.ksid_arr));
|
||||
if (gcb.ksid_arr == NULL)
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* Start the (potentially recursive) tuple processing by calling CONF_parse_list
|
||||
* with the TUPLE_DELIMITER_CHARACTER (which will call tuple_cb after cleaning spaces)
|
||||
*/
|
||||
parse_ret = CONF_parse_list(str, TUPLE_DELIMITER_CHARACTER, 1, tuple_cb, &gcb);
|
||||
|
||||
if (parse_ret == 0)
|
||||
goto end;
|
||||
if (parse_ret == -1) {
|
||||
ERR_raise_data(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT,
|
||||
"Syntax error in '%s'", str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
* We check whether a tuple was completly emptied by using "-" prefix excessively,
|
||||
* in which case we remove the tuple
|
||||
*/
|
||||
for (i = 0; i < gcb.tplcnt; i++) {
|
||||
if (gcb.tuplcnt_arr[i] == 0) {
|
||||
for (j = i; j < (gcb.tplcnt - 1); j++) /* Move tuples to the left */
|
||||
gcb.tuplcnt_arr[j] = gcb.tuplcnt_arr[j + 1];
|
||||
|
||||
gcb.tplcnt--; /* We just deleted a tuple, update book keeping */
|
||||
i--; /* Acount for the fact that the list is shorter now */
|
||||
}
|
||||
}
|
||||
|
||||
/* Some more checks (at least one remaining group, not more that nominally 4 key shares */
|
||||
if (gcb.gidcnt == 0) {
|
||||
ERR_raise_data(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT,
|
||||
"No valid groups in '%s'", str);
|
||||
goto end;
|
||||
}
|
||||
if (pext == NULL) {
|
||||
if (gcb.ksidcnt > OPENSSL_CLIENT_MAX_KEY_SHARES) {
|
||||
ERR_raise_data(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT,
|
||||
"To many keyshares requested in '%s' (max = %d)",
|
||||
str, OPENSSL_CLIENT_MAX_KEY_SHARES);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
* For backward compatibility we let the rest of the code know that a key share
|
||||
* for the first valid group should be added if no "*" prefix was used anywhere
|
||||
*/
|
||||
if (gcb.ksidcnt == 0) {
|
||||
/*
|
||||
* No key share group prefix character was used, hence we indicate that a single
|
||||
* key share should be sent and flag that it should come from the supported_groups list
|
||||
*/
|
||||
gcb.ksidcnt = 1;
|
||||
gcb.ksid_arr[0] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A call to tls1_set_groups_list with any of the args (other than ctx) set
|
||||
* to NULL only does a syntax check, hence we're done here and report success
|
||||
*/
|
||||
if (grpext == NULL || ksext == NULL || tplext == NULL ||
|
||||
grpextlen == NULL || ksextlen == NULL || tplextlen == NULL) {
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
* gid_cb ensurse there are no duplicates so we can just go ahead and set
|
||||
* the result
|
||||
* tuple_cb and gid_cb combo ensures there are no duplicates or unknown groups so we
|
||||
* can just go ahead and set the results (after diposing the existing)
|
||||
*/
|
||||
tmparr = OPENSSL_memdup(gcb.gid_arr, gcb.gidcnt * sizeof(*tmparr));
|
||||
if (tmparr == NULL)
|
||||
goto end;
|
||||
OPENSSL_free(*pext);
|
||||
*pext = tmparr;
|
||||
*pextlen = gcb.gidcnt;
|
||||
ret = 1;
|
||||
OPENSSL_free(*grpext);
|
||||
*grpext = gcb.gid_arr;
|
||||
*grpextlen = gcb.gidcnt;
|
||||
OPENSSL_free(*ksext);
|
||||
*ksext = gcb.ksid_arr;
|
||||
*ksextlen = gcb.ksidcnt;
|
||||
OPENSSL_free(*tplext);
|
||||
*tplext = gcb.tuplcnt_arr;
|
||||
*tplextlen = gcb.tplcnt;
|
||||
|
||||
return 1;
|
||||
|
||||
end:
|
||||
OPENSSL_free(gcb.gid_arr);
|
||||
OPENSSL_free(gcb.tuplcnt_arr);
|
||||
OPENSSL_free(gcb.ksid_arr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -170,9 +170,11 @@ EVP_PKEY *ssl_dh_to_pkey(DH *dh)
|
||||
|
||||
/* Some deprecated public APIs pass EC_KEY objects */
|
||||
int ssl_set_tmp_ecdh_groups(uint16_t **pext, size_t *pextlen,
|
||||
uint16_t **ksext, size_t *ksextlen,
|
||||
size_t **tplext, size_t *tplextlen,
|
||||
void *key)
|
||||
{
|
||||
# ifndef OPENSSL_NO_EC
|
||||
# ifndef OPENSSL_NO_EC
|
||||
const EC_GROUP *group = EC_KEY_get0_group((const EC_KEY *)key);
|
||||
int nid;
|
||||
|
||||
@ -183,10 +185,13 @@ int ssl_set_tmp_ecdh_groups(uint16_t **pext, size_t *pextlen,
|
||||
nid = EC_GROUP_get_curve_name(group);
|
||||
if (nid == NID_undef)
|
||||
return 0;
|
||||
return tls1_set_groups(pext, pextlen, &nid, 1);
|
||||
# else
|
||||
return tls1_set_groups(pext, pextlen,
|
||||
ksext, ksextlen,
|
||||
tplext, tplextlen,
|
||||
&nid, 1);
|
||||
# else
|
||||
return 0;
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -178,6 +178,13 @@ IF[{- !$disabled{tests} -}]
|
||||
INCLUDE[tls13ccstest]=../include ../apps/include
|
||||
DEPEND[tls13ccstest]=../libcrypto ../libssl libtestutil.a
|
||||
|
||||
IF[{- !$disabled{ecx} && !$disabled{tls} && !$disabled{tls1_3} -}]
|
||||
PROGRAMS{noinst}=tls13groupselection_test
|
||||
SOURCE[tls13groupselection_test]=tls13groupselection_test.c helpers/ssltestlib.c
|
||||
INCLUDE[tls13groupselection_test]=../include ../apps/include
|
||||
DEPEND[tls13groupselection_test]=../libcrypto ../libssl libtestutil.a
|
||||
ENDIF
|
||||
|
||||
SOURCE[upcallstest]=upcallstest.c
|
||||
INCLUDE[upcallstest]=../include ../apps/include
|
||||
DEPEND[upcallstest]=../libcrypto libtestutil.a
|
||||
|
26
test/recipes/70-test_tls13groupselection.t
Normal file
26
test/recipes/70-test_tls13groupselection.t
Normal file
@ -0,0 +1,26 @@
|
||||
#! /usr/bin/env perl
|
||||
# Copyright 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
|
||||
# in the file LICENSE in the source distribution or at
|
||||
# https://www.openssl.org/source/license.html
|
||||
|
||||
use OpenSSL::Test::Simple;
|
||||
use OpenSSL::Test qw/:DEFAULT srctop_file/;
|
||||
use OpenSSL::Test::Utils qw(disabled);
|
||||
|
||||
setup("test_tls13groupselection");
|
||||
|
||||
plan skip_all => "needs TLSv1.3 enabled"
|
||||
if disabled("tls1_3");
|
||||
|
||||
plan skip_all => "needs ECX enabled"
|
||||
if disabled("ecx");
|
||||
|
||||
|
||||
plan tests => 1;
|
||||
|
||||
ok(run(test(["tls13groupselection_test", srctop_file("apps", "server.pem"),
|
||||
srctop_file("apps", "server.pem")])),
|
||||
"running tls13groupselection_test");
|
546
test/tls13groupselection_test.c
Normal file
546
test/tls13groupselection_test.c
Normal file
@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright 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
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include "testutil.h"
|
||||
#include "helpers/ssltestlib.h"
|
||||
#include <openssl/objects.h>
|
||||
|
||||
#define TEST_true_or_end(a) if (!TEST_true(a)) \
|
||||
goto end;
|
||||
|
||||
#define TEST_false_or_end(a) if (!TEST_false(a)) \
|
||||
goto end;
|
||||
|
||||
#define SERVER_PREFERENCE 1
|
||||
#define CLIENT_PREFERENCE 0
|
||||
|
||||
#define WORK_ON_SSL_OBJECT 1
|
||||
#define WORK_ON_CONTEXT 0
|
||||
|
||||
#define SYNTAX_FAILURE "SYNTAX_FAILURE"
|
||||
#define NEGOTIATION_FAILURE "NEGOTIATION_FAILURE"
|
||||
|
||||
typedef enum TEST_TYPE {
|
||||
TEST_NEGOTIATION_FAILURE = 0,
|
||||
TEST_NEGOTIATION_SUCCESS = 1,
|
||||
TEST_SYNTAX_FAILURE = 2
|
||||
} TEST_TYPE;
|
||||
|
||||
typedef enum SERVER_RESPONSE {
|
||||
HRR = 0,
|
||||
INIT = 1,
|
||||
SH = 2
|
||||
} SERVER_RESPONSE;
|
||||
|
||||
static char *cert = NULL;
|
||||
static char *privkey = NULL;
|
||||
|
||||
struct tls13groupselection_test_st {
|
||||
const char *client_groups;
|
||||
const char *server_groups;
|
||||
const int preference;
|
||||
const char *expected_group;
|
||||
const enum SERVER_RESPONSE expected_server_response;
|
||||
};
|
||||
|
||||
static const struct tls13groupselection_test_st tls13groupselection_tests[] =
|
||||
{
|
||||
|
||||
/*
|
||||
* (A) Test with no explicit key share (backward compatibility)
|
||||
* Key share is implicitly sent for first client group
|
||||
* Test (implicitly) that the key share group is used
|
||||
*/
|
||||
{ "secp384r1:secp521r1:X25519:prime256v1:X448", /* test 0 */
|
||||
"X25519:secp521r1:secp384r1:prime256v1:X448",
|
||||
CLIENT_PREFERENCE,
|
||||
"secp384r1", SH
|
||||
},
|
||||
{ "secp521r1:secp384r1:X25519:prime256v1:X448", /* test 1 */
|
||||
"X25519:secp521r1:secp384r1:prime256v1:X448",
|
||||
SERVER_PREFERENCE,
|
||||
"secp521r1", SH
|
||||
},
|
||||
|
||||
/*
|
||||
* (B) No explicit key share test (backward compatibility)
|
||||
* Key share is implicitly sent for first client group
|
||||
* Check HRR if server does not support key share group
|
||||
*/
|
||||
{ "secp521r1:secp384r1:X25519:prime256v1:X448", /* test 2 */
|
||||
"X25519:secp384r1:prime256v1",
|
||||
CLIENT_PREFERENCE,
|
||||
"secp384r1", HRR
|
||||
},
|
||||
{ "secp521r1:secp384r1:X25519:prime256v1:X448", /* test 3 */
|
||||
"X25519:secp384r1:prime256v1",
|
||||
SERVER_PREFERENCE,
|
||||
"X25519", HRR
|
||||
},
|
||||
|
||||
/*
|
||||
* (C) Explicit key shares, SH tests
|
||||
* Test key share selection as function of client-/server-preference
|
||||
* Test (implicitly) that multiple key shares are generated
|
||||
* Test (implicitly) that multiple tuples don't influence the client
|
||||
* Test (implicitly) that key share prefix doesn't influence the server
|
||||
*/
|
||||
{ "secp521r1:secp384r1:*X25519/*prime256v1:X448", /* test 4 */
|
||||
"secp521r1:*prime256v1:X25519:X448",
|
||||
CLIENT_PREFERENCE,
|
||||
"X25519", SH
|
||||
},
|
||||
{ "secp521r1:secp384r1:*X25519/*prime256v1:X448", /* test 5 */
|
||||
"secp521r1:*prime256v1:X25519:X448",
|
||||
SERVER_PREFERENCE,
|
||||
"prime256v1", SH
|
||||
},
|
||||
|
||||
/*
|
||||
* (D) Explicit key shares, HRR tests
|
||||
* Check that HRR is issued if group in first tuple
|
||||
* is supported but no key share is available for the tuple
|
||||
*/
|
||||
{ "secp521r1:secp384r1:*X25519:prime256v1:*X448", /* test 6 */
|
||||
"secp384r1:secp521r1:prime256v1/X25519:X448",
|
||||
CLIENT_PREFERENCE,
|
||||
"secp521r1", HRR
|
||||
},
|
||||
{ "secp521r1:secp384r1:*X25519:prime256v1:*X448", /* test 7 */
|
||||
"secp384r1:secp521r1:prime256v1/X25519:X448",
|
||||
SERVER_PREFERENCE,
|
||||
"secp384r1", HRR
|
||||
},
|
||||
|
||||
/*
|
||||
* (E) Multiple tuples tests, client without tuple delimiters
|
||||
* Check that second tuple is evaluated if there isn't any match
|
||||
* first tuple
|
||||
*/
|
||||
{ "*X25519:prime256v1:*X448", /* test 8 */
|
||||
"secp521r1:secp384r1/X448:X25519",
|
||||
CLIENT_PREFERENCE,
|
||||
"X25519", SH
|
||||
},
|
||||
{ "*X25519:prime256v1:*X448", /* test 9 */
|
||||
"secp521r1:secp384r1/X448:X25519",
|
||||
SERVER_PREFERENCE,
|
||||
"X448", SH
|
||||
},
|
||||
|
||||
/* (F) Check that '?' will ignore unknown group but use known group */
|
||||
{ "*X25519:?unknown_group_123:prime256v1:*X448", /* test 10 */
|
||||
"secp521r1:secp384r1/X448:?unknown_group_456:?X25519",
|
||||
CLIENT_PREFERENCE,
|
||||
"X25519", SH
|
||||
},
|
||||
{ "*X25519:prime256v1:*X448:?*unknown_group_789", /* test 11 */
|
||||
"secp521r1:secp384r1/?X448:?unknown_group_456:X25519",
|
||||
SERVER_PREFERENCE,
|
||||
"X448", SH
|
||||
},
|
||||
|
||||
/*
|
||||
* (G) Check full backward compatibility (= don't explicitly set any groups)
|
||||
*/
|
||||
{ NULL, /* test 12 */
|
||||
NULL,
|
||||
CLIENT_PREFERENCE,
|
||||
"X25519", SH
|
||||
},
|
||||
{ NULL, /* test 13 */
|
||||
NULL,
|
||||
SERVER_PREFERENCE,
|
||||
"X25519", SH
|
||||
},
|
||||
|
||||
/*
|
||||
* (H) Check that removal of group is 'active'
|
||||
*/
|
||||
{ "*X25519:*X448", /* test 14 */
|
||||
"secp521r1:X25519:prime256v1:-X25519:secp384r1/X448",
|
||||
CLIENT_PREFERENCE,
|
||||
"X448", SH
|
||||
},
|
||||
{ "*X25519:*X448", /* test 15 */
|
||||
"secp521r1:X25519:prime256v1:-X25519:secp384r1/X448",
|
||||
SERVER_PREFERENCE,
|
||||
"X448", SH
|
||||
},
|
||||
{ "*X25519:prime256v1:*X448", /* test 16 */
|
||||
"X25519:prime256v1/X448:-X25519",
|
||||
CLIENT_PREFERENCE,
|
||||
"prime256v1", HRR
|
||||
},
|
||||
{ "*X25519:prime256v1:*X448", /* test 17 */
|
||||
"X25519:prime256v1/X448:-X25519",
|
||||
SERVER_PREFERENCE,
|
||||
"prime256v1", HRR
|
||||
},
|
||||
/*
|
||||
* (I) Check handling of the "DEFAULT" 'pseudo group name'
|
||||
*/
|
||||
{ "*X25519:DEFAULT:-prime256v1:-X448", /* test 18 */
|
||||
"DEFAULT:-X25519",
|
||||
CLIENT_PREFERENCE,
|
||||
"secp521r1", HRR
|
||||
},
|
||||
{ "*X25519:DEFAULT:-prime256v1:-X448", /* test 19 */
|
||||
"DEFAULT:-X25519",
|
||||
SERVER_PREFERENCE,
|
||||
"secp521r1", HRR
|
||||
},
|
||||
/*
|
||||
* (J) Deduplication check
|
||||
*/
|
||||
{ "secp521r1:X25519:prime256v1/X25519:prime256v1/X448", /* test 20 */
|
||||
"secp521r1:X25519:prime256v1/X25519:prime256v1/X448",
|
||||
CLIENT_PREFERENCE,
|
||||
"secp521r1", SH
|
||||
},
|
||||
{ "secp521r1:X25519:prime256v1/X25519:prime256v1/X448", /* test 21 */
|
||||
"secp521r1:X25519:prime256v1/X25519:prime256v1/X448",
|
||||
SERVER_PREFERENCE,
|
||||
"secp521r1", SH
|
||||
},
|
||||
/*
|
||||
* (K) Check group removal when first entry requested a keyshare
|
||||
*/
|
||||
{ "*X25519:*prime256v1:-X25519", /* test 22 */
|
||||
"X25519:prime256v1",
|
||||
CLIENT_PREFERENCE,
|
||||
"prime256v1", SH
|
||||
},
|
||||
/*
|
||||
* (L) Syntax errors
|
||||
*/
|
||||
{ "*X25519:*prime256v1:NOTVALID", /* test 23 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "X25519//prime256v1", /* test 24 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "**X25519:*prime256v1", /* test 25 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "*X25519:*secp256r1:*X448:*secp521r1:*secp384r1", /* test 26 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "*X25519:*secp256r1:?:*secp521r1", /* test 27 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "*X25519:*secp256r1::secp521r1", /* test 28 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ ":*secp256r1:secp521r1", /* test 29 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "*secp256r1:secp521r1:", /* test 30 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "/secp256r1/secp521r1", /* test 31 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "secp256r1/secp521r1/", /* test 32 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
/* test 33 remove all groups */
|
||||
{ "X25519:secp256r1:X448:secp521r1:-X448:-secp256r1:-X25519:-secp521r1",
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "X25519:??secp256r1:X448", /* test 34 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "X25519:secp256r1:**X448", /* test 35 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "--X25519:secp256r1:X448", /* test 36 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "-DEFAULT", /* test 37 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
{ "?DEFAULT", /* test 38 */
|
||||
"",
|
||||
CLIENT_PREFERENCE,
|
||||
SYNTAX_FAILURE
|
||||
},
|
||||
/*
|
||||
* Negotiation Failures
|
||||
* No overlapping groups between client and server
|
||||
*/
|
||||
{ "secp384r1:secp521r1:X25519", /* test 39 */
|
||||
"prime256v1:X448",
|
||||
CLIENT_PREFERENCE,
|
||||
NEGOTIATION_FAILURE
|
||||
},
|
||||
{ "secp521r1:secp384r1:X25519", /* test 40 */
|
||||
"prime256v1:X448",
|
||||
SERVER_PREFERENCE,
|
||||
NEGOTIATION_FAILURE
|
||||
},
|
||||
/*
|
||||
* These are allowed
|
||||
* "X25519/prime256v1:-X448", "X25519:-*X25519:*prime256v1, "*DEFAULT"
|
||||
*/
|
||||
/*
|
||||
* Tests to show that spaces between tuples are allowed
|
||||
*/
|
||||
{ "secp521r1:X25519 / prime256v1/X25519 / prime256v1/X448", /* test 41 */
|
||||
"secp521r1:X25519 / prime256v1/X25519 / prime256v1/X448",
|
||||
CLIENT_PREFERENCE,
|
||||
"secp521r1", SH
|
||||
},
|
||||
{ "secp521r1 / prime256v1:X25519 / prime256v1/X448", /* test 42 */
|
||||
"secp521r1 / prime256v1:X25519 / prime256v1/X448",
|
||||
SERVER_PREFERENCE,
|
||||
"secp521r1", SH
|
||||
},
|
||||
};
|
||||
|
||||
static void server_response_check_cb(int write_p, int version,
|
||||
int content_type, const void *buf,
|
||||
size_t len, SSL *ssl, void *arg)
|
||||
{
|
||||
/* Cast arg to SERVER_RESPONSE */
|
||||
enum SERVER_RESPONSE *server_response = (enum SERVER_RESPONSE *)arg;
|
||||
/* Prepare check for HRR */
|
||||
const uint8_t *incoming_random = (uint8_t *)buf + 6;
|
||||
const uint8_t magic_HRR_random[32] = { 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
|
||||
0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
|
||||
0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
|
||||
0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C };
|
||||
|
||||
/* Did a server hello arrive? */
|
||||
if (write_p == 0 && /* Incoming data... */
|
||||
content_type == SSL3_RT_HANDSHAKE && /* carrying a handshake record type ... */
|
||||
version == TLS1_3_VERSION && /* for TLSv1.3 ... */
|
||||
((uint8_t *)buf)[0] == SSL3_MT_SERVER_HELLO) { /* with message type "ServerHello" */
|
||||
/* Check what it is: SH or HRR (compare the 'random' data field with HRR magic number) */
|
||||
if (memcmp((void *)incoming_random, (void *)magic_HRR_random, 32) == 0)
|
||||
*server_response *= HRR;
|
||||
else
|
||||
*server_response *= SH;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_invalidsyntax(const struct tls13groupselection_test_st *current_test_vector,
|
||||
int ssl_or_ctx)
|
||||
{
|
||||
int ok = 0;
|
||||
SSL_CTX *client_ctx = NULL, *server_ctx = NULL;
|
||||
SSL *clientssl = NULL, *serverssl = NULL;
|
||||
|
||||
TEST_ptr(current_test_vector->client_groups);
|
||||
TEST_size_t_ne(strlen(current_test_vector->client_groups), 0);
|
||||
|
||||
/* Creation of the contexts */
|
||||
TEST_true_or_end(create_ssl_ctx_pair(NULL, TLS_server_method(),
|
||||
TLS_client_method(),
|
||||
TLS1_VERSION, 0,
|
||||
&server_ctx, &client_ctx,
|
||||
cert, privkey));
|
||||
|
||||
/* Customization of the contexts */
|
||||
if (ssl_or_ctx == WORK_ON_CONTEXT) {
|
||||
TEST_false_or_end(SSL_CTX_set1_groups_list(client_ctx,
|
||||
current_test_vector->client_groups));
|
||||
}
|
||||
/* Creation of the SSL objects */
|
||||
TEST_true_or_end(create_ssl_objects(server_ctx, client_ctx,
|
||||
&serverssl, &clientssl,
|
||||
NULL, NULL));
|
||||
|
||||
/* Customization of the SSL objects */
|
||||
if (ssl_or_ctx == WORK_ON_SSL_OBJECT)
|
||||
TEST_false_or_end(SSL_set1_groups_list(clientssl, current_test_vector->client_groups));
|
||||
|
||||
ok = 1;
|
||||
|
||||
end:
|
||||
SSL_free(serverssl);
|
||||
SSL_free(clientssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx);
|
||||
return ok;
|
||||
|
||||
}
|
||||
|
||||
static int test_groupnegotiation(const struct tls13groupselection_test_st *current_test_vector,
|
||||
int ssl_or_ctx, TEST_TYPE test_type)
|
||||
{
|
||||
int ok = 0;
|
||||
int negotiated_group_client = 0;
|
||||
int negotiated_group_server = 0;
|
||||
SSL_CTX *client_ctx = NULL, *server_ctx = NULL;
|
||||
SSL *clientssl = NULL, *serverssl = NULL;
|
||||
enum SERVER_RESPONSE server_response;
|
||||
|
||||
/* Creation of the contexts */
|
||||
TEST_true_or_end(create_ssl_ctx_pair(NULL, TLS_server_method(),
|
||||
TLS_client_method(),
|
||||
TLS1_VERSION, 0,
|
||||
&server_ctx, &client_ctx,
|
||||
cert, privkey));
|
||||
|
||||
/* Customization of the contexts */
|
||||
if (ssl_or_ctx == WORK_ON_CONTEXT) {
|
||||
if (current_test_vector->client_groups != NULL) {
|
||||
TEST_true_or_end(SSL_CTX_set1_groups_list(client_ctx,
|
||||
current_test_vector->client_groups));
|
||||
}
|
||||
if (current_test_vector->server_groups != NULL) {
|
||||
TEST_true_or_end(SSL_CTX_set1_groups_list(server_ctx,
|
||||
current_test_vector->server_groups));
|
||||
}
|
||||
TEST_true_or_end(SSL_CTX_set_min_proto_version(client_ctx, TLS1_3_VERSION));
|
||||
TEST_true_or_end(SSL_CTX_set_min_proto_version(server_ctx, TLS1_3_VERSION));
|
||||
if (current_test_vector->preference == SERVER_PREFERENCE)
|
||||
SSL_CTX_set_options(server_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
}
|
||||
/* Creation of the SSL objects */
|
||||
if (!TEST_true(create_ssl_objects(server_ctx, client_ctx,
|
||||
&serverssl, &clientssl,
|
||||
NULL, NULL)))
|
||||
goto end;
|
||||
|
||||
/* Customization of the SSL objects */
|
||||
if (ssl_or_ctx == WORK_ON_SSL_OBJECT) {
|
||||
if (current_test_vector->client_groups != NULL)
|
||||
TEST_true_or_end(SSL_set1_groups_list(clientssl, current_test_vector->client_groups));
|
||||
|
||||
if (current_test_vector->server_groups != NULL)
|
||||
TEST_true_or_end(SSL_set1_groups_list(serverssl, current_test_vector->server_groups));
|
||||
|
||||
TEST_true_or_end(SSL_set_min_proto_version(clientssl, TLS1_3_VERSION));
|
||||
TEST_true_or_end(SSL_set_min_proto_version(serverssl, TLS1_3_VERSION));
|
||||
|
||||
if (current_test_vector->preference == SERVER_PREFERENCE)
|
||||
SSL_set_options(serverssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
}
|
||||
|
||||
/* We set the message callback on the client side (which checks SH/HRR) */
|
||||
server_response = INIT; /* Variable to hold server response info */
|
||||
SSL_set_msg_callback_arg(clientssl, &server_response); /* add it to the callback */
|
||||
SSL_set_msg_callback(clientssl, server_response_check_cb); /* and activate callback */
|
||||
|
||||
/* Creating a test connection */
|
||||
if (test_type == TEST_NEGOTIATION_SUCCESS) {
|
||||
TEST_true_or_end(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE));
|
||||
|
||||
/*
|
||||
* Checking that the negotiated group matches our expectation
|
||||
* and must be identical on server and client
|
||||
* and must be expected SH or HRR
|
||||
*/
|
||||
negotiated_group_client = SSL_get_negotiated_group(clientssl);
|
||||
negotiated_group_server = SSL_get_negotiated_group(serverssl);
|
||||
if (!TEST_int_eq(negotiated_group_client, negotiated_group_server))
|
||||
goto end;
|
||||
if (!TEST_int_eq((int)current_test_vector->expected_server_response, (int)server_response))
|
||||
goto end;
|
||||
if (TEST_int_eq(negotiated_group_client, OBJ_sn2nid(current_test_vector->expected_group)))
|
||||
ok = 1;
|
||||
} else {
|
||||
TEST_false_or_end(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE));
|
||||
ok = 1;
|
||||
}
|
||||
|
||||
end:
|
||||
SSL_free(serverssl);
|
||||
SSL_free(clientssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int tls13groupselection_test(int i)
|
||||
{
|
||||
int testresult = 1; /* Assume the test will succeed */
|
||||
int res = 0;
|
||||
TEST_TYPE test_type = TEST_NEGOTIATION_SUCCESS;
|
||||
|
||||
/*
|
||||
* Call the code under test, once such that the ssl object is used and
|
||||
* once such that the ctx is used. If any of the tests fail (= return 0),
|
||||
* the end result will be 0 thanks to multiplication
|
||||
*/
|
||||
TEST_info("==> Running TLSv1.3 test %d", i);
|
||||
|
||||
if (strncmp(tls13groupselection_tests[i].expected_group,
|
||||
SYNTAX_FAILURE, sizeof(SYNTAX_FAILURE)) == 0)
|
||||
test_type = TEST_SYNTAX_FAILURE;
|
||||
else if (strncmp(tls13groupselection_tests[i].expected_group,
|
||||
NEGOTIATION_FAILURE, sizeof(NEGOTIATION_FAILURE)) == 0)
|
||||
test_type = TEST_NEGOTIATION_FAILURE;
|
||||
|
||||
if (test_type == TEST_SYNTAX_FAILURE)
|
||||
res = test_invalidsyntax(&tls13groupselection_tests[i],
|
||||
WORK_ON_SSL_OBJECT);
|
||||
else
|
||||
res = test_groupnegotiation(&tls13groupselection_tests[i],
|
||||
WORK_ON_SSL_OBJECT, test_type);
|
||||
|
||||
if (!res)
|
||||
TEST_error("====> [ERROR] TLSv1.3 test %d with WORK_ON_SSL_OBJECT failed", i);
|
||||
testresult *= res;
|
||||
|
||||
if (test_type == TEST_SYNTAX_FAILURE)
|
||||
res = test_invalidsyntax(&tls13groupselection_tests[i],
|
||||
WORK_ON_CONTEXT);
|
||||
else
|
||||
res = test_groupnegotiation(&tls13groupselection_tests[i],
|
||||
WORK_ON_CONTEXT, test_type);
|
||||
|
||||
if (!res)
|
||||
TEST_error("====> [ERROR] TLSv1.3 test %d with WORK_ON_CONTEXT failed", i);
|
||||
testresult *= res;
|
||||
|
||||
return testresult;
|
||||
}
|
||||
|
||||
int setup_tests(void)
|
||||
{
|
||||
if (!TEST_ptr(cert = test_get_argument(0))
|
||||
|| !TEST_ptr(privkey = test_get_argument(1)))
|
||||
return 0;
|
||||
|
||||
ADD_ALL_TESTS(tls13groupselection_test, OSSL_NELEM(tls13groupselection_tests));
|
||||
return 1;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user