diff --git a/crypto/cipher_extra/e_aesctrhmac.c b/crypto/cipher_extra/e_aesctrhmac.c index 2982d0de2..3034b8ff1 100644 --- a/crypto/cipher_extra/e_aesctrhmac.c +++ b/crypto/cipher_extra/e_aesctrhmac.c @@ -254,6 +254,7 @@ static const EVP_AEAD aead_aes_128_ctr_hmac_sha256 = { aead_aes_ctr_hmac_sha256_seal_scatter, aead_aes_ctr_hmac_sha256_open_gather, NULL /* get_iv */, + NULL /* tag_len */, }; static const EVP_AEAD aead_aes_256_ctr_hmac_sha256 = { @@ -270,6 +271,7 @@ static const EVP_AEAD aead_aes_256_ctr_hmac_sha256 = { aead_aes_ctr_hmac_sha256_seal_scatter, aead_aes_ctr_hmac_sha256_open_gather, NULL /* get_iv */, + NULL /* tag_len */, }; const EVP_AEAD *EVP_aead_aes_128_ctr_hmac_sha256(void) { diff --git a/crypto/cipher_extra/e_aesgcmsiv.c b/crypto/cipher_extra/e_aesgcmsiv.c index 2dd12670e..6adcf17ae 100644 --- a/crypto/cipher_extra/e_aesgcmsiv.c +++ b/crypto/cipher_extra/e_aesgcmsiv.c @@ -520,6 +520,7 @@ static const EVP_AEAD aead_aes_128_gcm_siv_asm = { aead_aes_gcm_siv_asm_seal_scatter, NULL /* open_gather */, NULL /* get_iv */, + NULL /* tag_len */, }; static const EVP_AEAD aead_aes_256_gcm_siv_asm = { @@ -536,6 +537,7 @@ static const EVP_AEAD aead_aes_256_gcm_siv_asm = { aead_aes_gcm_siv_asm_seal_scatter, NULL /* open_gather */, NULL /* get_iv */, + NULL /* tag_len */, }; #endif /* X86_64 && !NO_ASM */ @@ -804,6 +806,7 @@ static const EVP_AEAD aead_aes_128_gcm_siv = { aead_aes_gcm_siv_seal_scatter, aead_aes_gcm_siv_open_gather, NULL /* get_iv */, + NULL /* tag_len */, }; static const EVP_AEAD aead_aes_256_gcm_siv = { @@ -820,6 +823,7 @@ static const EVP_AEAD aead_aes_256_gcm_siv = { aead_aes_gcm_siv_seal_scatter, aead_aes_gcm_siv_open_gather, NULL /* get_iv */, + NULL /* tag_len */, }; #if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) diff --git a/crypto/cipher_extra/e_chacha20poly1305.c b/crypto/cipher_extra/e_chacha20poly1305.c index 515b60f2b..162e14b6d 100644 --- a/crypto/cipher_extra/e_chacha20poly1305.c +++ b/crypto/cipher_extra/e_chacha20poly1305.c @@ -294,6 +294,7 @@ static const EVP_AEAD aead_chacha20_poly1305 = { aead_chacha20_poly1305_seal_scatter, aead_chacha20_poly1305_open_gather, NULL, /* get_iv */ + NULL, /* tag_len */ }; const EVP_AEAD *EVP_aead_chacha20_poly1305(void) { diff --git a/crypto/cipher_extra/e_ssl3.c b/crypto/cipher_extra/e_ssl3.c index f2eb357cc..dc4371368 100644 --- a/crypto/cipher_extra/e_ssl3.c +++ b/crypto/cipher_extra/e_ssl3.c @@ -123,13 +123,33 @@ static int aead_ssl3_init(EVP_AEAD_CTX *ctx, const uint8_t *key, size_t key_len, return 1; } +static size_t aead_ssl3_tag_len(const EVP_AEAD_CTX *ctx, const size_t in_len, + const size_t extra_in_len) { + assert(extra_in_len == 0); + const AEAD_SSL3_CTX *ssl3_ctx = (AEAD_SSL3_CTX*)ctx->aead_state; + + const size_t digest_len = EVP_MD_CTX_size(&ssl3_ctx->md_ctx); + if (EVP_CIPHER_CTX_mode(&ssl3_ctx->cipher_ctx) != EVP_CIPH_CBC_MODE) { + // The NULL cipher. + return digest_len; + } + + const size_t block_size = EVP_CIPHER_CTX_block_size(&ssl3_ctx->cipher_ctx); + /* An overflow of |in_len + digest_len| doesn't affect the result mod + * |block_size|, provided that |block_size| is a smaller power of two. */ + assert(block_size != 0 && (block_size & (block_size - 1)) == 0); + const size_t pad_len = block_size - ((in_len + digest_len) % block_size); + return digest_len + pad_len; +} + static int aead_ssl3_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag, size_t *out_tag_len, - size_t max_out_tag_len, const uint8_t *nonce, - size_t nonce_len, const uint8_t *in, - size_t in_len, const uint8_t *extra_in, - size_t extra_in_len, const uint8_t *ad, - size_t ad_len) { + const size_t max_out_tag_len, + const uint8_t *nonce, const size_t nonce_len, + const uint8_t *in, const size_t in_len, + const uint8_t *extra_in, + const size_t extra_in_len, const uint8_t *ad, + const size_t ad_len) { AEAD_SSL3_CTX *ssl3_ctx = (AEAD_SSL3_CTX *)ctx->aead_state; if (!ssl3_ctx->cipher_ctx.encrypt) { @@ -144,8 +164,7 @@ static int aead_ssl3_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, return 0; } - const size_t max_overhead = EVP_AEAD_max_overhead(ctx->aead); - if (max_out_tag_len < max_overhead) { + if (max_out_tag_len < aead_ssl3_tag_len(ctx, in_len, extra_in_len)) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); return 0; } @@ -222,7 +241,7 @@ static int aead_ssl3_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, return 0; } tag_len += len; - assert(tag_len <= max_overhead); + assert(tag_len == aead_ssl3_tag_len(ctx, in_len, extra_in_len)); *out_tag_len = tag_len; return 1; @@ -372,6 +391,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha1_ssl3 = { aead_ssl3_seal_scatter, NULL, /* open_gather */ aead_ssl3_get_iv, + aead_ssl3_tag_len, }; static const EVP_AEAD aead_aes_256_cbc_sha1_ssl3 = { @@ -388,6 +408,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha1_ssl3 = { aead_ssl3_seal_scatter, NULL, /* open_gather */ aead_ssl3_get_iv, + aead_ssl3_tag_len, }; static const EVP_AEAD aead_des_ede3_cbc_sha1_ssl3 = { @@ -404,6 +425,7 @@ static const EVP_AEAD aead_des_ede3_cbc_sha1_ssl3 = { aead_ssl3_seal_scatter, NULL, /* open_gather */ aead_ssl3_get_iv, + aead_ssl3_tag_len, }; static const EVP_AEAD aead_null_sha1_ssl3 = { @@ -420,6 +442,7 @@ static const EVP_AEAD aead_null_sha1_ssl3 = { aead_ssl3_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_ssl3_tag_len, }; const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_ssl3(void) { diff --git a/crypto/cipher_extra/e_tls.c b/crypto/cipher_extra/e_tls.c index 14d537712..ca206abd9 100644 --- a/crypto/cipher_extra/e_tls.c +++ b/crypto/cipher_extra/e_tls.c @@ -99,13 +99,33 @@ static int aead_tls_init(EVP_AEAD_CTX *ctx, const uint8_t *key, size_t key_len, return 1; } +static size_t aead_tls_tag_len(const EVP_AEAD_CTX *ctx, const size_t in_len, + const size_t extra_in_len) { + assert(extra_in_len == 0); + AEAD_TLS_CTX *tls_ctx = (AEAD_TLS_CTX *)ctx->aead_state; + + const size_t hmac_len = HMAC_size(&tls_ctx->hmac_ctx); + if (EVP_CIPHER_CTX_mode(&tls_ctx->cipher_ctx) != EVP_CIPH_CBC_MODE) { + // The NULL cipher. + return hmac_len; + } + + const size_t block_size = EVP_CIPHER_CTX_block_size(&tls_ctx->cipher_ctx); + /* An overflow of |in_len + hmac_len| doesn't affect the result mod + * |block_size|, provided that |block_size| is a smaller power of two. */ + assert(block_size != 0 && (block_size & (block_size - 1)) == 0); + const size_t pad_len = block_size - (in_len + hmac_len) % block_size; + return hmac_len + pad_len; +} + static int aead_tls_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag, size_t *out_tag_len, - size_t max_out_tag_len, const uint8_t *nonce, - size_t nonce_len, const uint8_t *in, - size_t in_len, const uint8_t *extra_in, - size_t extra_in_len, const uint8_t *ad, - size_t ad_len) { + const size_t max_out_tag_len, + const uint8_t *nonce, const size_t nonce_len, + const uint8_t *in, const size_t in_len, + const uint8_t *extra_in, + const size_t extra_in_len, const uint8_t *ad, + const size_t ad_len) { AEAD_TLS_CTX *tls_ctx = (AEAD_TLS_CTX *)ctx->aead_state; if (!tls_ctx->cipher_ctx.encrypt) { @@ -120,8 +140,7 @@ static int aead_tls_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, return 0; } - const size_t max_overhead = EVP_AEAD_max_overhead(ctx->aead); - if (max_out_tag_len < max_overhead) { + if (max_out_tag_len < aead_tls_tag_len(ctx, in_len, extra_in_len)) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); return 0; } @@ -173,7 +192,8 @@ static int aead_tls_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, * block from encrypting the input and split the result between |out| and * |out_tag|. Then feed the rest. */ - size_t early_mac_len = (block_size - (in_len % block_size)) % block_size; + const size_t early_mac_len = + (block_size - (in_len % block_size) % block_size); if (early_mac_len != 0) { assert(len + block_size - early_mac_len == in_len); uint8_t buf[EVP_MAX_BLOCK_LENGTH]; @@ -212,8 +232,8 @@ static int aead_tls_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, if (!EVP_EncryptFinal_ex(&tls_ctx->cipher_ctx, out_tag + tag_len, &len)) { return 0; } - tag_len += len; - assert(tag_len <= max_overhead); + assert(len == 0); /* Padding is explicit. */ + assert(tag_len == aead_tls_tag_len(ctx, in_len, extra_in_len)); *out_tag_len = tag_len; return 1; @@ -467,6 +487,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha1_tls = { aead_tls_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_aes_128_cbc_sha1_tls_implicit_iv = { @@ -483,6 +504,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha1_tls_implicit_iv = { aead_tls_seal_scatter, NULL, /* open_gather */ aead_tls_get_iv, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_aes_128_cbc_sha256_tls = { @@ -499,6 +521,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha256_tls = { aead_tls_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_aes_256_cbc_sha1_tls = { @@ -515,6 +538,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha1_tls = { aead_tls_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_aes_256_cbc_sha1_tls_implicit_iv = { @@ -531,6 +555,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha1_tls_implicit_iv = { aead_tls_seal_scatter, NULL, /* open_gather */ aead_tls_get_iv, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_aes_256_cbc_sha256_tls = { @@ -547,6 +572,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha256_tls = { aead_tls_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_aes_256_cbc_sha384_tls = { @@ -563,6 +589,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha384_tls = { aead_tls_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_des_ede3_cbc_sha1_tls = { @@ -579,6 +606,7 @@ static const EVP_AEAD aead_des_ede3_cbc_sha1_tls = { aead_tls_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_des_ede3_cbc_sha1_tls_implicit_iv = { @@ -595,6 +623,7 @@ static const EVP_AEAD aead_des_ede3_cbc_sha1_tls_implicit_iv = { aead_tls_seal_scatter, NULL, /* open_gather */ aead_tls_get_iv, /* get_iv */ + aead_tls_tag_len, }; static const EVP_AEAD aead_null_sha1_tls = { @@ -611,6 +640,7 @@ static const EVP_AEAD aead_null_sha1_tls = { aead_tls_seal_scatter, NULL, /* open_gather */ NULL, /* get_iv */ + aead_tls_tag_len, }; const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_tls(void) { diff --git a/crypto/fipsmodule/cipher/aead.c b/crypto/fipsmodule/cipher/aead.c index 79139e60c..ed302096a 100644 --- a/crypto/fipsmodule/cipher/aead.c +++ b/crypto/fipsmodule/cipher/aead.c @@ -264,3 +264,21 @@ int EVP_AEAD_CTX_get_iv(const EVP_AEAD_CTX *ctx, const uint8_t **out_iv, return ctx->aead->get_iv(ctx, out_iv, out_len); } + +int EVP_AEAD_CTX_tag_len(const EVP_AEAD_CTX *ctx, size_t *out_tag_len, + const size_t in_len, const size_t extra_in_len) { + assert(ctx->aead->seal_scatter_supports_extra_in || !extra_in_len); + + if (ctx->aead->tag_len) { + *out_tag_len = ctx->aead->tag_len(ctx, in_len, extra_in_len); + return 1; + } + + if (extra_in_len + ctx->tag_len < extra_in_len) { + OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW); + *out_tag_len = 0; + return 0; + } + *out_tag_len = extra_in_len + ctx->tag_len; + return 1; +} diff --git a/crypto/fipsmodule/cipher/e_aes.c b/crypto/fipsmodule/cipher/e_aes.c index 7c7521b4c..2c6fc4173 100644 --- a/crypto/fipsmodule/cipher/e_aes.c +++ b/crypto/fipsmodule/cipher/e_aes.c @@ -1217,7 +1217,7 @@ static int aead_aes_gcm_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); return 0; } - if (max_out_tag_len < ctx->tag_len + extra_in_len) { + if (max_out_tag_len < extra_in_len + ctx->tag_len) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); return 0; } @@ -1226,11 +1226,6 @@ static int aead_aes_gcm_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out, return 0; } - if (max_out_tag_len < ctx->tag_len) { - OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); - return 0; - } - const AES_KEY *key = &gcm_ctx->ks.ks; OPENSSL_memcpy(&gcm, &gcm_ctx->gcm, sizeof(gcm)); diff --git a/crypto/fipsmodule/cipher/internal.h b/crypto/fipsmodule/cipher/internal.h index ea59723eb..02335e0dd 100644 --- a/crypto/fipsmodule/cipher/internal.h +++ b/crypto/fipsmodule/cipher/internal.h @@ -107,6 +107,9 @@ struct evp_aead_st { int (*get_iv)(const EVP_AEAD_CTX *ctx, const uint8_t **out_iv, size_t *out_len); + + size_t (*tag_len)(const EVP_AEAD_CTX *ctx, size_t in_Len, + size_t extra_in_len); }; /* aes_ctr_set_key initialises |*aes_key| using |key_bytes| bytes from |key|, diff --git a/include/openssl/aead.h b/include/openssl/aead.h index f0a67d8fc..dd2e4187b 100644 --- a/include/openssl/aead.h +++ b/include/openssl/aead.h @@ -389,6 +389,15 @@ OPENSSL_EXPORT int EVP_AEAD_CTX_init_with_direction( OPENSSL_EXPORT int EVP_AEAD_CTX_get_iv(const EVP_AEAD_CTX *ctx, const uint8_t **out_iv, size_t *out_len); +/* EVP_AEAD_CTX_tag_len computes the exact byte length of the tag written by + * |EVP_AEAD_CTX_seal_scatter| and writes it to |*out_tag_len|. It returns one + * on success or zero on error. |in_len| and |extra_in_len| must equal the + * arguments of the same names passed to |EVP_AEAD_CTX_seal_scatter|. */ +OPENSSL_EXPORT int EVP_AEAD_CTX_tag_len(const EVP_AEAD_CTX *ctx, + size_t *out_tag_len, + const size_t in_len, + const size_t extra_in_len); + #if defined(__cplusplus) } /* extern C */ diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 31af85794..6bbf15d54 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -4620,8 +4620,16 @@ OPENSSL_EXPORT OpenRecordResult OpenRecord(SSL *ssl, Span *out, uint8_t *out_alert, Span in); -OPENSSL_EXPORT size_t SealRecordPrefixLen(SSL *ssl, size_t plaintext_len); -OPENSSL_EXPORT size_t SealRecordMaxSuffixLen(SSL *ssl); +OPENSSL_EXPORT size_t SealRecordPrefixLen(const SSL *ssl, size_t plaintext_len); + +/* SealRecordSuffixLen returns the length of the suffix written by |SealRecord|. + * + * |plaintext_len| must be equal to the size of the plaintext passed to + * |SealRecord|. + * + * |plaintext_len| must not exceed |SSL3_RT_MAX_PLAINTEXT_LENGTH|. The returned + * suffix length will not exceed |SSL3_RT_MAX_ENCRYPTED_OVERHEAD|. */ +OPENSSL_EXPORT size_t SealRecordSuffixLen(const SSL *ssl, size_t plaintext_len); /* *** EXPERIMENTAL -- DO NOT USE *** * @@ -4630,9 +4638,9 @@ OPENSSL_EXPORT size_t SealRecordMaxSuffixLen(SSL *ssl); * returns true on success or false if an error occurred. * * The length of |out_prefix| must equal |SealRecordPrefixLen|. The length of - * |out| must equal the length of |in|. The length of |out_suffix| must equal - * |MaxSealRecordSuffixLen|. |*out_suffix_len| is set to the actual number of - * bytes written to |out_suffix|. + * |out| must equal the length of |in|, which must not exceed + * |SSL3_RT_MAX_PLAINTEXT_LENGTH|. The length of |out_suffix| must equal + * |SealRecordSuffixLen|. * * If enabled, |SealRecord| may perform TLS 1.0 CBC 1/n-1 record splitting. * |SealRecordPrefixLen| accounts for the required overhead if that is the case. @@ -4641,7 +4649,7 @@ OPENSSL_EXPORT size_t SealRecordMaxSuffixLen(SSL *ssl); * |out_prefix| and |out_suffix| may not alias anything. */ OPENSSL_EXPORT bool SealRecord(SSL *ssl, Span out_prefix, Span out, Span out_suffix, - size_t *out_suffix_len, Span in); + Span in); } // namespace bssl diff --git a/ssl/internal.h b/ssl/internal.h index bd1d1129a..cb39a93d7 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -498,10 +498,12 @@ class SSLAEADContext { /* MaxOverhead returns the maximum overhead of calling |Seal|. */ size_t MaxOverhead() const; - /* MaxSuffixLen returns the maximum suffix length written by |SealScatter|. - * |extra_in_len| should equal the argument of the same name passed to - * |SealScatter|. */ - size_t MaxSuffixLen(size_t extra_in_len) const; + /* SuffixLen calculates the suffix length written by |SealScatter| and writes + * it to |*out_suffix_len|. It returns true on success and false on error. + * |in_len| and |extra_in_len| should equal the argument of the same names + * passed to |SealScatter|. */ + bool SuffixLen(size_t *out_suffix_len, size_t in_len, + size_t extra_in_len) const; /* Open authenticates and decrypts |in_len| bytes from |in| in-place. On * success, it sets |*out| to the plaintext in |in| and returns true. @@ -523,19 +525,17 @@ class SSLAEADContext { * success and zero on error. * * On successful return, exactly |ExplicitNonceLen| bytes are written to - * |out_prefix|, |in_len| bytes to |out|, and up to |MaxSuffixLen| bytes to - * |out_suffix|. |*out_suffix_len| is set to the actual number of bytes - * written to |out_suffix|. + * |out_prefix|, |in_len| bytes to |out|, and |SuffixLen| bytes to + * |out_suffix|. * * |extra_in| may point to an additional plaintext buffer. If present, * |extra_in_len| additional bytes are encrypted and authenticated, and the - * ciphertext is written to the beginning of |out_suffix|. |MaxSuffixLen| - * may be used to size |out_suffix| accordingly. + * ciphertext is written to the beginning of |out_suffix|. |SuffixLen| should + * be used to size |out_suffix| accordingly. * * If |in| and |out| alias then |out| must be == |in|. Other arguments may not * alias anything. */ bool SealScatter(uint8_t *out_prefix, uint8_t *out, uint8_t *out_suffix, - size_t *out_suffix_len, size_t max_out_suffix_len, uint8_t type, uint16_t wire_version, const uint8_t seqnum[8], const uint8_t *in, size_t in_len, const uint8_t *extra_in, size_t extra_in_len); diff --git a/ssl/ssl_aead_ctx.cc b/ssl/ssl_aead_ctx.cc index 3b8d1b250..53dff78a3 100644 --- a/ssl/ssl_aead_ctx.cc +++ b/ssl/ssl_aead_ctx.cc @@ -150,15 +150,21 @@ size_t SSLAEADContext::ExplicitNonceLen() const { return 0; } -size_t SSLAEADContext::MaxSuffixLen(size_t extra_in_len) const { - return extra_in_len + - (is_null_cipher() || FUZZER_MODE - ? 0 - : EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(ctx_.get()))); +bool SSLAEADContext::SuffixLen(size_t *out_suffix_len, const size_t in_len, + const size_t extra_in_len) const { + if (is_null_cipher() || FUZZER_MODE) { + *out_suffix_len = extra_in_len; + return true; + } + return !!EVP_AEAD_CTX_tag_len(ctx_.get(), out_suffix_len, in_len, + extra_in_len); } size_t SSLAEADContext::MaxOverhead() const { - return ExplicitNonceLen() + MaxSuffixLen(0); + return ExplicitNonceLen() + + (is_null_cipher() || FUZZER_MODE + ? 0 + : EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(ctx_.get()))); } size_t SSLAEADContext::GetAdditionalData(uint8_t out[13], uint8_t type, @@ -255,18 +261,20 @@ bool SSLAEADContext::Open(CBS *out, uint8_t type, uint16_t wire_version, } bool SSLAEADContext::SealScatter(uint8_t *out_prefix, uint8_t *out, - uint8_t *out_suffix, size_t *out_suffix_len, - size_t max_out_suffix_len, uint8_t type, + uint8_t *out_suffix, uint8_t type, uint16_t wire_version, const uint8_t seqnum[8], const uint8_t *in, size_t in_len, const uint8_t *extra_in, size_t extra_in_len) { - if ((in != out && buffers_alias(in, in_len, out, in_len)) || - buffers_alias(in, in_len, out_suffix, max_out_suffix_len)) { - OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); + const size_t prefix_len = ExplicitNonceLen(); + size_t suffix_len; + if (!SuffixLen(&suffix_len, in_len, extra_in_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); return false; } - if (extra_in_len > max_out_suffix_len) { - OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + if ((in != out && buffers_alias(in, in_len, out, in_len)) || + buffers_alias(in, in_len, out_prefix, prefix_len) || + buffers_alias(in, in_len, out_suffix, suffix_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); return false; } @@ -274,7 +282,6 @@ bool SSLAEADContext::SealScatter(uint8_t *out_prefix, uint8_t *out, /* Handle the initial NULL cipher. */ OPENSSL_memmove(out, in, in_len); OPENSSL_memmove(out_suffix, extra_in, extra_in_len); - *out_suffix_len = extra_in_len; return true; } @@ -327,32 +334,38 @@ bool SSLAEADContext::SealScatter(uint8_t *out_prefix, uint8_t *out, } } - return !!EVP_AEAD_CTX_seal_scatter( - ctx_.get(), out, out_suffix, out_suffix_len, max_out_suffix_len, nonce, + size_t written_suffix_len; + bool result = !!EVP_AEAD_CTX_seal_scatter( + ctx_.get(), out, out_suffix, &written_suffix_len, suffix_len, nonce, nonce_len, in, in_len, extra_in, extra_in_len, ad, ad_len); + assert(!result || written_suffix_len == suffix_len); + return result; } bool SSLAEADContext::Seal(uint8_t *out, size_t *out_len, size_t max_out_len, uint8_t type, uint16_t wire_version, const uint8_t seqnum[8], const uint8_t *in, size_t in_len) { - size_t prefix_len = ExplicitNonceLen(); - if (in_len + prefix_len < in_len) { + const size_t prefix_len = ExplicitNonceLen(); + size_t suffix_len; + if (!SuffixLen(&suffix_len, in_len, 0)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); + return false; + } + if (in_len + prefix_len < in_len || + in_len + prefix_len + suffix_len < in_len + prefix_len) { OPENSSL_PUT_ERROR(CIPHER, SSL_R_RECORD_TOO_LARGE); return false; } - if (in_len + prefix_len > max_out_len) { + if (in_len + prefix_len + suffix_len > max_out_len) { OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); return false; } - size_t suffix_len; - if (!SealScatter(out, out + prefix_len, out + prefix_len + in_len, - &suffix_len, max_out_len - prefix_len - in_len, type, + if (!SealScatter(out, out + prefix_len, out + prefix_len + in_len, type, wire_version, seqnum, in, in_len, 0, 0)) { return false; } - assert(suffix_len <= MaxSuffixLen(0)); *out_len = prefix_len + in_len + suffix_len; return true; } diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 22979647c..4556fb774 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -3773,12 +3773,11 @@ TEST(SSLTest, SealRecord) { const std::vector record = {1, 2, 3, 4, 5}; std::vector prefix( bssl::SealRecordPrefixLen(client.get(), record.size())), - body(record.size()), suffix(bssl::SealRecordMaxSuffixLen(client.get())); - size_t suffix_size; + body(record.size()), + suffix(bssl::SealRecordSuffixLen(client.get(), record.size())); ASSERT_TRUE(bssl::SealRecord(client.get(), bssl::MakeSpan(prefix), bssl::MakeSpan(body), bssl::MakeSpan(suffix), - &suffix_size, record)); - suffix.resize(suffix_size); + record)); std::vector sealed; sealed.insert(sealed.end(), prefix.begin(), prefix.end()); @@ -3819,12 +3818,10 @@ TEST(SSLTest, SealRecordInPlace) { std::vector record = plaintext; std::vector prefix( bssl::SealRecordPrefixLen(client.get(), record.size())), - suffix(bssl::SealRecordMaxSuffixLen(client.get())); - size_t suffix_size; + suffix(bssl::SealRecordSuffixLen(client.get(), record.size())); ASSERT_TRUE(bssl::SealRecord(client.get(), bssl::MakeSpan(prefix), bssl::MakeSpan(record), bssl::MakeSpan(suffix), - &suffix_size, record)); - suffix.resize(suffix_size); + record)); record.insert(record.begin(), prefix.begin(), prefix.end()); record.insert(record.end(), suffix.begin(), suffix.end()); @@ -3860,12 +3857,10 @@ TEST(SSLTest, SealRecordTrailingData) { std::vector record = plaintext; std::vector prefix( bssl::SealRecordPrefixLen(client.get(), record.size())), - suffix(bssl::SealRecordMaxSuffixLen(client.get())); - size_t suffix_size; + suffix(bssl::SealRecordSuffixLen(client.get(), record.size())); ASSERT_TRUE(bssl::SealRecord(client.get(), bssl::MakeSpan(prefix), bssl::MakeSpan(record), bssl::MakeSpan(suffix), - &suffix_size, record)); - suffix.resize(suffix_size); + record)); record.insert(record.begin(), prefix.begin(), prefix.end()); record.insert(record.end(), suffix.begin(), suffix.end()); record.insert(record.end(), {5, 4, 3, 2, 1}); @@ -3901,8 +3896,8 @@ TEST(SSLTest, SealRecordInvalidSpanSize) { std::vector record = {1, 2, 3, 4, 5}; std::vector prefix( bssl::SealRecordPrefixLen(client.get(), record.size())), - suffix(bssl::SealRecordMaxSuffixLen(client.get())); - size_t suffix_size; + body(record.size()), + suffix(bssl::SealRecordSuffixLen(client.get(), record.size())); auto expect_err = []() { int err = ERR_get_error(); @@ -3912,31 +3907,31 @@ TEST(SSLTest, SealRecordInvalidSpanSize) { }; EXPECT_FALSE(bssl::SealRecord( client.get(), bssl::MakeSpan(prefix.data(), prefix.size() - 1), - bssl::MakeSpan(record), bssl::MakeSpan(suffix), &suffix_size, record)); + bssl::MakeSpan(record), bssl::MakeSpan(suffix), record)); expect_err(); EXPECT_FALSE(bssl::SealRecord( client.get(), bssl::MakeSpan(prefix.data(), prefix.size() + 1), - bssl::MakeSpan(record), bssl::MakeSpan(suffix), &suffix_size, record)); + bssl::MakeSpan(record), bssl::MakeSpan(suffix), record)); expect_err(); EXPECT_FALSE( bssl::SealRecord(client.get(), bssl::MakeSpan(prefix), bssl::MakeSpan(record.data(), record.size() - 1), - bssl::MakeSpan(suffix), &suffix_size, record)); + bssl::MakeSpan(suffix), record)); expect_err(); EXPECT_FALSE( bssl::SealRecord(client.get(), bssl::MakeSpan(prefix), bssl::MakeSpan(record.data(), record.size() + 1), - bssl::MakeSpan(suffix), &suffix_size, record)); + bssl::MakeSpan(suffix), record)); expect_err(); EXPECT_FALSE(bssl::SealRecord( client.get(), bssl::MakeSpan(prefix), bssl::MakeSpan(record), - bssl::MakeSpan(suffix.data(), suffix.size() - 1), &suffix_size, record)); + bssl::MakeSpan(suffix.data(), suffix.size() - 1), record)); expect_err(); EXPECT_FALSE(bssl::SealRecord( client.get(), bssl::MakeSpan(prefix), bssl::MakeSpan(record), - bssl::MakeSpan(suffix.data(), suffix.size() + 1), &suffix_size, record)); + bssl::MakeSpan(suffix.data(), suffix.size() + 1), record)); expect_err(); } diff --git a/ssl/tls_record.cc b/ssl/tls_record.cc index 46132e1e5..745ddb8da 100644 --- a/ssl/tls_record.cc +++ b/ssl/tls_record.cc @@ -345,20 +345,35 @@ skipped_data: } static int do_seal_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out, - uint8_t *out_suffix, size_t *out_suffix_len, - const size_t max_out_suffix_len, uint8_t type, - const uint8_t *in, const size_t in_len) { - assert(in == out || !buffers_alias(in, in_len, out, in_len)); - assert(!buffers_alias(in, in_len, out_prefix, ssl_record_prefix_len(ssl))); - assert(!buffers_alias(in, in_len, out_suffix, max_out_suffix_len)); - - /* TLS 1.3 hides the actual record type inside the encrypted data. */ + uint8_t *out_suffix, uint8_t type, const uint8_t *in, + const size_t in_len) { uint8_t *extra_in = NULL; size_t extra_in_len = 0; if (!ssl->s3->aead_write_ctx->is_null_cipher() && ssl->s3->aead_write_ctx->version() >= TLS1_3_VERSION) { + /* TLS 1.3 hides the actual record type inside the encrypted data. */ extra_in = &type; extra_in_len = 1; + } + + size_t suffix_len; + if (!ssl->s3->aead_write_ctx->SuffixLen(&suffix_len, in_len, extra_in_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); + return 0; + } + size_t ciphertext_len = + ssl->s3->aead_write_ctx->ExplicitNonceLen() + suffix_len; + if (ciphertext_len + in_len < ciphertext_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); + return 0; + } + ciphertext_len += in_len; + + assert(in == out || !buffers_alias(in, in_len, out, in_len)); + assert(!buffers_alias(in, in_len, out_prefix, ssl_record_prefix_len(ssl))); + assert(!buffers_alias(in, in_len, out_suffix, suffix_len)); + + if (extra_in_len) { out_prefix[0] = SSL3_RT_APPLICATION_DATA; } else { out_prefix[0] = type; @@ -377,26 +392,17 @@ static int do_seal_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out, } out_prefix[1] = wire_version >> 8; out_prefix[2] = wire_version & 0xff; + out_prefix[3] = ciphertext_len >> 8; + out_prefix[4] = ciphertext_len & 0xff; - /* Write the ciphertext, leaving two bytes for the length. */ - if (!ssl->s3->aead_write_ctx->SealScatter( - out_prefix + SSL3_RT_HEADER_LENGTH, out, out_suffix, out_suffix_len, - max_out_suffix_len, type, wire_version, ssl->s3->write_sequence, in, - in_len, extra_in, extra_in_len) || + if (!ssl->s3->aead_write_ctx->SealScatter(out_prefix + SSL3_RT_HEADER_LENGTH, + out, out_suffix, type, wire_version, + ssl->s3->write_sequence, in, in_len, + extra_in, extra_in_len) || !ssl_record_sequence_update(ssl->s3->write_sequence, 8)) { return 0; } - /* Fill in the length. */ - const size_t ciphertext_len = - ssl->s3->aead_write_ctx->ExplicitNonceLen() + in_len + *out_suffix_len; - if (ciphertext_len >= 1 << 15) { - OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); - return 0; - } - out_prefix[3] = ciphertext_len >> 8; - out_prefix[4] = ciphertext_len & 0xff; - ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, out_prefix, SSL3_RT_HEADER_LENGTH); return 1; @@ -419,28 +425,34 @@ static size_t tls_seal_scatter_prefix_len(const SSL *ssl, uint8_t type, return ret; } -static size_t tls_seal_scatter_max_suffix_len(const SSL *ssl) { - size_t ret = ssl->s3->aead_write_ctx->MaxOverhead(); - /* TLS 1.3 needs an extra byte for the encrypted record type. */ - if (ssl->s3->aead_write_ctx->is_null_cipher() && +static bool tls_seal_scatter_suffix_len(const SSL *ssl, size_t *out_suffix_len, + uint8_t type, size_t in_len) { + size_t extra_in_len = 0; + if (!ssl->s3->aead_write_ctx->is_null_cipher() && ssl->s3->aead_write_ctx->version() >= TLS1_3_VERSION) { - ret += 1; + /* TLS 1.3 adds an extra byte for encrypted record type. */ + extra_in_len = 1; } - return ret; + if (type == SSL3_RT_APPLICATION_DATA && // clang-format off + in_len > 1 && + ssl_needs_record_splitting(ssl)) { + /* With record splitting enabled, the first byte gets sealed into a separate + * record which is written into the prefix. */ + in_len -= 1; + } + return ssl->s3->aead_write_ctx->SuffixLen(out_suffix_len, in_len, extra_in_len); } /* tls_seal_scatter_record seals a new record of type |type| and body |in| and * splits it between |out_prefix|, |out|, and |out_suffix|. Exactly * |tls_seal_scatter_prefix_len| bytes are written to |out_prefix|, |in_len| - * bytes to |out|, and up to |tls_seal_scatter_max_suffix_len| bytes to - * |out_suffix|. |*out_suffix_len| is set to the actual number of bytes written - * to |out_suffix|. It returns one on success and zero on error. If enabled, + * bytes to |out|, and |tls_seal_scatter_suffix_len| bytes to |out_suffix|. It + * returns one on success and zero on error. If enabled, * |tls_seal_scatter_record| implements TLS 1.0 CBC 1/n-1 record splitting and * may write two records concatenated. */ static int tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out, - uint8_t *out_suffix, size_t *out_suffix_len, - size_t max_out_suffix_len, uint8_t type, - const uint8_t *in, size_t in_len) { + uint8_t *out_suffix, uint8_t type, + const uint8_t *in, size_t in_len) { if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 && ssl_needs_record_splitting(ssl)) { assert(ssl->s3->aead_write_ctx->ExplicitNonceLen() == 0); @@ -450,17 +462,17 @@ static int tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out, uint8_t *split_body = out_prefix + prefix_len; uint8_t *split_suffix = split_body + 1; - /* TODO(martinkr): Make AEAD code not complain if max_suffix_len is lower - * than |EVP_AEAD_max_overhead| but still sufficiently large. */ - size_t split_max_suffix_len = ssl->s3->aead_write_ctx->MaxSuffixLen(0); - size_t split_suffix_len = 0; - if (!do_seal_record(ssl, out_prefix, split_body, split_suffix, - &split_suffix_len, split_max_suffix_len, type, in, 1)) { + if (!do_seal_record(ssl, out_prefix, split_body, split_suffix, type, in, + 1)) { return 0; } - size_t split_record_len = prefix_len + 1 + split_suffix_len; - + size_t split_record_suffix_len; + if (!ssl->s3->aead_write_ctx->SuffixLen(&split_record_suffix_len, 1, 0)) { + assert(false); + return 0; + } + const size_t split_record_len = prefix_len + 1 + split_record_suffix_len; assert(SSL3_RT_HEADER_LENGTH + ssl_cipher_get_record_split_len( ssl->s3->aead_write_ctx->cipher()) == split_record_len); @@ -468,8 +480,8 @@ static int tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out, /* Write the n-1-byte fragment. The header gets split between |out_prefix| * (header[:-1]) and |out| (header[-1:]). */ uint8_t tmp_prefix[SSL3_RT_HEADER_LENGTH]; - if (!do_seal_record(ssl, tmp_prefix, out + 1, out_suffix, out_suffix_len, - max_out_suffix_len, type, in + 1, in_len - 1)) { + if (!do_seal_record(ssl, tmp_prefix, out + 1, out_suffix, type, in + 1, + in_len - 1)) { return 0; } assert(tls_seal_scatter_prefix_len(ssl, type, in_len) == @@ -480,8 +492,7 @@ static int tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out, return 1; } - return do_seal_record(ssl, out_prefix, out, out_suffix, out_suffix_len, - max_out_suffix_len, type, in, in_len); + return do_seal_record(ssl, out_prefix, out, out_suffix, type, in, in_len); } int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len, @@ -492,12 +503,16 @@ int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len, } const size_t prefix_len = tls_seal_scatter_prefix_len(ssl, type, in_len); - - if (in_len + prefix_len < in_len) { + size_t suffix_len; + if (!tls_seal_scatter_suffix_len(ssl, &suffix_len, type, in_len)) { + return false; + } + if (in_len + prefix_len < in_len || + prefix_len + in_len + suffix_len < prefix_len + in_len) { OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); return 0; } - if (max_out_len < in_len + prefix_len) { + if (max_out_len < in_len + prefix_len + suffix_len) { OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); return 0; } @@ -505,16 +520,7 @@ int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len, uint8_t *prefix = out; uint8_t *body = out + prefix_len; uint8_t *suffix = body + in_len; - size_t max_suffix_len = max_out_len - prefix_len - in_len; - size_t suffix_len = 0; - - if (!tls_seal_scatter_record(ssl, prefix, body, suffix, &suffix_len, - max_suffix_len, type, in, in_len)) { - return 0; - } - - if (prefix_len + in_len + suffix_len < prefix_len + in_len) { - OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); + if (!tls_seal_scatter_record(ssl, prefix, body, suffix, type, in, in_len)) { return 0; } @@ -630,17 +636,26 @@ OpenRecordResult OpenRecord(SSL *ssl, Span *out, return ret; } -size_t SealRecordPrefixLen(SSL *ssl, size_t record_len) { +size_t SealRecordPrefixLen(const SSL *ssl, const size_t record_len) { return tls_seal_scatter_prefix_len(ssl, SSL3_RT_APPLICATION_DATA, record_len); } -size_t SealRecordMaxSuffixLen(SSL *ssl) { - return tls_seal_scatter_max_suffix_len(ssl); +size_t SealRecordSuffixLen(const SSL *ssl, const size_t plaintext_len) { + assert(plaintext_len <= SSL3_RT_MAX_PLAIN_LENGTH); + size_t suffix_len; + if (!tls_seal_scatter_suffix_len(ssl, &suffix_len, SSL3_RT_APPLICATION_DATA, + plaintext_len)) { + assert(false); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + assert(suffix_len <= SSL3_RT_MAX_ENCRYPTED_OVERHEAD); + return suffix_len; } bool SealRecord(SSL *ssl, const Span out_prefix, const Span out, Span out_suffix, - size_t *out_suffix_len, const Span in) { + const Span in) { // This API is a work in progress and currently only works for TLS 1.2 servers // and below. if (SSL_in_init(ssl) || @@ -653,13 +668,13 @@ bool SealRecord(SSL *ssl, const Span out_prefix, if (out_prefix.size() != SealRecordPrefixLen(ssl, in.size()) || out.size() != in.size() || - out_suffix.size() != SealRecordMaxSuffixLen(ssl)) { + out_suffix.size() != SealRecordSuffixLen(ssl, in.size())) { OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); return false; } - return tls_seal_scatter_record( - ssl, out_prefix.data(), out.data(), out_suffix.data(), out_suffix_len, - out_suffix.size(), SSL3_RT_APPLICATION_DATA, in.data(), in.size()); + return tls_seal_scatter_record(ssl, out_prefix.data(), out.data(), + out_suffix.data(), SSL3_RT_APPLICATION_DATA, + in.data(), in.size()); } } // namespace bssl