diff --git a/apps/req.c b/apps/req.c index 7e7b994f..3d89f200 100644 --- a/apps/req.c +++ b/apps/req.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "apps.h" #include "progs.h" #include @@ -23,6 +24,8 @@ #include #include #include +#include +#include #ifndef OPENSSL_NO_RSA # include #endif @@ -147,6 +150,68 @@ const OPTIONS req_options[] = { {NULL} }; + +/* + * An LHASH of strings, where each string is an extension name. + */ +static unsigned long ext_name_hash(const OPENSSL_STRING *a) +{ + return OPENSSL_LH_strhash((const char *)a); +} + +static int ext_name_cmp(const OPENSSL_STRING *a, const OPENSSL_STRING *b) +{ + return strcmp((const char *)a, (const char *)b); +} + +static void exts_cleanup(OPENSSL_STRING *x) +{ + OPENSSL_free((char *)x); +} + +/* + * Is the |kv| key already duplicated? This is remarkably tricky to get + * right. Return 0 if unique, -1 on runtime error; 1 if found or a syntax + * error. + */ +static int duplicated(LHASH_OF(OPENSSL_STRING) *addexts, char *kv) +{ + char *p; + + /* Check syntax. */ + if (strchr(kv, '=') == NULL) + return 1; + + /* Skip leading whitespace, make a copy. */ + while (*kv && isspace(*kv)) + if (*++kv == '\0') + return 1; + if ((kv = OPENSSL_strdup(kv)) == NULL) + return -1; + + /* Skip trailing space before the equal sign. */ + for (p = strchr(kv, '='); p > kv; --p) + if (p[-1] != ' ' && p[-1] != '\t') + break; + if (p == kv) { + OPENSSL_free(kv); + return 1; + } + *p = '\0'; + + /* Finally have a clean "key"; see if it's there. */ + if (lh_OPENSSL_STRING_retrieve(addexts, (OPENSSL_STRING*)kv) != NULL) { + BIO_printf(bio_err, "Extension \"%s\" repeated\n", kv); + OPENSSL_free(kv); + return 1; + } + + /* Not found; add it. */ + if (lh_OPENSSL_STRING_insert(addexts, (OPENSSL_STRING*)kv) == NULL) + return -1; + return 0; +} + int req_main(int argc, char **argv) { ASN1_INTEGER *serial = NULL; @@ -155,6 +220,7 @@ int req_main(int argc, char **argv) EVP_PKEY *pkey = NULL; EVP_PKEY_CTX *genctx = NULL; STACK_OF(OPENSSL_STRING) *pkeyopts = NULL, *sigopts = NULL; + LHASH_OF(OPENSSL_STRING) *addexts = NULL; X509 *x509ss = NULL; X509_REQ *req = NULL; const EVP_CIPHER *cipher = NULL; @@ -324,11 +390,17 @@ int req_main(int argc, char **argv) multirdn = 1; break; case OPT_ADDEXT: - if (addext_bio == NULL) { + p = opt_arg(); + if (addexts == NULL) { + addexts = lh_OPENSSL_STRING_new(ext_name_hash, ext_name_cmp); addext_bio = BIO_new(BIO_s_mem()); + if (addexts == NULL || addext_bio == NULL) + goto end; } - if (addext_bio == NULL - || BIO_printf(addext_bio, "%s\n", opt_arg()) < 0) + i = duplicated(addexts, p); + if (i == 1) + goto opthelp; + if (i < 0 || BIO_printf(addext_bio, "%s\n", opt_arg()) < 0) goto end; break; case OPT_EXTENSIONS: @@ -885,6 +957,8 @@ int req_main(int argc, char **argv) EVP_PKEY_CTX_free(genctx); sk_OPENSSL_STRING_free(pkeyopts); sk_OPENSSL_STRING_free(sigopts); + lh_OPENSSL_STRING_doall(addexts, exts_cleanup); + lh_OPENSSL_STRING_free(addexts); #ifndef OPENSSL_NO_ENGINE ENGINE_free(gen_eng); #endif diff --git a/apps/s_server.c b/apps/s_server.c index b0502005..94c18266 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -192,8 +192,11 @@ static int psk_find_session_cb(SSL *ssl, const unsigned char *identity, const SSL_CIPHER *cipher = NULL; if (strlen(psk_identity) != identity_len - || memcmp(psk_identity, identity, identity_len) != 0) - return 0; + || memcmp(psk_identity, identity, identity_len) != 0) { + BIO_printf(bio_s_out, + "PSK warning: client identity not what we expected" + " (got '%s' expected '%s')\n", identity, psk_identity); + } if (psksess != NULL) { SSL_SESSION_up_ref(psksess); @@ -748,8 +751,8 @@ typedef enum OPTION_choice { OPT_ID_PREFIX, OPT_SERVERNAME, OPT_SERVERNAME_FATAL, OPT_CERT2, OPT_KEY2, OPT_NEXTPROTONEG, OPT_ALPN, OPT_SRTP_PROFILES, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, - OPT_KEYLOG_FILE, OPT_MAX_EARLY, OPT_EARLY_DATA, OPT_S_NUM_TICKETS, - OPT_ANTI_REPLAY, OPT_NO_ANTI_REPLAY, + OPT_KEYLOG_FILE, OPT_MAX_EARLY, OPT_RECV_MAX_EARLY, OPT_EARLY_DATA, + OPT_S_NUM_TICKETS, OPT_ANTI_REPLAY, OPT_NO_ANTI_REPLAY, OPT_R_ENUM, OPT_S_ENUM, OPT_V_ENUM, @@ -955,7 +958,9 @@ const OPTIONS s_server_options[] = { #endif {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"}, {"max_early_data", OPT_MAX_EARLY, 'n', - "The maximum number of bytes of early data"}, + "The maximum number of bytes of early data as advertised in tickets"}, + {"recv_max_early_data", OPT_RECV_MAX_EARLY, 'n', + "The maximum number of bytes of early data (hard limit)"}, {"early_data", OPT_EARLY_DATA, '-', "Attempt to read early data"}, {"num_tickets", OPT_S_NUM_TICKETS, 'n', "The number of TLSv1.3 session tickets that a server will automatically issue" }, @@ -1041,7 +1046,7 @@ int s_server_main(int argc, char *argv[]) unsigned int split_send_fragment = 0, max_pipelines = 0; const char *s_serverinfo_file = NULL; const char *keylog_file = NULL; - int max_early_data = -1; + int max_early_data = -1, recv_max_early_data = -1; char *psksessf = NULL; /* Init of few remaining global variables */ @@ -1570,6 +1575,13 @@ int s_server_main(int argc, char *argv[]) goto end; } break; + case OPT_RECV_MAX_EARLY: + recv_max_early_data = atoi(opt_arg()); + if (recv_max_early_data < 0) { + BIO_printf(bio_err, "Invalid value for recv_max_early_data\n"); + goto end; + } + break; case OPT_EARLY_DATA: early_data = 1; if (max_early_data == -1) @@ -2110,6 +2122,8 @@ int s_server_main(int argc, char *argv[]) if (max_early_data >= 0) SSL_CTX_set_max_early_data(ctx, max_early_data); + if (recv_max_early_data >= 0) + SSL_CTX_set_recv_max_early_data(ctx, recv_max_early_data); if (rev) server_cb = rev_body; diff --git a/crypto/evp/e_chacha20_poly1305.c b/crypto/evp/e_chacha20_poly1305.c index 47d5e50d..6a9bccfe 100644 --- a/crypto/evp/e_chacha20_poly1305.c +++ b/crypto/evp/e_chacha20_poly1305.c @@ -196,14 +196,23 @@ static int chacha20_poly1305_init_key(EVP_CIPHER_CTX *ctx, } # if !defined(OPENSSL_SMALL_FOOTPRINT) + +# if defined(POLY1305_ASM) && (defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_AMD64) || defined(_M_X64)) +# define XOR128_HELPERS +void *xor128_encrypt_n_pad(void *out, const void *inp, void *otp, size_t len); +void *xor128_decrypt_n_pad(void *out, const void *inp, void *otp, size_t len); +static const unsigned char zero[4 * CHACHA_BLK_SIZE] = { 0 }; +# else static const unsigned char zero[2 * CHACHA_BLK_SIZE] = { 0 }; +# endif static int chacha20_poly1305_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t len) { EVP_CHACHA_AEAD_CTX *actx = aead_data(ctx); - size_t i, tail, tohash_len, plen = actx->tls_payload_length; - unsigned char *buf, *tohash, *ctr, storage[2 * CHACHA_BLK_SIZE + 32]; + size_t tail, tohash_len, buf_len, plen = actx->tls_payload_length; + unsigned char *buf, *tohash, *ctr, storage[sizeof(zero) + 32]; if (len != plen + POLY1305_BLOCK_SIZE) return -1; @@ -212,9 +221,11 @@ static int chacha20_poly1305_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, ctr = buf + CHACHA_BLK_SIZE; tohash = buf + CHACHA_BLK_SIZE - POLY1305_BLOCK_SIZE; - if (plen <= CHACHA_BLK_SIZE) { +# ifdef XOR128_HELPERS + if (plen <= 3 * CHACHA_BLK_SIZE) { actx->key.counter[0] = 0; - ChaCha20_ctr32(buf, zero, 2 * CHACHA_BLK_SIZE, actx->key.key.d, + buf_len = (plen + 2 * CHACHA_BLK_SIZE - 1) & (0 - CHACHA_BLK_SIZE); + ChaCha20_ctr32(buf, zero, buf_len, actx->key.key.d, actx->key.counter); Poly1305_Init(POLY1305_ctx(actx), buf); actx->key.partial_len = 0; @@ -223,6 +234,31 @@ static int chacha20_poly1305_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, actx->len.aad = EVP_AEAD_TLS1_AAD_LEN; actx->len.text = plen; + if (plen) { + if (ctx->encrypt) + ctr = xor128_encrypt_n_pad(out, in, ctr, plen); + else + ctr = xor128_decrypt_n_pad(out, in, ctr, plen); + + in += plen; + out += plen; + tohash_len = (size_t)(ctr - tohash); + } + } +# else + if (plen <= CHACHA_BLK_SIZE) { + size_t i; + + actx->key.counter[0] = 0; + ChaCha20_ctr32(buf, zero, (buf_len = 2 * CHACHA_BLK_SIZE), + actx->key.key.d, actx->key.counter); + Poly1305_Init(POLY1305_ctx(actx), buf); + actx->key.partial_len = 0; + memcpy(tohash, actx->tls_aad, POLY1305_BLOCK_SIZE); + tohash_len = POLY1305_BLOCK_SIZE; + actx->len.aad = EVP_AEAD_TLS1_AAD_LEN; + actx->len.text = plen; + if (ctx->encrypt) { for (i = 0; i < plen; i++) { out[i] = ctr[i] ^= in[i]; @@ -242,10 +278,12 @@ static int chacha20_poly1305_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, memset(ctr + i, 0, tail); ctr += i + tail; tohash_len += i + tail; - } else { + } +# endif + else { actx->key.counter[0] = 0; - ChaCha20_ctr32(buf, zero, CHACHA_BLK_SIZE, actx->key.key.d, - actx->key.counter); + ChaCha20_ctr32(buf, zero, (buf_len = CHACHA_BLK_SIZE), + actx->key.key.d, actx->key.counter); Poly1305_Init(POLY1305_ctx(actx), buf); actx->key.counter[0] = 1; actx->key.partial_len = 0; @@ -300,7 +338,7 @@ static int chacha20_poly1305_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out, } Poly1305_Update(POLY1305_ctx(actx), tohash, tohash_len); - OPENSSL_cleanse(buf, 2 * CHACHA_BLK_SIZE); + OPENSSL_cleanse(buf, buf_len); Poly1305_Final(POLY1305_ctx(actx), ctx->encrypt ? actx->tag : tohash); diff --git a/crypto/poly1305/asm/poly1305-x86_64.pl b/crypto/poly1305/asm/poly1305-x86_64.pl index 0d1c0de6..0b4c56e5 100755 --- a/crypto/poly1305/asm/poly1305-x86_64.pl +++ b/crypto/poly1305/asm/poly1305-x86_64.pl @@ -3753,6 +3753,110 @@ poly1305_emit_base2_44: .size poly1305_emit_base2_44,.-poly1305_emit_base2_44 ___ } } } + +{ # chacha20-poly1305 helpers +my ($out,$inp,$otp,$len)=$win64 ? ("%rcx","%rdx","%r8", "%r9") : # Win64 order + ("%rdi","%rsi","%rdx","%rcx"); # Unix order +$code.=<<___; +.globl xor128_encrypt_n_pad +.type xor128_encrypt_n_pad,\@abi-omnipotent +.align 16 +xor128_encrypt_n_pad: + sub $otp,$inp + sub $otp,$out + mov $len,%r10 # put len aside + shr \$4,$len # len / 16 + jz .Ltail_enc + nop +.Loop_enc_xmm: + movdqu ($inp,$otp),%xmm0 + pxor ($otp),%xmm0 + movdqu %xmm0,($out,$otp) + movdqa %xmm0,($otp) + lea 16($otp),$otp + dec $len + jnz .Loop_enc_xmm + + and \$15,%r10 # len % 16 + jz .Ldone_enc + +.Ltail_enc: + mov \$16,$len + sub %r10,$len + xor %eax,%eax +.Loop_enc_byte: + mov ($inp,$otp),%al + xor ($otp),%al + mov %al,($out,$otp) + mov %al,($otp) + lea 1($otp),$otp + dec %r10 + jnz .Loop_enc_byte + + xor %eax,%eax +.Loop_enc_pad: + mov %al,($otp) + lea 1($otp),$otp + dec $len + jnz .Loop_enc_pad + +.Ldone_enc: + mov $otp,%rax + ret +.size xor128_encrypt_n_pad,.-xor128_encrypt_n_pad + +.globl xor128_decrypt_n_pad +.type xor128_decrypt_n_pad,\@abi-omnipotent +.align 16 +xor128_decrypt_n_pad: + sub $otp,$inp + sub $otp,$out + mov $len,%r10 # put len aside + shr \$4,$len # len / 16 + jz .Ltail_dec + nop +.Loop_dec_xmm: + movdqu ($inp,$otp),%xmm0 + movdqa ($otp),%xmm1 + pxor %xmm0,%xmm1 + movdqu %xmm1,($out,$otp) + movdqa %xmm0,($otp) + lea 16($otp),$otp + dec $len + jnz .Loop_dec_xmm + + pxor %xmm1,%xmm1 + and \$15,%r10 # len % 16 + jz .Ldone_dec + +.Ltail_dec: + mov \$16,$len + sub %r10,$len + xor %eax,%eax + xor %r11,%r11 +.Loop_dec_byte: + mov ($inp,$otp),%r11b + mov ($otp),%al + xor %r11b,%al + mov %al,($out,$otp) + mov %r11b,($otp) + lea 1($otp),$otp + dec %r10 + jnz .Loop_dec_byte + + xor %eax,%eax +.Loop_dec_pad: + mov %al,($otp) + lea 1($otp),$otp + dec $len + jnz .Loop_dec_pad + +.Ldone_dec: + mov $otp,%rax + ret +.size xor128_decrypt_n_pad,.-xor128_decrypt_n_pad +___ +} $code.=<<___; .align 64 .Lconst: diff --git a/crypto/sm2/sm2_crypt.c b/crypto/sm2/sm2_crypt.c index 0fe1dd83..aedf9054 100644 --- a/crypto/sm2/sm2_crypt.c +++ b/crypto/sm2/sm2_crypt.c @@ -48,7 +48,8 @@ static size_t ec_field_size(const EC_GROUP *group) if (p == NULL || a == NULL || b == NULL) goto done; - EC_GROUP_get_curve_GFp(group, p, a, b, NULL); + if (!EC_GROUP_get_curve_GFp(group, p, a, b, NULL)) + goto done; field_size = (BN_num_bits(p) + 7) / 8; done: @@ -121,19 +122,20 @@ int sm2_encrypt(const EC_KEY *key, uint8_t *msg_mask = NULL; uint8_t *x2y2 = NULL; uint8_t *C3 = NULL; - const size_t field_size = ec_field_size(group); - const size_t C3_size = EVP_MD_size(digest); + size_t field_size; + const int C3_size = EVP_MD_size(digest); /* NULL these before any "goto done" */ ctext_struct.C2 = NULL; ctext_struct.C3 = NULL; - if (hash == NULL - || group == NULL - || order == NULL - || P == NULL - || field_size == 0 - || C3_size == 0) { + if (hash == NULL || C3_size <= 0) { + SM2err(SM2_F_SM2_ENCRYPT, ERR_R_INTERNAL_ERROR); + goto done; + } + + field_size = ec_field_size(group); + if (field_size == 0) { SM2err(SM2_F_SM2_ENCRYPT, ERR_R_INTERNAL_ERROR); goto done; } @@ -273,7 +275,7 @@ int sm2_decrypt(const EC_KEY *key, int msg_len = 0; EVP_MD_CTX *hash = NULL; - if (field_size == 0 || hash_size == 0) + if (field_size == 0 || hash_size <= 0) goto done; memset(ptext_buf, 0xFF, *ptext_len); diff --git a/crypto/sm2/sm2_sign.c b/crypto/sm2/sm2_sign.c index 14576ca8..adde9520 100644 --- a/crypto/sm2/sm2_sign.c +++ b/crypto/sm2/sm2_sign.c @@ -25,19 +25,20 @@ static BIGNUM *sm2_compute_msg_hash(const EVP_MD *digest, { EVP_MD_CTX *hash = EVP_MD_CTX_new(); const int md_size = EVP_MD_size(digest); - uint8_t *za = OPENSSL_zalloc(md_size); + uint8_t *za = NULL; BIGNUM *e = NULL; - if (hash == NULL || za == NULL) { - SM2err(SM2_F_SM2_COMPUTE_MSG_HASH, ERR_R_MALLOC_FAILURE); - goto done; - } - if (md_size < 0) { SM2err(SM2_F_SM2_COMPUTE_MSG_HASH, SM2_R_INVALID_DIGEST); goto done; } + za = OPENSSL_zalloc(md_size); + if (hash == NULL || za == NULL) { + SM2err(SM2_F_SM2_COMPUTE_MSG_HASH, ERR_R_MALLOC_FAILURE); + goto done; + } + if (!sm2_compute_userid_digest(za, digest, user_id, key)) { /* SM2err already called */ goto done; diff --git a/crypto/sm2/sm2_za.c b/crypto/sm2/sm2_za.c index 94d0dac0..8f45082d 100644 --- a/crypto/sm2/sm2_za.c +++ b/crypto/sm2/sm2_za.c @@ -59,8 +59,6 @@ int sm2_compute_userid_digest(uint8_t *out, goto done; } - memset(out, 0, EVP_MD_size(digest)); - if (!EVP_DigestInit(hash, digest)) { SM2err(SM2_F_SM2_COMPUTE_USERID_DIGEST, ERR_R_EVP_LIB); goto done; diff --git a/doc/man3/SSL_read_early_data.pod b/doc/man3/SSL_read_early_data.pod index cf6f7579..27c127d3 100644 --- a/doc/man3/SSL_read_early_data.pod +++ b/doc/man3/SSL_read_early_data.pod @@ -6,6 +6,10 @@ SSL_set_max_early_data, SSL_CTX_set_max_early_data, SSL_get_max_early_data, SSL_CTX_get_max_early_data, +SSL_set_recv_max_early_data, +SSL_CTX_set_recv_max_early_data, +SSL_get_recv_max_early_data, +SSL_CTX_get_recv_max_early_data, SSL_SESSION_get_max_early_data, SSL_SESSION_set_max_early_data, SSL_write_early_data, @@ -24,6 +28,12 @@ SSL_set_allow_early_data_cb uint32_t SSL_CTX_get_max_early_data(const SSL_CTX *ctx); int SSL_set_max_early_data(SSL *s, uint32_t max_early_data); uint32_t SSL_get_max_early_data(const SSL *s); + + int SSL_CTX_set_recv_max_early_data(SSL_CTX *ctx, uint32_t recv_max_early_data); + uint32_t SSL_CTX_get_recv_max_early_data(const SSL_CTX *ctx); + int SSL_set_recv_max_early_data(SSL *s, uint32_t recv_max_early_data); + uint32_t SSL_get_recv_max_early_data(const SSL *s); + uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s); int SSL_SESSION_set_max_early_data(SSL_SESSION *s, uint32_t max_early_data); @@ -195,9 +205,26 @@ since there is no practical benefit from using only one of them. If the maximum early data setting for a server is non-zero then replay protection is automatically enabled (see L below). -In the event that the current maximum early data setting for the server is -different to that originally specified in a session that a client is resuming -with then the lower of the two values will apply. +If the server rejects the early data sent by a client then it will skip over +the data that is sent. The maximum amount of received early data that is skipped +is controlled by the recv_max_early_data setting. If a client sends more than +this then the connection will abort. This value can be set by calling +SSL_CTX_set_recv_max_early_data() or SSL_set_recv_max_early_data(). The current +value for this setting can be obtained by calling +SSL_CTX_get_recv_max_early_data() or SSL_get_recv_max_early_data(). The default +value for this setting is 16,384 bytes. + +The recv_max_early_data value also has an impact on early data that is accepted. +The amount of data that is accepted will always be the lower of the +max_early_data for the session and the recv_max_early_data setting for the +server. If a client sends more data than this then the connection will abort. + +The configured value for max_early_data on a server may change over time as +required. However clients may have tickets containing the previously configured +max_early_data value. The recv_max_early_data should always be equal to or +higher than any recently configured max_early_data value in order to avoid +aborted connections. The recv_max_early_data should never be set to less than +the current configured max_early_data value. Some server applications may wish to have more control over whether early data is accepted or not, for example to mitigate replay risks (see L diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index bbcfb3c0..2376828e 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -919,6 +919,10 @@ int SSL_CTX_set_max_early_data(SSL_CTX *ctx, uint32_t max_early_data); uint32_t SSL_CTX_get_max_early_data(const SSL_CTX *ctx); int SSL_set_max_early_data(SSL *s, uint32_t max_early_data); uint32_t SSL_get_max_early_data(const SSL *s); +int SSL_CTX_set_recv_max_early_data(SSL_CTX *ctx, uint32_t recv_max_early_data); +uint32_t SSL_CTX_get_recv_max_early_data(const SSL_CTX *ctx); +int SSL_set_recv_max_early_data(SSL *s, uint32_t recv_max_early_data); +uint32_t SSL_get_recv_max_early_data(const SSL *s); #ifdef __cplusplus } diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c index ae510b2e..ad478bf3 100644 --- a/ssl/record/ssl3_record.c +++ b/ssl/record/ssl3_record.c @@ -103,7 +103,7 @@ static int ssl3_record_app_data_waiting(SSL *s) int early_data_count_ok(SSL *s, size_t length, size_t overhead, int send) { - uint32_t max_early_data = s->max_early_data; + uint32_t max_early_data; SSL_SESSION *sess = s->session; /* @@ -120,9 +120,14 @@ int early_data_count_ok(SSL *s, size_t length, size_t overhead, int send) } sess = s->psksession; } - if (!s->server - || (s->hit && sess->ext.max_early_data < s->max_early_data)) + + if (!s->server) max_early_data = sess->ext.max_early_data; + else if (s->ext.early_data != SSL_EARLY_DATA_ACCEPTED) + max_early_data = s->recv_max_early_data; + else + max_early_data = s->recv_max_early_data < sess->ext.max_early_data + ? s->recv_max_early_data : sess->ext.max_early_data; if (max_early_data == 0) { SSLfatal(s, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 1dbc3613..80a5be26 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -700,6 +700,7 @@ SSL *SSL_new(SSL_CTX *ctx) s->mode = ctx->mode; s->max_cert_list = ctx->max_cert_list; s->max_early_data = ctx->max_early_data; + s->recv_max_early_data = ctx->recv_max_early_data; s->num_tickets = ctx->num_tickets; /* Shallow copy of the ciphersuites stack */ @@ -3105,6 +3106,16 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) */ ret->max_early_data = 0; + /* + * Default recv_max_early_data is a fully loaded single record. Could be + * split across multiple records in practice. We set this differently to + * max_early_data so that, in the default case, we do not advertise any + * support for early_data, but if a client were to send us some (e.g. + * because of an old, stale ticket) then we will tolerate it and skip over + * it. + */ + ret->recv_max_early_data = SSL3_RT_MAX_PLAIN_LENGTH; + /* By default we send two session tickets automatically in TLSv1.3 */ ret->num_tickets = 2; @@ -5444,6 +5455,30 @@ uint32_t SSL_get_max_early_data(const SSL *s) return s->max_early_data; } +int SSL_CTX_set_recv_max_early_data(SSL_CTX *ctx, uint32_t recv_max_early_data) +{ + ctx->recv_max_early_data = recv_max_early_data; + + return 1; +} + +uint32_t SSL_CTX_get_recv_max_early_data(const SSL_CTX *ctx) +{ + return ctx->recv_max_early_data; +} + +int SSL_set_recv_max_early_data(SSL *s, uint32_t recv_max_early_data) +{ + s->recv_max_early_data = recv_max_early_data; + + return 1; +} + +uint32_t SSL_get_recv_max_early_data(const SSL *s) +{ + return s->recv_max_early_data; +} + __owur unsigned int ssl_get_max_send_fragment(const SSL *ssl) { /* Return any active Max Fragment Len extension */ diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 53dbbd59..b1ff0950 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1069,9 +1069,18 @@ struct ssl_ctx_st { */ SSL_CTX_keylog_cb_func keylog_callback; - /* The maximum number of bytes that can be sent as early data */ + /* + * The maximum number of bytes advertised in session tickets that can be + * sent as early data. + */ uint32_t max_early_data; + /* + * The maximum number of bytes of early data that a server will tolerate + * (which should be at least as much as max_early_data). + */ + uint32_t recv_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; @@ -1443,8 +1452,17 @@ struct ssl_st { ASYNC_WAIT_CTX *waitctx; size_t asyncrw; - /* The maximum number of plaintext bytes that can be sent as early data */ + /* + * The maximum number of bytes advertised in session tickets that can be + * sent as early data. + */ uint32_t max_early_data; + /* + * The maximum number of bytes of early data that a server will tolerate + * (which should be at least as much as max_early_data). + */ + uint32_t recv_max_early_data; + /* * The number of bytes of early data received so far. If we accepted early * data then this is a count of the plaintext bytes. If we rejected it then diff --git a/test/recipes/25-test_req.t b/test/recipes/25-test_req.t index 82b9bf8a..fa79219e 100644 --- a/test/recipes/25-test_req.t +++ b/test/recipes/25-test_req.t @@ -15,13 +15,24 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/; setup("test_req"); -plan tests => 4; +plan tests => 8; require_ok(srctop_file('test','recipes','tconversion.pl')); open RND, ">>", ".rnd"; print RND "string to make the random number generator think it has randomness"; close RND; + +# Check for duplicate -addext parameters +my $val = "subjectAltName=DNS:example.com"; +my $val2 = " " . $val; +my $val3 = $val; +$val3 =~ s/=/ =/; +ok(!run(app(["openssl", "req", "-new", "-addext", $val, "-addext", $val]))); +ok(!run(app(["openssl", "req", "-new", "-addext", $val, "-addext", $val2]))); +ok(!run(app(["openssl", "req", "-new", "-addext", $val, "-addext", $val3]))); +ok(!run(app(["openssl", "req", "-new", "-addext", $val2, "-addext", $val3]))); + subtest "generating certificate requests" => sub { my @req_new; if (disabled("rsa")) { diff --git a/test/sslapitest.c b/test/sslapitest.c index 64f8f597..cdac8bc4 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -2314,8 +2314,11 @@ static int test_early_data_replay(int idx) /* * Helper function to test that a server attempting to read early data can * handle a connection from a client where the early data should be skipped. + * testtype: 0 == No HRR + * testtype: 1 == HRR + * testtype: 2 == recv_max_early_data set to 0 */ -static int early_data_skip_helper(int hrr, int idx) +static int early_data_skip_helper(int testtype, int idx) { SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; @@ -2328,7 +2331,7 @@ static int early_data_skip_helper(int hrr, int idx) &serverssl, &sess, idx))) goto end; - if (hrr) { + if (testtype == 1) { /* Force an HRR to occur */ if (!TEST_true(SSL_set1_groups_list(serverssl, "P-256"))) goto end; @@ -2348,13 +2351,17 @@ static int early_data_skip_helper(int hrr, int idx) goto end; } + if (testtype == 2 + && !TEST_true(SSL_set_recv_max_early_data(serverssl, 0))) + goto end; + /* Write some early data */ if (!TEST_true(SSL_write_early_data(clientssl, MSG1, strlen(MSG1), &written)) || !TEST_size_t_eq(written, strlen(MSG1))) goto end; - /* Server should reject the early data and skip over it */ + /* Server should reject the early data */ if (!TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf), &readbytes), SSL_READ_EARLY_DATA_FINISH) @@ -2363,7 +2370,7 @@ static int early_data_skip_helper(int hrr, int idx) SSL_EARLY_DATA_REJECTED)) goto end; - if (hrr) { + if (testtype == 1) { /* * Finish off the handshake. We perform the same writes and reads as * further down but we expect them to fail due to the incomplete @@ -2373,9 +2380,24 @@ static int early_data_skip_helper(int hrr, int idx) || !TEST_false(SSL_read_ex(serverssl, buf, sizeof(buf), &readbytes))) goto end; + } else if (testtype == 2) { + /* + * This client has sent more early_data than we are willing to skip so + * the connection should abort. + */ + if (!TEST_false(SSL_read_ex(serverssl, buf, sizeof(buf), &readbytes)) + || !TEST_int_eq(SSL_get_error(serverssl, 0), SSL_ERROR_SSL)) + goto end; + + /* Connection has failed - nothing more to do */ + testresult = 1; + goto end; } - /* Should be able to send normal data despite rejection of early data */ + /* + * Should be able to send normal data despite rejection of early data. The + * early_data should be skipped. + */ if (!TEST_true(SSL_write_ex(clientssl, MSG2, strlen(MSG2), &written)) || !TEST_size_t_eq(written, strlen(MSG2)) || !TEST_int_eq(SSL_get_early_data_status(clientssl), @@ -2416,6 +2438,15 @@ static int test_early_data_skip_hrr(int idx) return early_data_skip_helper(1, idx); } +/* + * Test that a server attempting to read early data will abort if it tries to + * skip over too much. + */ +static int test_early_data_skip_abort(int idx) +{ + return early_data_skip_helper(2, idx); +} + /* * Test that a server attempting to read early data can handle a connection * from a client that doesn't send any. @@ -5267,6 +5298,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_early_data_replay, 2); ADD_ALL_TESTS(test_early_data_skip, 3); ADD_ALL_TESTS(test_early_data_skip_hrr, 3); + ADD_ALL_TESTS(test_early_data_skip_abort, 3); ADD_ALL_TESTS(test_early_data_not_sent, 3); ADD_ALL_TESTS(test_early_data_psk, 8); ADD_ALL_TESTS(test_early_data_not_expected, 3); diff --git a/util/libssl.num b/util/libssl.num index df6a71e1..9b6d2661 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -492,3 +492,7 @@ SSL_get_num_tickets 492 1_1_1 EXIST::FUNCTION: SSL_CTX_set_num_tickets 493 1_1_1 EXIST::FUNCTION: SSL_CTX_set_allow_early_data_cb 494 1_1_1 EXIST::FUNCTION: SSL_set_allow_early_data_cb 495 1_1_1 EXIST::FUNCTION: +SSL_set_recv_max_early_data 496 1_1_1 EXIST::FUNCTION: +SSL_get_recv_max_early_data 497 1_1_1 EXIST::FUNCTION: +SSL_CTX_get_recv_max_early_data 498 1_1_1 EXIST::FUNCTION: +SSL_CTX_set_recv_max_early_data 499 1_1_1 EXIST::FUNCTION: