Provide standalone version of ssl_encrypt_buf

The previous version of the record encryption function
`ssl_encrypt_buf` takes the entire SSL context as an argument,
while intuitively, it should only depend on the current security
parameters and the record buffer.

Analyzing the exact dependencies, it turned out that in addition
to the currently active `ssl_transform` instance and the record
information, the encryption function needs access to
- the negotiated protocol version, and
- the status of the encrypt-then-MAC extension.

This commit moves these two fields into `ssl_transform` and
changes the signature of `ssl_encrypt_buf` to only use an instance
of `ssl_transform` and an instance of the new `ssl_record` type.
The `ssl_context` instance is *solely* kept for the debugging macros
which need an SSL context instance.

The benefit of the change is twofold:
1) It avoids the need of the MPS to deal with instances of
   `ssl_context`. The MPS should only work with records and
   opaque security parameters, which is what the change in
   this commit makes progress towards.
2) It significantly eases testing of the encryption function:
   independent of any SSL context, the encryption function can
   be passed some record buffer to encrypt alongside some arbitrary
   choice of parameters, and e.g. be checked to not overflow the
   provided memory.
This commit is contained in:
Hanno Becker 2017-12-27 21:37:21 +00:00
parent d362dc504d
commit 9eddaebda5
2 changed files with 221 additions and 141 deletions

View file

@ -581,6 +581,12 @@ struct mbedtls_ssl_transform
mbedtls_cipher_context_t cipher_ctx_enc; /*!< encryption context */ mbedtls_cipher_context_t cipher_ctx_enc; /*!< encryption context */
mbedtls_cipher_context_t cipher_ctx_dec; /*!< decryption context */ mbedtls_cipher_context_t cipher_ctx_dec; /*!< decryption context */
#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
int encrypt_then_mac; /*!< flag for EtM activation */
#endif
int minor_ver;
/* /*
* Session specific compression layer * Session specific compression layer
*/ */
@ -593,11 +599,6 @@ struct mbedtls_ssl_transform
/* /*
* Internal representation of record frames * Internal representation of record frames
* *
* The header layout is chosen to facilitate the computation of
* authentication tags which often use the header bytes laid out
* exactly as in the struct; note that it does not match what's
* transferred on the wire.
*
* Instances come in two flavors: * Instances come in two flavors:
* (1) Encrypted * (1) Encrypted
* These always have data_offset = 0 * These always have data_offset = 0
@ -617,7 +618,6 @@ typedef struct
uint8_t ctr[8]; /*!< Record sequence number */ uint8_t ctr[8]; /*!< Record sequence number */
uint8_t type; /*!< Record type */ uint8_t type; /*!< Record type */
uint8_t ver[2]; /*!< SSL/TLS version */ uint8_t ver[2]; /*!< SSL/TLS version */
uint8_t len[2]; /*!< Content length, little endian */
unsigned char *buf; /*!< Memory buffer enclosing the record content */ unsigned char *buf; /*!< Memory buffer enclosing the record content */
size_t buf_len; /*!< Buffer length */ size_t buf_len; /*!< Buffer length */

View file

@ -757,6 +757,10 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> derive keys" ) ); MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> derive keys" ) );
#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
transform->encrypt_then_mac = session->encrypt_then_mac;
#endif
transform->minor_ver = ssl->minor_ver;
ciphersuite_info = handshake->ciphersuite_info; ciphersuite_info = handshake->ciphersuite_info;
cipher_info = mbedtls_cipher_info_from_type( ciphersuite_info->cipher ); cipher_info = mbedtls_cipher_info_from_type( ciphersuite_info->cipher );
@ -1734,23 +1738,75 @@ static void ssl_read_memory( unsigned char *p, size_t len )
/* /*
* Encryption/decryption functions * Encryption/decryption functions
*/ */
static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
static void ssl_extract_add_data_from_record( unsigned char* add_data,
mbedtls_record *rec )
{
memcpy( add_data, rec->ctr, sizeof( rec->ctr ) );
add_data[8] = rec->type;
memcpy( add_data + 9, rec->ver, sizeof( rec->ver ) );
add_data[11] = ( rec->data_len >> 8 ) & 0xFF;
add_data[12] = rec->data_len & 0xFF;
}
static int ssl_encrypt_buf( mbedtls_ssl_context *ssl,
mbedtls_ssl_transform *transform,
mbedtls_record *rec,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng )
{ {
mbedtls_cipher_mode_t mode; mbedtls_cipher_mode_t mode;
int auth_done = 0; int auth_done = 0;
unsigned char * data;
unsigned char add_data[13];
size_t post_avail;
/* The SSL context is only used for debugging purposes! */
#if !defined(MBEDTLS_SSL_DEBUG_C)
((void) ssl);
#endif
/* The PRNG is used for dynamic IV generation that's used
* for CBC transformations in TLS 1.1 and TLS 1.2. */
#if !( defined(MBEDTLS_CIPHER_MODE_CBC) && \
( defined(MBEDTLS_AES_C) || \
defined(MBEDTLS_ARIA_C) || \
defined(MBEDTLS_CAMELLIA_C) ) && \
( defined(MBEDTLS_SSL_PROTO_TLS1_1) || defined(MBEDTLS_SSL_PROTO_TLS1_2) ) )
((void) f_rng);
((void) p_rng);
#endif
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> encrypt buf" ) ); MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> encrypt buf" ) );
if( ssl->session_out == NULL || ssl->transform_out == NULL ) if( transform == NULL )
{ {
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); MBEDTLS_SSL_DEBUG_MSG( 1, ( "no transform provided to encrypt_buf" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
if( rec == NULL ||
rec->buf == NULL ||
rec->buf_len < rec->data_offset ||
rec->buf_len - rec->data_offset < rec->data_len )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad record structure provided to encrypt_buf" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
} }
mode = mbedtls_cipher_get_cipher_mode( &ssl->transform_out->cipher_ctx_enc ); post_avail = rec->buf_len - ( rec->data_len + rec->data_offset );
data = rec->buf + rec->data_offset;
MBEDTLS_SSL_DEBUG_BUF( 4, "before encrypt: output payload", MBEDTLS_SSL_DEBUG_BUF( 4, "before encrypt: output payload",
ssl->out_msg, ssl->out_msglen ); data, rec->data_len );
mode = mbedtls_cipher_get_cipher_mode( &transform->cipher_ctx_enc );
if( rec->data_len > MBEDTLS_SSL_OUT_CONTENT_LEN )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "Record content %u too large, maximum %d",
(unsigned) rec->data_len,
MBEDTLS_SSL_OUT_CONTENT_LEN ) );
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
}
/* /*
* Add MAC before if needed * Add MAC before if needed
@ -1759,40 +1815,42 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
if( mode == MBEDTLS_MODE_STREAM || if( mode == MBEDTLS_MODE_STREAM ||
( mode == MBEDTLS_MODE_CBC ( mode == MBEDTLS_MODE_CBC
#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) #if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
&& ssl->session_out->encrypt_then_mac == MBEDTLS_SSL_ETM_DISABLED && transform->encrypt_then_mac == MBEDTLS_SSL_ETM_DISABLED
#endif #endif
) ) ) )
{ {
if( post_avail < transform->maclen )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "Buffer provided for encrypted record not large enough" ) );
return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
}
#if defined(MBEDTLS_SSL_PROTO_SSL3) #if defined(MBEDTLS_SSL_PROTO_SSL3)
if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 ) if( transform->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 )
{ {
unsigned char mac[SSL_MAC_MAX_BYTES]; unsigned char mac[SSL_MAC_MAX_BYTES];
ssl_mac( &transform->md_ctx_enc, transform->mac_enc,
ssl_mac( &ssl->transform_out->md_ctx_enc, data, rec->data_len, rec->ctr, rec->type, mac );
ssl->transform_out->mac_enc, memcpy( data + rec->data_len, mac, transform->maclen );
ssl->out_msg, ssl->out_msglen,
ssl->out_ctr, ssl->out_msgtype,
mac );
memcpy( ssl->out_msg + ssl->out_msglen, mac, ssl->transform_out->maclen );
} }
else else
#endif #endif
#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \ #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
defined(MBEDTLS_SSL_PROTO_TLS1_2) defined(MBEDTLS_SSL_PROTO_TLS1_2)
if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 ) if( transform->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 )
{ {
unsigned char mac[MBEDTLS_SSL_MAC_ADD]; unsigned char mac[MBEDTLS_SSL_MAC_ADD];
mbedtls_md_hmac_update( &ssl->transform_out->md_ctx_enc, ssl->out_ctr, 8 ); ssl_extract_add_data_from_record( add_data, rec );
mbedtls_md_hmac_update( &ssl->transform_out->md_ctx_enc, ssl->out_hdr, 3 );
mbedtls_md_hmac_update( &ssl->transform_out->md_ctx_enc, ssl->out_len, 2 );
mbedtls_md_hmac_update( &ssl->transform_out->md_ctx_enc,
ssl->out_msg, ssl->out_msglen );
mbedtls_md_hmac_finish( &ssl->transform_out->md_ctx_enc, mac );
mbedtls_md_hmac_reset( &ssl->transform_out->md_ctx_enc );
memcpy( ssl->out_msg + ssl->out_msglen, mac, ssl->transform_out->maclen ); mbedtls_md_hmac_update( &transform->md_ctx_enc, add_data,
sizeof( add_data ) );
mbedtls_md_hmac_update( &transform->md_ctx_enc,
data, rec->data_len );
mbedtls_md_hmac_finish( &transform->md_ctx_enc, mac );
mbedtls_md_hmac_reset( &transform->md_ctx_enc );
memcpy( data + rec->data_len, mac, transform->maclen );
} }
else else
#endif #endif
@ -1801,11 +1859,11 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
} }
MBEDTLS_SSL_DEBUG_BUF( 4, "computed mac", MBEDTLS_SSL_DEBUG_BUF( 4, "computed mac", data + rec->data_len,
ssl->out_msg + ssl->out_msglen, transform->maclen );
ssl->transform_out->maclen );
ssl->out_msglen += ssl->transform_out->maclen; rec->data_len += transform->maclen;
post_avail -= transform->maclen;
auth_done++; auth_done++;
} }
#endif /* MBEDTLS_SSL_SOME_MODES_USE_MAC */ #endif /* MBEDTLS_SSL_SOME_MODES_USE_MAC */
@ -1817,23 +1875,21 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
if( mode == MBEDTLS_MODE_STREAM ) if( mode == MBEDTLS_MODE_STREAM )
{ {
int ret; int ret;
size_t olen = 0; size_t olen;
MBEDTLS_SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " MBEDTLS_SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, "
"including %d bytes of padding", "including %d bytes of padding",
ssl->out_msglen, 0 ) ); rec->data_len, 0 ) );
if( ( ret = mbedtls_cipher_crypt( &ssl->transform_out->cipher_ctx_enc, if( ( ret = mbedtls_cipher_crypt( &transform->cipher_ctx_enc,
ssl->transform_out->iv_enc, transform->iv_enc, transform->ivlen,
ssl->transform_out->ivlen, data, rec->data_len,
ssl->out_msg, ssl->out_msglen, data, &olen ) ) != 0 )
ssl->out_msg, &olen ) ) != 0 )
{ {
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_crypt", ret ); MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_crypt", ret );
return( ret ); return( ret );
} }
if( ssl->out_msglen != olen ) if( rec->data_len != olen )
{ {
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
@ -1849,24 +1905,17 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
mode == MBEDTLS_MODE_CHACHAPOLY ) mode == MBEDTLS_MODE_CHACHAPOLY )
{ {
int ret; int ret;
size_t enc_msglen, olen;
unsigned char *enc_msg;
unsigned char add_data[13];
unsigned char iv[12]; unsigned char iv[12];
mbedtls_ssl_transform *transform = ssl->transform_out; size_t explicit_iv_len = transform->ivlen - transform->fixed_ivlen;
size_t explicit_ivlen = transform->ivlen - transform->fixed_ivlen;
/* /* Check that there's space for both the authentication tag
* Prepare additional authenticated data * and the explicit IV before and after the record content. */
*/ if( post_avail < transform->taglen ||
memcpy( add_data, ssl->out_ctr, 8 ); rec->data_offset < explicit_iv_len )
add_data[8] = ssl->out_msgtype; {
mbedtls_ssl_write_version( ssl->major_ver, ssl->minor_ver, MBEDTLS_SSL_DEBUG_MSG( 1, ( "Buffer provided for encrypted record not large enough" ) );
ssl->conf->transport, add_data + 9 ); return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
add_data[11] = ( ssl->out_msglen >> 8 ) & 0xFF; }
add_data[12] = ssl->out_msglen & 0xFF;
MBEDTLS_SSL_DEBUG_BUF( 4, "additional data for AEAD", add_data, 13 );
/* /*
* Generate IV * Generate IV
@ -1875,9 +1924,10 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
{ {
/* GCM and CCM: fixed || explicit (=seqnum) */ /* GCM and CCM: fixed || explicit (=seqnum) */
memcpy( iv, transform->iv_enc, transform->fixed_ivlen ); memcpy( iv, transform->iv_enc, transform->fixed_ivlen );
memcpy( iv + transform->fixed_ivlen, ssl->out_ctr, 8 ); memcpy( iv + transform->fixed_ivlen, rec->ctr,
memcpy( ssl->out_iv, ssl->out_ctr, 8 ); explicit_iv_len );
/* Prefix record content with explicit IV. */
memcpy( data - explicit_iv_len, rec->ctr, explicit_iv_len );
} }
else if( transform->ivlen == 12 && transform->fixed_ivlen == 12 ) else if( transform->ivlen == 12 && transform->fixed_ivlen == 12 )
{ {
@ -1887,7 +1937,7 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
memcpy( iv, transform->iv_enc, transform->fixed_ivlen ); memcpy( iv, transform->iv_enc, transform->fixed_ivlen );
for( i = 0; i < 8; i++ ) for( i = 0; i < 8; i++ )
iv[i+4] ^= ssl->out_ctr[i]; iv[i+4] ^= rec->ctr[i];
} }
else else
{ {
@ -1899,45 +1949,36 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
MBEDTLS_SSL_DEBUG_BUF( 4, "IV used (internal)", MBEDTLS_SSL_DEBUG_BUF( 4, "IV used (internal)",
iv, transform->ivlen ); iv, transform->ivlen );
MBEDTLS_SSL_DEBUG_BUF( 4, "IV used (transmitted)", MBEDTLS_SSL_DEBUG_BUF( 4, "IV used (transmitted)",
ssl->out_iv, explicit_ivlen ); data - explicit_iv_len, explicit_iv_len );
MBEDTLS_SSL_DEBUG_BUF( 4, "additional data used for AEAD",
/* add_data, 13 );
* Fix message length with added IV
*/
enc_msg = ssl->out_msg;
enc_msglen = ssl->out_msglen;
ssl->out_msglen += explicit_ivlen;
MBEDTLS_SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " MBEDTLS_SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, "
"including 0 bytes of padding", "including 0 bytes of padding",
ssl->out_msglen ) ); rec->data_len ) );
/* /*
* Encrypt and authenticate * Encrypt and authenticate
*/ */
ssl_extract_add_data_from_record( add_data, rec );
if( ( ret = mbedtls_cipher_auth_encrypt( &transform->cipher_ctx_enc, if( ( ret = mbedtls_cipher_auth_encrypt( &transform->cipher_ctx_enc,
iv, transform->ivlen, iv, transform->ivlen,
add_data, 13, add_data, 13, /* add data */
enc_msg, enc_msglen, data, rec->data_len, /* source */
enc_msg, &olen, data, &rec->data_len, /* destination */
enc_msg + enc_msglen, data + rec->data_len, transform->taglen ) ) != 0 )
ssl->transform_out->taglen ) ) != 0 )
{ {
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_auth_encrypt", ret ); MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_auth_encrypt", ret );
return( ret ); return( ret );
} }
if( olen != enc_msglen ) MBEDTLS_SSL_DEBUG_BUF( 4, "after encrypt: tag",
{ data + rec->data_len, transform->taglen );
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
ssl->out_msglen += ssl->transform_out->taglen; rec->data_len += transform->taglen + explicit_iv_len;
rec->data_offset -= explicit_iv_len;
post_avail -= transform->taglen;
auth_done++; auth_done++;
MBEDTLS_SSL_DEBUG_BUF( 4, "after encrypt: tag", enc_msg + enc_msglen,
ssl->transform_out->taglen );
} }
else else
#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C */ #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C */
@ -1946,81 +1987,97 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
if( mode == MBEDTLS_MODE_CBC ) if( mode == MBEDTLS_MODE_CBC )
{ {
int ret; int ret;
unsigned char *enc_msg; size_t padlen, i;
size_t enc_msglen, padlen, olen = 0, i; size_t olen;
padlen = ssl->transform_out->ivlen - ( ssl->out_msglen + 1 ) % /* Currently we're always using minimal padding
ssl->transform_out->ivlen; * (up to 255 bytes would be allowed). */
if( padlen == ssl->transform_out->ivlen ) padlen = transform->ivlen - ( rec->data_len + 1 ) % transform->ivlen;
if( padlen == transform->ivlen )
padlen = 0; padlen = 0;
/* Check there's enough space in the buffer for the padding. */
if( post_avail < padlen + 1 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "Buffer provided for encrypted record not large enough" ) );
return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
}
for( i = 0; i <= padlen; i++ ) for( i = 0; i <= padlen; i++ )
ssl->out_msg[ssl->out_msglen + i] = (unsigned char) padlen; data[rec->data_len + i] = (unsigned char) padlen;
ssl->out_msglen += padlen + 1; rec->data_len += padlen + 1;
post_avail -= padlen + 1;
enc_msglen = ssl->out_msglen;
enc_msg = ssl->out_msg;
#if defined(MBEDTLS_SSL_PROTO_TLS1_1) || defined(MBEDTLS_SSL_PROTO_TLS1_2) #if defined(MBEDTLS_SSL_PROTO_TLS1_1) || defined(MBEDTLS_SSL_PROTO_TLS1_2)
/* /*
* Prepend per-record IV for block cipher in TLS v1.1 and up as per * Prepend per-record IV for block cipher in TLS v1.1 and up as per
* Method 1 (6.2.3.2. in RFC4346 and RFC5246) * Method 1 (6.2.3.2. in RFC4346 and RFC5246)
*/ */
if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_2 ) if( transform->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_2 )
{ {
if( f_rng == NULL )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "No PRNG provided to encrypt_record routine" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
if( rec->data_offset < transform->ivlen )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "Buffer provided for encrypted record not large enough" ) );
return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
}
/* /*
* Generate IV * Generate IV
*/ */
ret = ssl->conf->f_rng( ssl->conf->p_rng, ssl->transform_out->iv_enc, ret = f_rng( p_rng, transform->iv_enc, transform->ivlen );
ssl->transform_out->ivlen );
if( ret != 0 ) if( ret != 0 )
return( ret ); return( ret );
memcpy( ssl->out_iv, ssl->transform_out->iv_enc, memcpy( data - transform->ivlen, transform->iv_enc,
ssl->transform_out->ivlen ); transform->ivlen );
/*
* Fix pointer positions and message length with added IV
*/
enc_msg = ssl->out_msg;
enc_msglen = ssl->out_msglen;
ssl->out_msglen += ssl->transform_out->ivlen;
} }
#endif /* MBEDTLS_SSL_PROTO_TLS1_1 || MBEDTLS_SSL_PROTO_TLS1_2 */ #endif /* MBEDTLS_SSL_PROTO_TLS1_1 || MBEDTLS_SSL_PROTO_TLS1_2 */
MBEDTLS_SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " MBEDTLS_SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, "
"including %d bytes of IV and %d bytes of padding", "including %d bytes of IV and %d bytes of padding",
ssl->out_msglen, ssl->transform_out->ivlen, rec->data_len, transform->ivlen,
padlen + 1 ) ); padlen + 1 ) );
if( ( ret = mbedtls_cipher_crypt( &ssl->transform_out->cipher_ctx_enc, if( ( ret = mbedtls_cipher_crypt( &transform->cipher_ctx_enc,
ssl->transform_out->iv_enc, transform->iv_enc,
ssl->transform_out->ivlen, transform->ivlen,
enc_msg, enc_msglen, data, rec->data_len,
enc_msg, &olen ) ) != 0 ) data, &olen ) ) != 0 )
{ {
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_crypt", ret ); MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_cipher_crypt", ret );
return( ret ); return( ret );
} }
if( enc_msglen != olen ) if( rec->data_len != olen )
{ {
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
} }
#if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) #if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1)
if( ssl->minor_ver < MBEDTLS_SSL_MINOR_VERSION_2 ) if( transform->minor_ver < MBEDTLS_SSL_MINOR_VERSION_2 )
{ {
/* /*
* Save IV in SSL3 and TLS1 * Save IV in SSL3 and TLS1
*/ */
memcpy( ssl->transform_out->iv_enc, memcpy( transform->iv_enc, transform->cipher_ctx_enc.iv,
ssl->transform_out->cipher_ctx_enc.iv, transform->ivlen );
ssl->transform_out->ivlen );
} }
else
#endif #endif
{
data -= transform->ivlen;
rec->data_offset -= transform->ivlen;
rec->data_len += transform->ivlen;
}
#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) #if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
if( auth_done == 0 ) if( auth_done == 0 )
@ -2035,27 +2092,30 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl )
* IV + // except for TLS 1.0 * IV + // except for TLS 1.0
* ENC(content + padding + padding_length)); * ENC(content + padding + padding_length));
*/ */
unsigned char pseudo_hdr[13];
if( post_avail < transform->maclen)
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "Buffer provided for encrypted record not large enough" ) );
return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
}
MBEDTLS_SSL_DEBUG_MSG( 3, ( "using encrypt then mac" ) ); MBEDTLS_SSL_DEBUG_MSG( 3, ( "using encrypt then mac" ) );
MBEDTLS_SSL_DEBUG_BUF( 4, "MAC'd meta-data", add_data,
sizeof( add_data ) );
memcpy( pseudo_hdr + 0, ssl->out_ctr, 8 ); ssl_extract_add_data_from_record( add_data, rec );
memcpy( pseudo_hdr + 8, ssl->out_hdr, 3 );
pseudo_hdr[11] = (unsigned char)( ( ssl->out_msglen >> 8 ) & 0xFF );
pseudo_hdr[12] = (unsigned char)( ( ssl->out_msglen ) & 0xFF );
MBEDTLS_SSL_DEBUG_BUF( 4, "MAC'd meta-data", pseudo_hdr, 13 ); mbedtls_md_hmac_update( &transform->md_ctx_enc, add_data,
sizeof( add_data ) );
mbedtls_md_hmac_update( &transform->md_ctx_enc,
data, rec->data_len );
mbedtls_md_hmac_finish( &transform->md_ctx_enc, mac );
mbedtls_md_hmac_reset( &transform->md_ctx_enc );
mbedtls_md_hmac_update( &ssl->transform_out->md_ctx_enc, pseudo_hdr, 13 ); memcpy( data + rec->data_len, mac, transform->maclen );
mbedtls_md_hmac_update( &ssl->transform_out->md_ctx_enc,
ssl->out_iv, ssl->out_msglen );
mbedtls_md_hmac_finish( &ssl->transform_out->md_ctx_enc, mac );
mbedtls_md_hmac_reset( &ssl->transform_out->md_ctx_enc );
memcpy( ssl->out_iv + ssl->out_msglen, mac, rec->data_len += transform->maclen;
ssl->transform_out->maclen ); post_avail -= transform->maclen;
ssl->out_msglen += ssl->transform_out->maclen;
auth_done++; auth_done++;
} }
#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ #endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */
@ -3684,15 +3744,35 @@ int mbedtls_ssl_write_record( mbedtls_ssl_context *ssl, uint8_t force_flush )
if( ssl->transform_out != NULL ) if( ssl->transform_out != NULL )
{ {
if( ( ret = ssl_encrypt_buf( ssl ) ) != 0 ) mbedtls_record rec;
rec.buf = ssl->out_iv;
rec.buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN -
( ssl->out_iv - ssl->out_buf );
rec.data_len = ssl->out_msglen;
rec.data_offset = ssl->out_msg - rec.buf;
memcpy( &rec.ctr[0], ssl->out_ctr, 8 );
mbedtls_ssl_write_version( ssl->major_ver, ssl->minor_ver,
ssl->conf->transport, rec.ver );
rec.type = ssl->out_msgtype;
if( ( ret = ssl_encrypt_buf( ssl, ssl->transform_out, &rec,
ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 )
{ {
MBEDTLS_SSL_DEBUG_RET( 1, "ssl_encrypt_buf", ret ); MBEDTLS_SSL_DEBUG_RET( 1, "ssl_encrypt_buf", ret );
return( ret ); return( ret );
} }
len = ssl->out_msglen; if( rec.data_offset != 0 )
ssl->out_len[0] = (unsigned char)( len >> 8 ); {
ssl->out_len[1] = (unsigned char)( len ); MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
ssl->out_msglen = rec.data_len;
ssl->out_len[0] = (unsigned char)( rec.data_len >> 8 );
ssl->out_len[1] = (unsigned char)( rec.data_len );
} }
protected_record_size = len + mbedtls_ssl_hdr_len( ssl ); protected_record_size = len + mbedtls_ssl_hdr_len( ssl );