Skip to content

Commit

Permalink
move ECH specific padding into library
Browse files Browse the repository at this point in the history
  • Loading branch information
sftcd committed Jun 4, 2024
1 parent fafa0b4 commit 060c7c3
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 88 deletions.
90 changes: 6 additions & 84 deletions apps/s_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,6 @@ typedef unsigned int u_int;
#endif
#include "internal/sockets.h"

#ifndef OPENSSL_NO_ECH
/*
* 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.
*/
# define ECH_CERTSPECIFIC_MIN 1808
# define ECH_CERTVERSPECIFIC_MIN 480
# define ECH_ENCEXTSPECIFIC_MIN 32
#endif

static int not_resumable_sess_cb(SSL *s, int is_forward_secure);
static int sv_body(int s, int stype, int prot, unsigned char *context);
static int www_body(int s, int stype, int prot, unsigned char *context);
Expand All @@ -106,60 +93,6 @@ static unsigned int ech_print_cb(SSL *s, const char *str);
static size_t ech_trace_cb(const char *buf, size_t cnt,
int category, int cmd, void *vdata);
# endif

/* ECH padding size info, var of this type is passed via callback */
typedef struct {
/* Certificate messages to be a multiple of this size */
size_t certpad;
/* CertificateVerify messages to be a multiple of this size */
size_t certverifypad;
/* EncryptedExtensions are padded to be a multiple of this size */
size_t eepad;
} ech_padding_sizes;

/* passed as an argument to callback */
static ech_padding_sizes *ech_ps=NULL;

/*
* @brief pad Certificate and CertificateVerify messages
* @param s is the SSL connection
* @param len is the plaintext length before padding
* @param arg is a pointer to an esni_padding_sizes struct
* @return is the number of bytes of padding to add to the plaintext
*
* This is passed to SSL_CTX_set_record_padding_callback
* and pads the Certificate, CertificateVerify and
* EncryptedExtensions handshake messages to a size derived
* from the argument arg
*/
static size_t ech_padding_cb(SSL *s, int type, size_t len, void *arg)
{
ech_padding_sizes *ps = (ech_padding_sizes *)arg;
int state = SSL_get_state(s);

if (state == TLS_ST_SW_CERT) {
int newlen = ps->certpad - (len % ps->certpad) - 16;

if (newlen < 0)
newlen += ps->certpad;
return (newlen > 0 ? newlen : 0);
}
if (state == TLS_ST_SW_CERT_VRFY) {
int newlen = ps->certverifypad - (len % ps->certverifypad) - 16;

if (newlen < 0)
newlen += ps->certverifypad;
return (newlen > 0 ? newlen : 0);
}
if (state == TLS_ST_SW_ENCRYPTED_EXTENSIONS) {
int newlen = ps->eepad - (len % ps->eepad) - 16;

if (newlen < 0)
newlen += ps->eepad;
return (newlen > 0 ? newlen : 0);
}
return 0;
}
#endif

static const int bufsize = 16 * 1024;
Expand Down Expand Up @@ -2538,14 +2471,8 @@ int s_server_main(int argc, char *argv[])
/*
* Set padding sizes
*/
if (echspecificpad != 0) {
ech_ps = OPENSSL_malloc(sizeof(ech_padding_sizes));
ech_ps->certpad = ECH_CERTSPECIFIC_MIN;
ech_ps->certverifypad = ECH_CERTVERSPECIFIC_MIN ;
ech_ps->eepad = ECH_ENCEXTSPECIFIC_MIN ;
SSL_CTX_set_record_padding_callback_arg(ctx, (void *)ech_ps);
SSL_CTX_set_record_padding_callback(ctx, ech_padding_cb);
}
if (echspecificpad != 0)
SSL_CTX_set_options(ctx, SSL_OP_ECH_SPECIFIC_PADDING);
#endif

if (s_cert2) {
Expand All @@ -2556,16 +2483,12 @@ int s_server_main(int argc, char *argv[])
}

#ifndef OPENSSL_NO_ECH
if (echtrialdecrypt != 0) {
if (echtrialdecrypt != 0)
SSL_CTX_set_options(ctx2, SSL_OP_ECH_TRIALDECRYPT);
}
if (echgrease_rc != 0) {
if (echgrease_rc != 0)
SSL_CTX_set_options(ctx, SSL_OP_ECH_GREASE_RETRY_CONFIG);
}
if (echspecificpad != 0) {
SSL_CTX_set_record_padding_callback_arg(ctx2, (void *)ech_ps);
SSL_CTX_set_record_padding_callback(ctx2, ech_padding_cb);
}
if (echspecificpad != 0)
SSL_CTX_set_options(ctx2, SSL_OP_ECH_SPECIFIC_PADDING);
#endif
}

Expand Down Expand Up @@ -2941,7 +2864,6 @@ int s_server_main(int argc, char *argv[])
BIO_meth_free(methods_ebcdic);
#endif
#ifndef OPENSSL_NO_ECH
OPENSSL_free(ech_ps);
#endif
return ret;
}
Expand Down
65 changes: 61 additions & 4 deletions doc/designs/ech-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,60 @@ ChangeCipherSuite or Early data), then the caller is responsible for
disentangling those, and for assembling a new flight containing the inner
ClientHello.

### 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:

```c
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 | 480 | 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.
```c
/*
* 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;
int SSL_CTX_ech_set_pad_sizes(SSL_CTX *ctx, OSSL_ECH_PAD_SIZES *sizes);
int SSL_ech_set_pad_sizes(SSL *s, OSSL_ECH_PAD_SIZES *sizes);
```

Client-side APIs
----------------

Expand Down Expand Up @@ -422,17 +476,20 @@ The following options are defined for ECH and may be set via
```c
/* set this to tell client to emit greased ECH values when not doing
* "real" ECH */
#define SSL_OP_ECH_GREASE SSL_OP_BIT(34)
#define SSL_OP_ECH_GREASE SSL_OP_BIT(36)
/* If this is set then the server side will attempt trial decryption */
/* of ECHs even if there is no matching record_digest. That's a bit */
/* inefficient, but more privacy friendly */
#define SSL_OP_ECH_TRIALDECRYPT SSL_OP_BIT(35)
#define SSL_OP_ECH_TRIALDECRYPT SSL_OP_BIT(37)
/* If set, clients will ignore the supplied ECH config_id and replace
* that with a random value */
#define SSL_OP_ECH_IGNORE_CID SSL_OP_BIT(36)
#define SSL_OP_ECH_IGNORE_CID SSL_OP_BIT(38)
/* If set, servers will add GREASEy ECHConfig values to those sent
* in retry_configs */
#define SSL_OP_ECH_GREASE_RETRY_CONFIG SSL_OP_BIT(37)
#define SSL_OP_ECH_GREASE_RETRY_CONFIG SSL_OP_BIT(39)
/* If set, servers will add ECH-specific padding to Certificate,
* CertificateVerify and EncryptedExtensions messages */
#define SSL_OP_ECH_SPECIFIC_PADDING SSL_OP_BIT(40)
```

Build Options
Expand Down
36 changes: 36 additions & 0 deletions include/openssl/ech.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,39 @@ typedef struct ossl_ech_info_st {
char *echconfig; /* a JSON-like version of the associated ECHConfig */
} OSSL_ECH_INFO;

/*
* Default padding minima and multiples
* 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);
*/
# define OSSL_ECH_CERTPAD_MIN 1792
# define OSSL_ECH_CERTVERPAD_MIN 480
# define OSSL_ECH_ENCEXTPAD_MIN 32
# define OSSL_ECH_CERTPAD_UNIT 128
# define OSSL_ECH_CERTVERPAD_UNIT 16
# define OSSL_ECH_ENCEXTPAD_UNIT 16

/*
* 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;

/* Values for the for_retry inputs */
# define SSL_ECH_USE_FOR_RETRY 1
# define SSL_ECH_NOT_FOR_RETRY 0
Expand Down Expand Up @@ -116,6 +149,7 @@ int SSL_ech_set_grease_type(SSL *s, uint16_t type);

typedef unsigned int (*SSL_ech_cb_func)(SSL *s, const char *str);
void SSL_ech_set_callback(SSL *s, SSL_ech_cb_func f);
int SSL_ech_set_pad_sizes(SSL *s, OSSL_ECH_PAD_SIZES *sizes);

int SSL_ech_get_retry_config(SSL *s, unsigned char **ec, size_t *eclen);

Expand All @@ -140,6 +174,8 @@ int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
unsigned char *inner_ch, size_t *inner_len,
unsigned char **hrrtok, size_t *toklen);
void SSL_CTX_ech_set_callback(SSL_CTX *ctx, SSL_ech_cb_func f);
int SSL_CTX_ech_set_pad_sizes(SSL_CTX *ctx, OSSL_ECH_PAD_SIZES *sizes);


/* Misc API calls */
int OSSL_ech_make_echconfig(unsigned char *echconfig, size_t *echconfiglen,
Expand Down
3 changes: 3 additions & 0 deletions include/openssl/ssl.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,9 @@ typedef int (*SSL_async_callback_fn)(SSL *s, void *arg);
* in retry_configs.
*/
# define SSL_OP_ECH_GREASE_RETRY_CONFIG SSL_OP_BIT(39)
/* If set, servers will add ECH-specific padding to Certificate,
* CertificateVerify and EncryptedExtensions messages */
# define SSL_OP_ECH_SPECIFIC_PADDING SSL_OP_BIT(40)
#endif


Expand Down
18 changes: 18 additions & 0 deletions ssl/ech.c
Original file line number Diff line number Diff line change
Expand Up @@ -4698,6 +4698,24 @@ int OSSL_ECH_INFO_print(BIO *out, OSSL_ECH_INFO *se, int count)
return 1;
}

int SSL_CTX_ech_set_pad_sizes(SSL_CTX *ctx, OSSL_ECH_PAD_SIZES *sizes)
{
if (ctx == NULL || sizes == NULL)
return 0;
ctx->ext.pad_sizes = *sizes;
return 1;
}

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)
return 0;
s->ext.ech.pad_sizes = *sizes;
return 1;
}

int SSL_ech_set1_echconfig(SSL *ssl, const unsigned char *val, size_t len)
{
SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl);
Expand Down
1 change: 1 addition & 0 deletions ssl/ech_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ typedef struct ssl_connection_ech_st {
SSL_ECH *cfgs; /* array of configured ECH configurations */
int ncfgs; /* number of elements in array */
SSL_ech_cb_func cb; /* callback function for when ECH "done" */
OSSL_ECH_PAD_SIZES pad_sizes; /* padding sizes */
} SSL_CONNECTION_ECH;

/**
Expand Down
53 changes: 53 additions & 0 deletions ssl/record/methods/tls_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1830,6 +1830,59 @@ 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
Loading

0 comments on commit 060c7c3

Please sign in to comment.