TLS1.3 Padding
authorTodd Short <tshort@akamai.com>
Wed, 5 Apr 2017 16:35:25 +0000 (12:35 -0400)
committerMatt Caswell <matt@openssl.org>
Tue, 2 May 2017 08:44:43 +0000 (09:44 +0100)
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 <tjh@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/3130)

14 files changed:
apps/apps.h
doc/man3/SSL_CONF_cmd.pod
doc/man3/SSL_CTX_set_record_padding_callback.pod [new file with mode: 0644]
include/openssl/ssl.h
ssl/packet.c
ssl/packet_locl.h
ssl/record/rec_layer_s3.c
ssl/ssl_conf.c
ssl/ssl_lib.c
ssl/ssl_locl.h
test/recipes/80-test_ssl_new.t
test/ssl-tests/24-padding.conf [new file with mode: 0644]
test/ssl-tests/24-padding.conf.in [new file with mode: 0644]
util/libssl.num

index a8de2dc..de50de5 100644 (file)
@@ -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) \
index f5c6576..efd766d 100644 (file)
@@ -110,6 +110,12 @@ Attempts to use the file B<value> 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<value> in
+length on send. A B<value> of 0 or 1 turns off padding. Otherwise, the
+B<value> 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<value> as the set of temporary DH parameters for
 the appropriate context. This option is only supported if certificate
 operations are permitted.
 
+=item B<RecordPadding>
+
+Attempts to pad TLS 1.3 records so that they are a multiple of B<value> in
+length on send. A B<value> of 0 or 1 turns off padding. Otherwise, the
+B<value> must be >1 or <=16384.
+
 =item B<SignatureAlgorithms>
 
 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 (file)
index 0000000..9eea2bc
--- /dev/null
@@ -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 <openssl/ssl.h>
+
+ 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<cb> to specify the padding
+for TLS 1.3 records. The value set in B<ctx> 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<arg> that is passed to the callback when it is invoked. The value
+set in B<ctx> 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<arg> 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<block_size>. A B<block_size> of 0 or 1 disables block padding. The limit of
+B<block_size> is SSL3_RT_MAX_PLAIN_LENGTH.
+
+The callback is invoked for every record before encryption.
+The B<type> 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<len> parameter is the current plaintext length of the record before encryption.
+The B<arg> 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<arg> 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<block_size> is too large.
+
+The B<cb> 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<ssl(7)>, L<SSL_new(3)>
+
+=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<https://www.openssl.org/source/license.html>.
+
+=cut
index d533a0c..b1da6c5 100644 (file)
@@ -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
index 3479f1f..d081f55 100644 (file)
@@ -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;
index 67b4999..8e553e6 100644 (file)
@@ -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
index 14c6778..43c4a94 100644 (file)
@@ -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);
+            }
         }
 
         /*
index 4b46192..484bb61 100644 (file)
@@ -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 */
index c59aa84..d1a1f02 100644 (file)
@@ -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.
index 15065c7..1d3a207 100644 (file)
@@ -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;
 };
 
index fbcb46a..100b852 100644 (file)
@@ -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 (file)
index 0000000..3c9f450
--- /dev/null
@@ -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 (file)
index 0000000..7bf256c
--- /dev/null
@@ -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" },
+    },
+);
index ccaf4bc..a17ebbc 100644 (file)
@@ -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: