Skip to content

Commit 38b4c02

Browse files
committed
Add RFC8701 GREASE Support
This adds support for RFC8701, which can be used in testing to ensure TLS extensibility and proper processing. This is different from fuzzing in that this can be done on both the client and the server, and allows the connection to complete. The API is public to allow end-users to do their own testing with GREASE.
1 parent 9505105 commit 38b4c02

File tree

16 files changed

+765
-8
lines changed

16 files changed

+765
-8
lines changed

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,13 @@ OpenSSL 3.2
878878

879879
*Richard Levitte*
880880

881+
* Add support for RFC8701 GREASE. Extra values can be added to known
882+
extensions and cipher lists in the ClientHello. Additional extensions can
883+
also be added. This is intended for testing unknown values in extensions
884+
and cipher lists, and not for implementing custom extensions or ciphers.
885+
886+
*Todd Short*
887+
881888
* The BLAKE2b hash algorithm supports a configurable output length
882889
by setting the "size" parameter.
883890

doc/build.info

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,10 @@ DEPEND[html/man3/SSL_CTX_add_session.html]=man3/SSL_CTX_add_session.pod
22352235
GENERATE[html/man3/SSL_CTX_add_session.html]=man3/SSL_CTX_add_session.pod
22362236
DEPEND[man/man3/SSL_CTX_add_session.3]=man3/SSL_CTX_add_session.pod
22372237
GENERATE[man/man3/SSL_CTX_add_session.3]=man3/SSL_CTX_add_session.pod
2238+
DEPEND[html/man3/SSL_CTX_clean_grease.html]=man3/SSL_CTX_clean_grease.pod
2239+
GENERATE[html/man3/SSL_CTX_clean_grease.html]=man3/SSL_CTX_clean_grease.pod
2240+
DEPEND[man/man3/SSL_CTX_clean_grease.3]=man3/SSL_CTX_clean_grease.pod
2241+
GENERATE[man/man3/SSL_CTX_clean_grease.3]=man3/SSL_CTX_clean_grease.pod
22382242
DEPEND[html/man3/SSL_CTX_config.html]=man3/SSL_CTX_config.pod
22392243
GENERATE[html/man3/SSL_CTX_config.html]=man3/SSL_CTX_config.pod
22402244
DEPEND[man/man3/SSL_CTX_config.3]=man3/SSL_CTX_config.pod
@@ -3604,6 +3608,7 @@ html/man3/SSL_CONF_cmd_argv.html \
36043608
html/man3/SSL_CTX_add1_chain_cert.html \
36053609
html/man3/SSL_CTX_add_extra_chain_cert.html \
36063610
html/man3/SSL_CTX_add_session.html \
3611+
html/man3/SSL_CTX_clean_grease.html \
36073612
html/man3/SSL_CTX_config.html \
36083613
html/man3/SSL_CTX_ctrl.html \
36093614
html/man3/SSL_CTX_dane_enable.html \
@@ -4276,6 +4281,7 @@ man/man3/SSL_CONF_cmd_argv.3 \
42764281
man/man3/SSL_CTX_add1_chain_cert.3 \
42774282
man/man3/SSL_CTX_add_extra_chain_cert.3 \
42784283
man/man3/SSL_CTX_add_session.3 \
4284+
man/man3/SSL_CTX_clean_grease.3 \
42794285
man/man3/SSL_CTX_config.3 \
42804286
man/man3/SSL_CTX_ctrl.3 \
42814287
man/man3/SSL_CTX_dane_enable.3 \

doc/man3/SSL_CTX_clean_grease.pod

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
=pod
2+
3+
=head1 NAME
4+
5+
SSL_CTX_add_grease_to_extension,
6+
SSL_CTX_add_grease_to_ciphers,
7+
SSL_CTX_add_grease_extension,
8+
SSL_CTX_clean_grease - Add GREASE (RFC8701) to the TLS handshake
9+
10+
=head1 SYNOPSIS
11+
12+
#include <openssl/ssl.h>
13+
14+
int SSL_CTX_add_grease_to_extension(SSL_CTX *ctx, unsigned int extension,
15+
unsigned int value);
16+
int SSL_CTX_add_grease_to_ciphers(SSL_CTX *ctx, unsigned int value);
17+
int SSL_CTX_add_grease_extension(SSL_CTX *ctx, unsigned int extension,
18+
unsigned char* value, size_t length);
19+
void SSL_CTX_clean_grease(SSL_CTX *ctx);
20+
21+
=head1 DESCRIPTION
22+
23+
SSL_CTX_add_grease_to_extension() adds a GREASE value to a known extension.
24+
If the extension cannot support GREASE values, an error is returned.
25+
These GREASE values only apply to the ClientHello.
26+
27+
SSL_CTX_add_grease_to_ciphers() adds a 16-bit GREASE value to the list of ciphers.
28+
These GREASE values only apply to the ClientHello.
29+
30+
SSL_CTX_add_grease_extension() adds a new GREASE extionsion with the given
31+
payload. If the extension is a known, supported extension, an error is returned.
32+
33+
SSL_CTX_clean_grease() removes GREASE values from the b<ctx>.
34+
35+
=head1 NOTES
36+
37+
GREASE is defined in RFC8701 "Applying Generate Random Extensions And Sustain
38+
Extensibility" to TLS Extensibility. It is a method of adding unsupported
39+
values to extensions and other fields to ensure that breakage (in terms of
40+
protocol or program) does not occur.
41+
42+
These functions store these GREASE values to an SSL_CTX object, which are then
43+
added to the handshake of any SSL object that refereces the SSL_CTX object.
44+
45+
A set of GREASE values are defined as reserved: two-byte values where the lower
46+
nibble of each byte is 0xA. These values may not be used by other RFCs.
47+
This API does not require that only these values are used; any value may be used
48+
as a GREASE value, as long as it doesn't conflict with an existing extension
49+
number.
50+
51+
GREASE values are prepended to the normal extension values.
52+
53+
The SSL_CTX_add_grease_to_extension() function supports the following extensions:
54+
55+
=over 4
56+
57+
=item TLSEXT_TYPE_supported_versions
58+
59+
=item TLSEXT_TYPE_signature_algorithms
60+
61+
=item TLSEXT_TYPE_supported_groups
62+
63+
=item TLSEXT_TYPE_compress_certificate
64+
65+
The above extensions support 16-bit GREASE values.
66+
67+
=item TLSEXT_TYPE_psk_kex_modes
68+
69+
The above extension supports 8-bit GREASE values.
70+
71+
=item TLSEXT_TYPE_key_share
72+
73+
The above extension adds a 16-bit GREASE encoded-point type (lower 16-bits of
74+
b<value>) with the whole contents of b<value> (sizeof(unsigned int)) used as
75+
the encoded-point value.
76+
77+
=back
78+
79+
The SSL_CTX_add_grease_extension() function will add all configuted GREASE
80+
extentions to the following handshake messages:
81+
82+
=over 4
83+
84+
=item Client Hello
85+
86+
This applies only to the client.
87+
88+
=item Server Hello
89+
90+
This applies only to the server.
91+
92+
=item Certificate Request
93+
94+
This applies only to the server when doing TLSv1.3.
95+
96+
=back
97+
98+
GREASE values are not (supposed to be) processed by servers and clients. They
99+
are present to ensure TLS extensibility.
100+
101+
=head1 RETURN VALUES
102+
103+
The SSL_CTX_add_grease_to_extension(), SSL_CTX_add_grease_to_ciphers(),
104+
and SSL_CTX_add_grease_extension() functions
105+
return 1 on success and 0 on failure.
106+
107+
Success means that the GREASE value was stored.
108+
109+
=head1 SEE ALSO
110+
111+
L<SSL_CTX_new(3)>, L<SSL_new(3)>
112+
113+
=head1 HISTORY
114+
115+
The SSL_CTX_add_grease_to_extension(),
116+
SSL_CTX_add_grease_to_ciphers(),
117+
SSL_CTX_add_grease_extension(), and
118+
SSL_CTX_clean_grease() functions were added in Openssl 3.6.
119+
120+
=head1 COPYRIGHT
121+
122+
Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
123+
124+
Licensed under the Apache License 2.0 (the "License"). You may not use
125+
this file except in compliance with the License. You can obtain a copy
126+
in the file LICENSE in the source distribution or at
127+
L<https://www.openssl.org/source/license.html>.
128+
129+
=cut

include/openssl/ssl.h.in

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,6 +2880,14 @@ int SSL_set_quic_tls_transport_params(SSL *s,
28802880

28812881
int SSL_set_quic_tls_early_data_enabled(SSL *s, int enabled);
28822882

2883+
/* RFC 8701 GREASE support */
2884+
2885+
int SSL_CTX_add_grease_to_extension(SSL_CTX *ctx, unsigned int extension, unsigned int value);
2886+
int SSL_CTX_add_grease_to_ciphers(SSL_CTX *ctx, unsigned int value);
2887+
int SSL_CTX_add_grease_extension(SSL_CTX *ctx, unsigned int extension, unsigned char *value,
2888+
size_t length);
2889+
void SSL_CTX_clean_grease(SSL_CTX *ctx);
2890+
28832891
# ifdef __cplusplus
28842892
}
28852893
# endif

ssl/ssl_lib.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4360,6 +4360,7 @@ void SSL_CTX_free(SSL_CTX *a)
43604360

43614361
X509_VERIFY_PARAM_free(a->param);
43624362
dane_ctx_final(&a->dane);
4363+
SSL_CTX_clean_grease(a);
43634364

43644365
/*
43654366
* Free internal session cache. However: the remove_cb() may reference
@@ -8321,3 +8322,108 @@ int SSL_CTX_get0_server_cert_type(const SSL_CTX *ctx, unsigned char **t, size_t
83218322
*len = ctx->server_cert_type_len;
83228323
return 1;
83238324
}
8325+
8326+
static int ossl_grease_common(SSL_CTX *ctx, unsigned int extension,
8327+
int new_extension,
8328+
unsigned int value,
8329+
unsigned char *data, size_t length)
8330+
{
8331+
OSSL_GREASE *g;
8332+
OSSL_GREASE *newg = NULL;
8333+
int ret = 0;
8334+
8335+
if (!CRYPTO_THREAD_write_lock(ctx->lock))
8336+
return 0;
8337+
/* Duplicate check */
8338+
for (g = ctx->grease; g != NULL; g = g->next) {
8339+
if (g->extension == extension && g->value == value) {
8340+
/* don't care about the data contents */
8341+
goto err;
8342+
}
8343+
}
8344+
if ((newg = OPENSSL_zalloc(sizeof(*newg))) == NULL)
8345+
goto err;
8346+
newg->extension = extension;
8347+
newg->value = value;
8348+
newg->new_extension = new_extension;
8349+
if (data != NULL) {
8350+
if ((newg->data = OPENSSL_memdup(data, length)) == NULL)
8351+
goto err;
8352+
newg->length = length;
8353+
}
8354+
newg->next = ctx->grease;
8355+
ctx->grease = newg;
8356+
ret = 1;
8357+
newg = NULL;
8358+
8359+
err:
8360+
if (newg != NULL) {
8361+
OPENSSL_free(newg->data);
8362+
OPENSSL_free(newg);
8363+
}
8364+
CRYPTO_THREAD_unlock(ctx->lock);
8365+
return ret;
8366+
}
8367+
8368+
int SSL_CTX_add_grease_to_extension(SSL_CTX *ctx, unsigned int extension, unsigned int value)
8369+
{
8370+
/* Add an entry for a known (supported) extension */
8371+
if (!SSL_extension_supported(extension))
8372+
return 0;
8373+
8374+
/* Not all extensions support grease though */
8375+
switch (extension) {
8376+
case TLSEXT_TYPE_supported_versions:
8377+
case TLSEXT_TYPE_signature_algorithms:
8378+
case TLSEXT_TYPE_supported_groups:
8379+
case TLSEXT_TYPE_psk_kex_modes:
8380+
case TLSEXT_TYPE_key_share:
8381+
case TLSEXT_TYPE_compress_certificate:
8382+
case TLSEXT_TYPE_server_cert_type:
8383+
case TLSEXT_TYPE_client_cert_type:
8384+
break;
8385+
default:
8386+
return 0;
8387+
}
8388+
8389+
/* Does not apply to servers */
8390+
return ossl_grease_common(ctx, extension, 0, value, NULL, 0);
8391+
}
8392+
8393+
int SSL_CTX_add_grease_to_ciphers(SSL_CTX *ctx, unsigned int cipher)
8394+
{
8395+
/* Add an entry for ciphers - not an extension! */
8396+
/* Does not apply to servers */
8397+
return ossl_grease_common(ctx, OSSL_GREASE_CIPHER, 0, cipher, NULL, 0);
8398+
}
8399+
8400+
/* Could this just be done via a custom extension instead, or is this easier? */
8401+
int SSL_CTX_add_grease_extension(SSL_CTX *ctx, unsigned int extension, unsigned char *value,
8402+
size_t length)
8403+
{
8404+
/* Add an entry for a (new) extension */
8405+
/* Invalid extension */
8406+
if (extension > 0xFFFF)
8407+
return 0;
8408+
/* This is the real limit, but not really a practical one */
8409+
if (length > 0xFFFF)
8410+
return 0;
8411+
/* Check to make sure it's not a pre-existing/supported extension */
8412+
if (SSL_extension_supported(extension))
8413+
return 0;
8414+
8415+
return ossl_grease_common(ctx, extension, 1, 0, value, length);
8416+
}
8417+
8418+
void SSL_CTX_clean_grease(SSL_CTX *ctx)
8419+
{
8420+
/* Delete all GREASE entries */
8421+
OSSL_GREASE *g;
8422+
8423+
while (ctx->grease) {
8424+
g = ctx->grease;
8425+
ctx->grease = g->next;
8426+
OPENSSL_free(g->data);
8427+
OPENSSL_free(g);
8428+
}
8429+
}

ssl/ssl_local.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,8 @@ struct ssl_ctx_st {
12161216
# ifndef OPENSSL_NO_QLOG
12171217
char *qlog_title; /* Session title for qlog */
12181218
# endif
1219+
1220+
struct ossl_grease_st *grease;
12191221
};
12201222

12211223
typedef struct ossl_quic_tls_callbacks_st {
@@ -3171,4 +3173,17 @@ long ossl_ctrl_internal(SSL *s, int cmd, long larg, void *parg, int no_quic);
31713173
SSL_DOMAIN_FLAG_BLOCKING | \
31723174
SSL_DOMAIN_FLAG_LEGACY_BLOCKING)
31733175

3176+
/* EXTENSION < 0xFFFF is the actual extension value */
3177+
# define OSSL_GREASE_CIPHER 0x10001
3178+
3179+
struct ossl_grease_st {
3180+
struct ossl_grease_st *next;
3181+
unsigned int extension;
3182+
int new_extension;
3183+
unsigned int value;
3184+
unsigned char *data;
3185+
size_t length;
3186+
};
3187+
typedef struct ossl_grease_st OSSL_GREASE;
3188+
31743189
#endif

ssl/statem/extensions.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,25 @@ int tls_construct_extensions(SSL_CONNECTION *s, WPACKET *pkt,
890890
/* SSLfatal() already called */
891891
return 0;
892892
}
893+
/* Add GREASE extensions */
894+
if ((context & (SSL_EXT_CLIENT_HELLO
895+
| SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
896+
| SSL_EXT_TLS1_2_SERVER_HELLO
897+
| SSL_EXT_TLS1_3_SERVER_HELLO
898+
| SSL_EXT_TLS1_3_NEW_SESSION_TICKET)) != 0) {
899+
SSL_CTX *ctx = SSL_CONNECTION_GET_CTX(s);
900+
OSSL_GREASE *g;
901+
902+
for (g = ctx->grease; g != NULL; g = g->next) {
903+
if (g->new_extension) {
904+
if (!WPACKET_put_bytes_u16(pkt, g->extension)
905+
|| !WPACKET_sub_memcpy_u16(pkt, g->data, g->length)) {
906+
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
907+
return 0;
908+
}
909+
}
910+
}
911+
}
893912

894913
for (i = 0, thisexd = ext_defs; i < OSSL_NELEM(ext_defs); i++, thisexd++) {
895914
EXT_RETURN (*construct)(SSL_CONNECTION *s, WPACKET *pkt,
@@ -1813,7 +1832,8 @@ static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET
18131832

18141833
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_compress_certificate)
18151834
|| !WPACKET_start_sub_packet_u16(pkt)
1816-
|| !WPACKET_start_sub_packet_u8(pkt))
1835+
|| !WPACKET_start_sub_packet_u8(pkt)
1836+
|| !tls_add_grease16_to_packet(sc, pkt, TLSEXT_TYPE_compress_certificate))
18171837
goto err;
18181838

18191839
for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) {

0 commit comments

Comments
 (0)