From c649d10d3fee9fe22e4ae6bdf7f8117b91b92b03 Mon Sep 17 00:00:00 2001 From: Todd Short Date: Wed, 5 Apr 2017 12:35:25 -0400 Subject: [PATCH] TLS1.3 Padding Add padding callback for application control Standard block_size callback Documentation and tests included Configuration file/s_client/s_srver option Reviewed-by: Tim Hudson Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/3130) --- apps/apps.h | 6 +- doc/man3/SSL_CONF_cmd.pod | 12 +++ .../SSL_CTX_set_record_padding_callback.pod | 96 +++++++++++++++++++ include/openssl/ssl.h | 15 +++ ssl/packet.c | 15 +++ ssl/packet_locl.h | 3 + ssl/record/rec_layer_s3.c | 37 ++++++- ssl/ssl_conf.c | 22 ++++- ssl/ssl_lib.c | 61 ++++++++++++ ssl/ssl_locl.h | 10 ++ test/recipes/80-test_ssl_new.t | 3 +- test/ssl-tests/24-padding.conf | 34 +++++++ test/ssl-tests/24-padding.conf.in | 25 +++++ util/libssl.num | 8 ++ 14 files changed, 340 insertions(+), 7 deletions(-) create mode 100644 doc/man3/SSL_CTX_set_record_padding_callback.pod create mode 100644 test/ssl-tests/24-padding.conf create mode 100644 test/ssl-tests/24-padding.conf.in diff --git a/apps/apps.h b/apps/apps.h index a8de2dc0aa..de50de5907 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -214,7 +214,7 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate, OPT_S_SERVERPREF, OPT_S_LEGACYRENEG, OPT_S_LEGACYCONN, \ OPT_S_ONRESUMP, OPT_S_NOLEGACYCONN, OPT_S_STRICT, OPT_S_SIGALGS, \ OPT_S_CLIENTSIGALGS, OPT_S_CURVES, OPT_S_NAMEDCURVE, OPT_S_CIPHER, \ - OPT_S_DHPARAM, OPT_S_DEBUGBROKE, OPT_S_COMP, \ + OPT_S_DHPARAM, OPT_S_RECORD_PADDING, OPT_S_DEBUGBROKE, OPT_S_COMP, \ OPT_S__LAST # define OPT_S_OPTIONS \ @@ -251,9 +251,12 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate, {"cipher", OPT_S_CIPHER, 's', "Specify cipher list to be used"}, \ {"dhparam", OPT_S_DHPARAM, '<', \ "DH parameter file to use, in cert file if not specified"}, \ + {"record_padding", OPT_S_RECORD_PADDING, 's', \ + "Block size to pad TLS 1.3 records to."}, \ {"debug_broken_protocol", OPT_S_DEBUGBROKE, '-', \ "Perform all sorts of protocol violations for testing purposes"} + # define OPT_S_CASES \ OPT_S__FIRST: case OPT_S__LAST: break; \ case OPT_S_NOSSL3: \ @@ -277,6 +280,7 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate, case OPT_S_NAMEDCURVE: \ case OPT_S_CIPHER: \ case OPT_S_DHPARAM: \ + case OPT_S_RECORD_PADDING: \ case OPT_S_DEBUGBROKE #define IS_NO_PROT_FLAG(o) \ diff --git a/doc/man3/SSL_CONF_cmd.pod b/doc/man3/SSL_CONF_cmd.pod index f5c6576ed0..efd766d7db 100644 --- a/doc/man3/SSL_CONF_cmd.pod +++ b/doc/man3/SSL_CONF_cmd.pod @@ -110,6 +110,12 @@ Attempts to use the file B as the set of temporary DH parameters for the appropriate context. This option is only supported if certificate operations are permitted. +=item B<-record_padding> + +Attempts to pad TLS 1.3 records so that they are a multiple of B in +length on send. A B of 0 or 1 turns off padding. Otherwise, the +B must be >1 or <=16384. + =item B<-min_protocol>, B<-max_protocol> Sets the minimum and maximum supported protocol. @@ -236,6 +242,12 @@ Attempts to use the file B as the set of temporary DH parameters for the appropriate context. This option is only supported if certificate operations are permitted. +=item B + +Attempts to pad TLS 1.3 records so that they are a multiple of B in +length on send. A B of 0 or 1 turns off padding. Otherwise, the +B must be >1 or <=16384. + =item B This sets the supported signature algorithms for TLS v1.2. For clients this diff --git a/doc/man3/SSL_CTX_set_record_padding_callback.pod b/doc/man3/SSL_CTX_set_record_padding_callback.pod new file mode 100644 index 0000000000..9eea2bc9df --- /dev/null +++ b/doc/man3/SSL_CTX_set_record_padding_callback.pod @@ -0,0 +1,96 @@ +=pod + +=head1 NAME + +SSL_CTX_set_record_padding_callback, +SSL_set_record_padding_callback, +SSL_CTX_set_record_padding_callback_arg, +SSL_set_record_padding_callback_arg, +SSL_CTX_get_record_padding_callback_arg, +SSL_get_record_padding_callback_arg, +SSL_CTX_set_block_padding, +SSL_set_block_padding - install callback to specify TLS 1.3 record padding + +=head1 SYNOPSIS + + #include + + void SSL_CTX_set_record_padding_callback(SSL_CTX *ctx, size_t (*cb)(SSL *s, int type, size_t len, void *arg)); + void SSL_set_record_padding_callback(SSL *ssl, size_t (*cb)(SSL *s, int type, size_t len, void *arg)); + + void SSL_CTX_set_record_padding_callback_arg(SSL_CTX *ctx, void *arg); + void *SSL_CTX_get_record_padding_callback_arg(SSL_CTX *ctx); + + void SSL_set_record_padding_callback_arg(SSL *ssl, void *arg); + void *SSL_get_record_padding_callback_arg(SSL *ssl); + + int SSL_CTX_set_block_padding(SSL_CTX *ctx, size_t block_size); + int SSL_set_block_padding(SSL *ssl, size_t block_size); + +=head1 DESCRIPTION + +SSL_CTX_set_record_padding_callback() or SSL_set_record_padding_callback() +can be used to assign a callback function I to specify the padding +for TLS 1.3 records. The value set in B is copied to a new SSL by SSL_new(). + +SSL_CTX_set_record_padding_callback_arg() and SSL_set_record_padding_callback_arg() +assign a value B that is passed to the callback when it is invoked. The value +set in B is copied to a new SSL by SSL_new(). + +SSL_CTX_get_record_padding_callback_arg() and SSL_get_record_padding_callback_arg() +retrieve the B value that is passed to the callback. + +SSL_CTX_set_block_padding() and SSL_set_block_padding() pads the record to a multiple +of the B. A B of 0 or 1 disables block padding. The limit of +B is SSL3_RT_MAX_PLAIN_LENGTH. + +The callback is invoked for every record before encryption. +The B parameter is the TLS record type that is being processed; may be +one of SSL3_RT_APPLICATION_DATA, SSL3_RT_HANDSHAKE, or SSL3_RT_ALERT. +The B parameter is the current plaintext length of the record before encryption. +The B parameter is the value set via SSL_CTX_set_record_padding_callback_arg() +or SSL_set_record_padding_callback_arg(). + +=head1 RETURN VALUES + +The SSL_CTX_get_record_padding_callback_arg() and SSL_get_record_padding_callback_arg() +functions return the B value assignd in the corresponding set functions. + +The SSL_CTX_set_block_padding() and SSL_set_block_padding() functions return 1 on success +or 0 if B is too large. + +The B returns the number of padding bytes to add to the record. A return of 0 +indicates no padding will be added. A return value that causes the record to +exceed the maximum record size (SSL3_RT_MAX_PLAIN_LENGTH) will pad out to the +maximum record size. + +=head1 NOTES + +The default behavior is to add no padding to the record. + +A user-supplied padding callback function will override the behavior set by +SSL_set_block_padding() or SSL_CTX_set_block_padding(). Setting the user-supplied +callback to NULL will restore the configured block padding behavior. + +These functions only apply to TLS 1.3 records being written. + +Padding bytes are not added in constant-time. + +=head1 SEE ALSO + +L, L + +=head1 HISTORY + +The record padding API was added for TLS 1.3 support in OpenSSL 1.1.1. + +=head1 COPYRIGHT + +Copyright 2017 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the OpenSSL license (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 +L. + +=cut diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index d533a0c881..b1da6c5a69 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1952,6 +1952,21 @@ void SSL_set_not_resumable_session_callback(SSL *ssl, int (*cb) (SSL *ssl, int is_forward_secure)); + +void SSL_CTX_set_record_padding_callback(SSL_CTX *ctx, + size_t (*cb) (SSL *ssl, int type, + size_t len, void *arg)); +void SSL_CTX_set_record_padding_callback_arg(SSL_CTX *ctx, void *arg); +void *SSL_CTX_get_record_padding_callback_arg(SSL_CTX *ctx); +int SSL_CTX_set_block_padding(SSL_CTX *ctx, size_t block_size); + +void SSL_set_record_padding_callback(SSL *ssl, + size_t (*cb) (SSL *ssl, int type, + size_t len, void *arg)); +void SSL_set_record_padding_callback_arg(SSL *ssl, void *arg); +void *SSL_get_record_padding_callback_arg(SSL *ssl); +int SSL_set_block_padding(SSL *ssl, size_t block_size); + # if OPENSSL_API_COMPAT < 0x10100000L # define SSL_cache_hit(s) SSL_session_reused(s) # endif diff --git a/ssl/packet.c b/ssl/packet.c index 3479f1fed8..d081f557e7 100644 --- a/ssl/packet.c +++ b/ssl/packet.c @@ -350,6 +350,21 @@ int WPACKET_set_max_size(WPACKET *pkt, size_t maxsize) return 1; } +int WPACKET_memset(WPACKET *pkt, int ch, size_t len) +{ + unsigned char *dest; + + if (len == 0) + return 1; + + if (!WPACKET_allocate_bytes(pkt, len, &dest)) + return 0; + + memset(dest, ch, len); + + return 1; +} + int WPACKET_memcpy(WPACKET *pkt, const void *src, size_t len) { unsigned char *dest; diff --git a/ssl/packet_locl.h b/ssl/packet_locl.h index 67b49994f7..8e553e62b5 100644 --- a/ssl/packet_locl.h +++ b/ssl/packet_locl.h @@ -833,6 +833,9 @@ int WPACKET_set_max_size(WPACKET *pkt, size_t maxsize); /* Copy |len| bytes of data from |*src| into the WPACKET. */ int WPACKET_memcpy(WPACKET *pkt, const void *src, size_t len); +/* Set |len| bytes of data to |ch| into the WPACKET. */ +int WPACKET_memset(WPACKET *pkt, int ch, size_t len); + /* * Copy |len| bytes of data from |*src| into the WPACKET and prefix with its * length (consuming |lenbytes| of data for the length). Don't call this diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index 14c6778ae6..43c4a949d7 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -860,15 +860,44 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, } if (SSL_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) { + size_t padding = 0; + if (!WPACKET_put_bytes_u8(thispkt, type)) { SSLerr(SSL_F_DO_SSL3_WRITE, ERR_R_INTERNAL_ERROR); goto err; } SSL3_RECORD_add_length(thiswr, 1); - /* - * TODO(TLS1.3): Padding goes here. Do we need an API to add this? - * For now, use no padding - */ + + /* Add TLS1.3 padding */ + if (s->record_padding_cb != NULL) { + size_t rlen = SSL3_RECORD_get_length(thiswr); + + padding = s->record_padding_cb(s, type, rlen, s->record_padding_arg); + /* do not allow the record to exceed max plaintext length */ + if (padding > (SSL3_RT_MAX_PLAIN_LENGTH - rlen)) + padding = SSL3_RT_MAX_PLAIN_LENGTH - rlen; + } else if (s->block_padding > 0) { + size_t mask = s->block_padding - 1; + size_t remainder; + + /* optimize for power of 2 */ + if ((s->block_padding & mask) == 0) + remainder = SSL3_RECORD_get_length(thiswr) & mask; + else + remainder = SSL3_RECORD_get_length(thiswr) % s->block_padding; + /* don't want to add a block of padding if we don't have to */ + if (remainder == 0) + padding = 0; + else + padding = s->block_padding - remainder; + } + if (padding > 0) { + if (!WPACKET_memset(thispkt, 0, padding)) { + SSLerr(SSL_F_DO_SSL3_WRITE, ERR_R_INTERNAL_ERROR); + goto err; + } + SSL3_RECORD_add_length(thiswr, padding); + } } /* diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c index 4b4619279e..484bb61feb 100644 --- a/ssl/ssl_conf.c +++ b/ssl/ssl_conf.c @@ -520,6 +520,25 @@ static int cmd_DHParameters(SSL_CONF_CTX *cctx, const char *value) return rv > 0; } #endif + +static int cmd_RecordPadding(SSL_CONF_CTX *cctx, const char *value) +{ + int rv = 0; + int block_size = atoi(value); + + /* + * All we care about is a non-negative value, + * the setters check the range + */ + if (block_size >= 0) { + if (cctx->ctx) + rv = SSL_CTX_set_block_padding(cctx->ctx, block_size); + if (cctx->ssl) + rv = SSL_set_block_padding(cctx->ssl, block_size); + } + return rv; +} + typedef struct { int (*cmd) (SSL_CONF_CTX *cctx, const char *value); const char *str_file; @@ -598,8 +617,9 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = { #ifndef OPENSSL_NO_DH SSL_CONF_CMD(DHParameters, "dhparam", SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CERTIFICATE, - SSL_CONF_TYPE_FILE) + SSL_CONF_TYPE_FILE), #endif + SSL_CONF_CMD_STRING(RecordPadding, "record_padding", 0) }; /* Supported switches: must match order of switches in ssl_conf_cmds */ diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index c59aa847e4..d1a1f027d9 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -571,6 +571,9 @@ SSL *SSL_new(SSL_CTX *ctx) s->msg_callback_arg = ctx->msg_callback_arg; s->verify_mode = ctx->verify_mode; s->not_resumable_session_cb = ctx->not_resumable_session_cb; + s->record_padding_cb = ctx->record_padding_cb; + s->record_padding_arg = ctx->record_padding_arg; + s->block_padding = ctx->block_padding; s->sid_ctx_length = ctx->sid_ctx_length; OPENSSL_assert(s->sid_ctx_length <= sizeof s->sid_ctx); memcpy(&s->sid_ctx, &ctx->sid_ctx, sizeof(s->sid_ctx)); @@ -3889,6 +3892,64 @@ void SSL_set_not_resumable_session_callback(SSL *ssl, (void (*)(void))cb); } +void SSL_CTX_set_record_padding_callback(SSL_CTX *ctx, + size_t (*cb) (SSL *ssl, int type, + size_t len, void *arg)) +{ + ctx->record_padding_cb = cb; +} + +void SSL_CTX_set_record_padding_callback_arg(SSL_CTX *ctx, void *arg) +{ + ctx->record_padding_arg = arg; +} + +void *SSL_CTX_get_record_padding_callback_arg(SSL_CTX *ctx) +{ + return ctx->record_padding_arg; +} + +int SSL_CTX_set_block_padding(SSL_CTX *ctx, size_t block_size) +{ + /* block size of 0 or 1 is basically no padding */ + if (block_size == 1) + ctx->block_padding = 0; + else if (block_size <= SSL3_RT_MAX_PLAIN_LENGTH) + ctx->block_padding = block_size; + else + return 0; + return 1; +} + +void SSL_set_record_padding_callback(SSL *ssl, + size_t (*cb) (SSL *ssl, int type, + size_t len, void *arg)) +{ + ssl->record_padding_cb = cb; +} + +void SSL_set_record_padding_callback_arg(SSL *ssl, void *arg) +{ + ssl->record_padding_arg = arg; +} + +void *SSL_get_record_padding_callback_arg(SSL *ssl) +{ + return ssl->record_padding_arg; +} + +int SSL_set_block_padding(SSL *ssl, size_t block_size) +{ + /* block size of 0 or 1 is basically no padding */ + if (block_size == 1) + ssl->block_padding = 0; + else if (block_size <= SSL3_RT_MAX_PLAIN_LENGTH) + ssl->block_padding = block_size; + else + return 0; + return 1; +} + /* * Allocates new EVP_MD_CTX and sets pointer to it into given pointer * variable, freeing EVP_MD_CTX previously stored in that variable, if any. diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 15065c7d98..1d3a207c78 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -973,6 +973,11 @@ struct ssl_ctx_st { /* The maximum number of bytes that can be sent as early data */ uint32_t max_early_data; + + /* TLS1.3 padding callback */ + size_t (*record_padding_cb)(SSL *s, int type, size_t len, void *arg); + void *record_padding_arg; + size_t block_padding; }; struct ssl_st { @@ -1289,6 +1294,11 @@ struct ssl_st { */ uint32_t early_data_count; + /* TLS1.3 padding callback */ + size_t (*record_padding_cb)(SSL *s, int type, size_t len, void *arg); + void *record_padding_arg; + size_t block_padding; + CRYPTO_RWLOCK *lock; }; diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t index fbcb46a8fb..100b8528c8 100644 --- a/test/recipes/80-test_ssl_new.t +++ b/test/recipes/80-test_ssl_new.t @@ -29,7 +29,7 @@ map { s/\^// } @conf_files if $^O eq "VMS"; # We hard-code the number of tests to double-check that the globbing above # finds all files as expected. -plan tests => 23; # = scalar @conf_srcs +plan tests => 24; # = scalar @conf_srcs # Some test results depend on the configuration of enabled protocols. We only # verify generated sources in the default configuration. @@ -94,6 +94,7 @@ my %skip = ( "22-compression.conf" => disabled("zlib") || $no_tls, "23-srp.conf" => (disabled("tls1") && disabled ("tls1_1") && disabled("tls1_2")) || disabled("srp"), + "24-padding.conf" => disabled("tls1_3"), ); foreach my $conf (@conf_files) { diff --git a/test/ssl-tests/24-padding.conf b/test/ssl-tests/24-padding.conf new file mode 100644 index 0000000000..3c9f450102 --- /dev/null +++ b/test/ssl-tests/24-padding.conf @@ -0,0 +1,34 @@ +# Generated with generate_ssl_tests.pl + +num_tests = 1 + +test-0 = 0-default +# =========================================================== + +[0-default] +ssl_conf = 0-default-ssl + +[0-default-ssl] +server = 0-default-server +client = 0-default-client + +[0-default-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem +RecordPadding = 64 + +[0-default-client] +CipherString = DEFAULT +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +RecordPadding = 11 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-0] +ExpectedResult = Success + + diff --git a/test/ssl-tests/24-padding.conf.in b/test/ssl-tests/24-padding.conf.in new file mode 100644 index 0000000000..7bf256c8db --- /dev/null +++ b/test/ssl-tests/24-padding.conf.in @@ -0,0 +1,25 @@ +# -*- mode: perl; -*- +# Copyright 2017 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (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 + + +## SSL test configurations + +package ssltests; + +our @tests = ( + { + name => "default", + server => { "RecordPadding" => 64, + "MaxProtocol" => "TLSv1.3", + "MinProtocol" => "TLSv1.3" }, + client => { "RecordPadding" => 11, + "MaxProtocol" => "TLSv1.3", + "MinProtocol" => "TLSv1.3" }, + test => { "ExpectedResult" => "Success" }, + }, +); diff --git a/util/libssl.num b/util/libssl.num index ccaf4bce6f..a17ebbc6ad 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -441,3 +441,11 @@ SSL_CTX_add1_CA_list 441 1_1_1 EXIST::FUNCTION: SSL_CTX_get0_CA_list 442 1_1_1 EXIST::FUNCTION: SSL_CTX_add_custom_ext 443 1_1_1 EXIST::FUNCTION: SSL_SESSION_is_resumable 444 1_1_1 EXIST::FUNCTION: +SSL_CTX_set_record_padding_callback 445 1_1_1 EXIST::FUNCTION: +SSL_set_record_padding_callback 446 1_1_1 EXIST::FUNCTION: +SSL_CTX_set_block_padding 447 1_1_1 EXIST::FUNCTION: +SSL_CTX_get_record_padding_callback_arg 448 1_1_1 EXIST::FUNCTION: +SSL_get_record_padding_callback_arg 449 1_1_1 EXIST::FUNCTION: +SSL_set_block_padding 450 1_1_1 EXIST::FUNCTION: +SSL_set_record_padding_callback_arg 451 1_1_1 EXIST::FUNCTION: +SSL_CTX_set_record_padding_callback_arg 452 1_1_1 EXIST::FUNCTION: -- 2.25.1