Skip to content

Commit

Permalink
ECH specific padding doc, test and tweak
Browse files Browse the repository at this point in the history
  • Loading branch information
sftcd committed Jun 6, 2024
1 parent 060c7c3 commit ddfec93
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 61 deletions.
52 changes: 50 additions & 2 deletions doc/man3/SSL_ech_set1_echconfig.pod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ SSL_CTX_ech_server_flush_keys, SSL_CTX_ech_server_enable_file,
SSL_CTX_ech_server_enable_buffer, SSL_CTX_ech_server_enable_dir,
SSL_CTX_ech_raw_decrypt, SSL_CTX_ech_set_callback,
SSL_CTX_ech_set_outer_alpn_protos, SSL_ech_set_outer_alpn_protos,
OSSL_ech_make_echconfig, OSSL_ech_find_echconfigs
OSSL_ech_make_echconfig, OSSL_ech_find_echconfigs,
SSL_CTX_ech_set_pad_sizes,SSL_ech_set_pad_sizes
- Encrypted ClientHello (ECH) functions

=head1 SYNOPSIS
Expand Down Expand Up @@ -69,6 +70,8 @@ OSSL_ech_make_echconfig, OSSL_ech_find_echconfigs
int OSSL_ech_find_echconfigs(int *num_echs,
unsigned char ***echconfigs, size_t **echlens,
const unsigned char *val, size_t len);
int SSL_ech_set_pad_sizes(SSL *s, OSSL_ECH_PAD_SIZES *sizes);
int SSL_CTX_ech_set_pad_sizes(SSL_CTX *ctx, OSSL_ECH_PAD_SIZES *sizes);

=head1 DESCRIPTION

Expand Down Expand Up @@ -246,7 +249,6 @@ make use of ECH extensions.
OSSL_ech_make_echconfig() is intended for use by applications that generate
ECH configuration data, such as L<openssl-ech(1)>.


=head2 Enabling ECH for servers

In order to enable ECH for servers it is necessary to load one or more ECH
Expand Down Expand Up @@ -320,6 +322,52 @@ succeeds, the I<inner_sni> and I<outer_sni> values will be populated
based on the values seen. (The caller must free those strings using
OPENSSL_free() when done.)

=head2 ECH-specific Padding of server messages

If a web server were to host a set of web sites, one of which had a much longer
name than the others, the size of some TLS handshake server messages could
expose which web site was being accessed. Similarly, if the TLS server
certificate for one web site were significantly larger or smaller than others,
message sizes could reveal which web site was being visited. For these
reasons, we provide a way to enable additional ECH-specific padding of the
Certifiate, CertificateVerify and EncryptedExtensions messages sent from the
server to the client during the handshake.

To enable ECH-specific padding, one makes a call to
``SSL_CTX_set_options(ctx, SSL_OP_ECH_SPECIFIC_PADDING)``

The default padding scheme is to ensure the following sizes for the plaintext
form of these messages:

| ------------------- | ------------ | ------------------- |
| Message | Minimum Size | Size is multiple of |
| ------------------- | ------------ | ------------------- |
| Certificate | 1792 | 128 |
| CertificateVerify | 304 | 16 |
| EncryptedExtensions | 32 | 16 |
| ------------------- | ------------ | ------------------- |

The ciphertext form of these messages, as seen on the network in the record
layer protocol, will usually be 16 octets more, due to the AEAD tag that is
added as part of encryption.

If a server wishes to have finer-grained control of these sizes, then it can
make use of the ``SSL_CTX_ech_set_pad_sizes()`` or ``SSL_ech_set_pad_sizes()``
APIs. Both involve populating an ``OSSL_ECH_PAD_SIZES`` data structure as
described below in the obvious manner.

/*
* Fine-grained ECH-spacific padding controls for a server
*/
typedef struct ossl_ech_pad_sizes_st {
size_t cert_min; /* minimum size */
size_t cert_unit; /* size will be multiple of */
size_t certver_min; /* minimum size */
size_t certver_unit; /* size will be multiple of */
size_t ee_min; /* minimum size */
size_t ee_unit; /* size will be multiple of */
} OSSL_ECH_PAD_SIZES;

=head2 Accessing ECH information

Given multiple ECH public values can be associated with a single I<SSL>
Expand Down
2 changes: 1 addition & 1 deletion include/openssl/ech.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ typedef struct ossl_ech_info_st {
* SSL_CTX_set_options(ctx, SSL_OP_ECH_SPECIFIC_PADDING);
*/
# define OSSL_ECH_CERTPAD_MIN 1792
# define OSSL_ECH_CERTVERPAD_MIN 480
# define OSSL_ECH_CERTVERPAD_MIN 304
# define OSSL_ECH_ENCEXTPAD_MIN 32
# define OSSL_ECH_CERTPAD_UNIT 128
# define OSSL_ECH_CERTVERPAD_UNIT 16
Expand Down
20 changes: 18 additions & 2 deletions ssl/ech.c
Original file line number Diff line number Diff line change
Expand Up @@ -4700,8 +4700,16 @@ int OSSL_ECH_INFO_print(BIO *out, OSSL_ECH_INFO *se, int count)

int SSL_CTX_ech_set_pad_sizes(SSL_CTX *ctx, OSSL_ECH_PAD_SIZES *sizes)
{
if (ctx == NULL || sizes == NULL)
if (ctx == NULL || sizes == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (sizes->cert_min == 0 || sizes->certver_min == 0 || sizes->ee_min == 0
|| sizes->cert_unit == 0 || sizes->certver_unit == 0
|| sizes->ee_unit == 0) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
ctx->ext.pad_sizes = *sizes;
return 1;
}
Expand All @@ -4710,8 +4718,16 @@ int SSL_ech_set_pad_sizes(SSL *ssl, OSSL_ECH_PAD_SIZES *sizes)
{
SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl);

if (s == NULL || sizes == NULL)
if (s == NULL || sizes == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (sizes->cert_min == 0 || sizes->certver_min == 0 || sizes->ee_min == 0
|| sizes->cert_unit == 0 || sizes->certver_unit == 0
|| sizes->ee_unit == 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_LENGTH);
return 0;
}
s->ext.ech.pad_sizes = *sizes;
return 1;
}
Expand Down
53 changes: 53 additions & 0 deletions ssl/record/methods/tls13_meth.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,59 @@ static int tls13_add_record_padding(OSSL_RECORD_LAYER *rl,
}
TLS_RL_RECORD_add_length(thiswr, 1);

#ifndef OPENSSL_NO_ECH
/*
* If we are doing ECH and we want ECH specific padding then
* now is the time to ensure that happens.
* A lack of padding can expose information intended to be hidden via ECH,
* e.g. if only two inner CH SNI values were in live use. In that case we
* pad the Certificate, CertificateVerify and EncryptedExtensions handshake
* messages from the server. These are the minimum lengths to which those
* will be padded in that case.
* We do this before calling any padding callback supplied by the
* application, so they get to see the final sizes.
*/
if ((rl->options & SSL_OP_ECH_SPECIFIC_PADDING) != 0) {
SSL_CONNECTION *s = rl->cbarg;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
int do_pad = 0, echpad = 0, state = SSL_get_state(ssl);
size_t len = 0, min = 0, unit = 0;

if (!WPACKET_get_length(thispkt, &len)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if (s != NULL && s->server == 1 && s->ext.ech.attempted == 1
&& s->ext.ech.success == 1) {
if (state == TLS_ST_SW_CERT) {
min = s->ext.ech.pad_sizes.cert_min;
unit = s->ext.ech.pad_sizes.cert_unit;
do_pad = 1;
}
if (state == TLS_ST_SW_CERT_VRFY) {
min = s->ext.ech.pad_sizes.certver_min;
unit = s->ext.ech.pad_sizes.certver_unit;
do_pad = 1;
}
if (state == TLS_ST_SW_ENCRYPTED_EXTENSIONS) {
min = s->ext.ech.pad_sizes.ee_min;
unit = s->ext.ech.pad_sizes.ee_unit;
do_pad = 1;
}
if (do_pad == 1) {
if (len < min)
echpad = min - len;
echpad += ((len + echpad) % unit ? unit - ((len + echpad) % unit) : 0);
if (echpad > 0 && !WPACKET_memset(thispkt, 0, echpad)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
TLS_RL_RECORD_add_length(thiswr, echpad);
}
}
}
#endif

/* Add TLS1.3 padding */
rlen = TLS_RL_RECORD_get_length(thiswr);
if (rlen < rl->max_frag_len) {
Expand Down
53 changes: 0 additions & 53 deletions ssl/record/methods/tls_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1830,59 +1830,6 @@ int tls_write_records_default(OSSL_RECORD_LAYER *rl,
goto err;
}

#ifndef OPENSSL_NO_ECH
/*
* If we are doing ECH and we want ECH specific padding then
* now is the time to ensure that happens.
* A lack of padding can expose information intended to be hidden via ECH,
* e.g. if only two inner CH SNI values were in live use. In that case we
* pad the Certificate, CertificateVerify and EncryptedExtensions handshake
* messages from the server. These are the minimum lengths to which those
* will be padded in that case.
*/
if ((rl->options & SSL_OP_ECH_SPECIFIC_PADDING) != 0)
{
SSL_CONNECTION *s = rl->cbarg;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
int do_pad = 0, morepad = 0, state = SSL_get_state(ssl);
size_t len = 0, min = 0, unit = 0;

if (!WPACKET_get_length(thispkt, &len)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}

if (s != NULL && s->server == 1 && s->ext.ech.attempted == 1
&& s->ext.ech.success == 1) {

if (state == TLS_ST_SW_CERT) {
min = s->ext.ech.pad_sizes.cert_min;
unit = s->ext.ech.pad_sizes.cert_unit;
do_pad = 1;
}
if (state == TLS_ST_SW_CERT_VRFY) {
min = s->ext.ech.pad_sizes.certver_min;
unit = s->ext.ech.pad_sizes.certver_min;
do_pad = 1;
}
if (state == TLS_ST_SW_ENCRYPTED_EXTENSIONS) {
min = s->ext.ech.pad_sizes.ee_min;
unit = s->ext.ech.pad_sizes.ee_min;
do_pad = 1;
}
if (do_pad == 1) {
if (len < min)
morepad = min - len;
morepad += ((len + morepad) % unit ? unit - ((len + morepad) % unit) : 0);
if (morepad > 0 && !WPACKET_memset(thispkt, 0, morepad)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
}
}
}
#endif

if (!rl->funcs->prepare_for_encryption(rl, mac_size, thispkt, thiswr)) {
/* RLAYERfatal() already called */
goto err;
Expand Down

0 comments on commit ddfec93

Please sign in to comment.