mirror of
https://github.com/ipxe/ipxe
synced 2026-02-14 02:31:26 +03:00
[tls] Allow for arbitrary-length initialisation vectors
Restructure the encryption and decryption operations to allow for the use of ciphers where the initialisation vector is constructed by concatenating the fixed IV (derived as part of key expansion) with a record IV (prepended to the ciphertext). Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -249,6 +249,10 @@ static inline int is_stream_cipher ( struct cipher_algorithm *cipher ) {
|
|||||||
return ( cipher->blocksize == 1 );
|
return ( cipher->blocksize == 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_block_cipher ( struct cipher_algorithm *cipher ) {
|
||||||
|
return ( cipher->blocksize > 1 );
|
||||||
|
}
|
||||||
|
|
||||||
static inline int is_auth_cipher ( struct cipher_algorithm *cipher ) {
|
static inline int is_auth_cipher ( struct cipher_algorithm *cipher ) {
|
||||||
return cipher->authsize;
|
return cipher->authsize;
|
||||||
}
|
}
|
||||||
|
|||||||
342
src/net/tls.c
342
src/net/tls.c
@@ -86,14 +86,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||||||
#define EINFO_EINVAL_HANDSHAKE \
|
#define EINFO_EINVAL_HANDSHAKE \
|
||||||
__einfo_uniqify ( EINFO_EINVAL, 0x08, \
|
__einfo_uniqify ( EINFO_EINVAL, 0x08, \
|
||||||
"Invalid Handshake record" )
|
"Invalid Handshake record" )
|
||||||
#define EINVAL_STREAM __einfo_error ( EINFO_EINVAL_STREAM )
|
#define EINVAL_IV __einfo_error ( EINFO_EINVAL_IV )
|
||||||
#define EINFO_EINVAL_STREAM \
|
#define EINFO_EINVAL_IV \
|
||||||
__einfo_uniqify ( EINFO_EINVAL, 0x09, \
|
|
||||||
"Invalid stream-ciphered record" )
|
|
||||||
#define EINVAL_BLOCK __einfo_error ( EINFO_EINVAL_BLOCK )
|
|
||||||
#define EINFO_EINVAL_BLOCK \
|
|
||||||
__einfo_uniqify ( EINFO_EINVAL, 0x0a, \
|
__einfo_uniqify ( EINFO_EINVAL, 0x0a, \
|
||||||
"Invalid block-ciphered record" )
|
"Invalid initialisation vector" )
|
||||||
#define EINVAL_PADDING __einfo_error ( EINFO_EINVAL_PADDING )
|
#define EINVAL_PADDING __einfo_error ( EINFO_EINVAL_PADDING )
|
||||||
#define EINFO_EINVAL_PADDING \
|
#define EINFO_EINVAL_PADDING \
|
||||||
__einfo_uniqify ( EINFO_EINVAL, 0x0b, \
|
__einfo_uniqify ( EINFO_EINVAL, 0x0b, \
|
||||||
@@ -2581,86 +2577,26 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate and assemble stream-ciphered record from data and MAC portions
|
* Calculate HMAC over list of I/O buffers
|
||||||
*
|
*
|
||||||
* @v tls TLS connection
|
* @v cipherspec Cipher specification
|
||||||
* @ret data Data
|
* @v authhdr Authentication header
|
||||||
* @ret len Length of data
|
* @v list List of I/O buffers
|
||||||
* @ret digest MAC digest
|
* @v mac HMAC to fill in
|
||||||
* @ret plaintext_len Length of plaintext record
|
|
||||||
* @ret plaintext Allocated plaintext record
|
|
||||||
*/
|
*/
|
||||||
static void * __malloc
|
static void tls_hmac_list ( struct tls_cipherspec *cipherspec,
|
||||||
tls_assemble_stream ( struct tls_connection *tls, const void *data, size_t len,
|
struct tls_auth_header *authhdr,
|
||||||
void *digest, size_t *plaintext_len ) {
|
struct list_head *list, void *hmac ) {
|
||||||
size_t mac_len = tls->tx_cipherspec.suite->mac_len;
|
struct digest_algorithm *digest = cipherspec->suite->digest;
|
||||||
void *plaintext;
|
uint8_t ctx[ hmac_ctxsize ( digest ) ];
|
||||||
void *content;
|
struct io_buffer *iobuf;
|
||||||
void *mac;
|
|
||||||
|
|
||||||
/* Calculate stream-ciphered struct length */
|
tls_hmac_init ( cipherspec, ctx, authhdr );
|
||||||
*plaintext_len = ( len + mac_len );
|
list_for_each_entry ( iobuf, list, list ) {
|
||||||
|
tls_hmac_update ( cipherspec, ctx, iobuf->data,
|
||||||
/* Allocate stream-ciphered struct */
|
iob_len ( iobuf ) );
|
||||||
plaintext = malloc ( *plaintext_len );
|
}
|
||||||
if ( ! plaintext )
|
tls_hmac_final ( cipherspec, ctx, hmac );
|
||||||
return NULL;
|
|
||||||
content = plaintext;
|
|
||||||
mac = ( content + len );
|
|
||||||
|
|
||||||
/* Fill in stream-ciphered struct */
|
|
||||||
memcpy ( content, data, len );
|
|
||||||
memcpy ( mac, digest, mac_len );
|
|
||||||
|
|
||||||
return plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate and assemble block-ciphered record from data and MAC portions
|
|
||||||
*
|
|
||||||
* @v tls TLS connection
|
|
||||||
* @ret data Data
|
|
||||||
* @ret len Length of data
|
|
||||||
* @ret digest MAC digest
|
|
||||||
* @ret plaintext_len Length of plaintext record
|
|
||||||
* @ret plaintext Allocated plaintext record
|
|
||||||
*/
|
|
||||||
static void * tls_assemble_block ( struct tls_connection *tls,
|
|
||||||
const void *data, size_t len,
|
|
||||||
void *digest, size_t *plaintext_len ) {
|
|
||||||
size_t blocksize = tls->tx_cipherspec.suite->cipher->blocksize;
|
|
||||||
size_t mac_len = tls->tx_cipherspec.suite->mac_len;
|
|
||||||
size_t iv_len = blocksize;
|
|
||||||
size_t padding_len;
|
|
||||||
void *plaintext;
|
|
||||||
void *iv;
|
|
||||||
void *content;
|
|
||||||
void *mac;
|
|
||||||
void *padding;
|
|
||||||
|
|
||||||
/* Sanity check */
|
|
||||||
assert ( iv_len == tls->tx_cipherspec.suite->record_iv_len );
|
|
||||||
|
|
||||||
/* Calculate block-ciphered struct length */
|
|
||||||
padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) );
|
|
||||||
*plaintext_len = ( iv_len + len + mac_len + padding_len + 1 );
|
|
||||||
|
|
||||||
/* Allocate block-ciphered struct */
|
|
||||||
plaintext = malloc ( *plaintext_len );
|
|
||||||
if ( ! plaintext )
|
|
||||||
return NULL;
|
|
||||||
iv = plaintext;
|
|
||||||
content = ( iv + iv_len );
|
|
||||||
mac = ( content + len );
|
|
||||||
padding = ( mac + mac_len );
|
|
||||||
|
|
||||||
/* Fill in block-ciphered struct */
|
|
||||||
tls_generate_random ( tls, iv, iv_len );
|
|
||||||
memcpy ( content, data, len );
|
|
||||||
memcpy ( mac, digest, mac_len );
|
|
||||||
memset ( padding, padding_len, ( padding_len + 1 ) );
|
|
||||||
|
|
||||||
return plaintext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2678,32 +2614,43 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
|
|||||||
struct tls_cipher_suite *suite = cipherspec->suite;
|
struct tls_cipher_suite *suite = cipherspec->suite;
|
||||||
struct cipher_algorithm *cipher = suite->cipher;
|
struct cipher_algorithm *cipher = suite->cipher;
|
||||||
struct digest_algorithm *digest = suite->digest;
|
struct digest_algorithm *digest = suite->digest;
|
||||||
|
struct {
|
||||||
|
uint8_t fixed[suite->fixed_iv_len];
|
||||||
|
uint8_t record[suite->record_iv_len];
|
||||||
|
} __attribute__ (( packed )) iv;
|
||||||
struct tls_auth_header authhdr;
|
struct tls_auth_header authhdr;
|
||||||
struct tls_header *tlshdr;
|
struct tls_header *tlshdr;
|
||||||
void *plaintext = NULL;
|
void *plaintext = NULL;
|
||||||
size_t plaintext_len;
|
size_t plaintext_len = len;
|
||||||
struct io_buffer *ciphertext = NULL;
|
struct io_buffer *ciphertext = NULL;
|
||||||
size_t ciphertext_len;
|
size_t ciphertext_len;
|
||||||
|
size_t padding_len;
|
||||||
uint8_t mac[digest->digestsize];
|
uint8_t mac[digest->digestsize];
|
||||||
|
void *tmp;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Construct header */
|
/* Construct initialisation vector */
|
||||||
|
memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
|
||||||
|
tls_generate_random ( tls, iv.record, sizeof ( iv.record ) );
|
||||||
|
|
||||||
|
/* Construct authentication data */
|
||||||
authhdr.seq = cpu_to_be64 ( tls->tx_seq );
|
authhdr.seq = cpu_to_be64 ( tls->tx_seq );
|
||||||
authhdr.header.type = type;
|
authhdr.header.type = type;
|
||||||
authhdr.header.version = htons ( tls->version );
|
authhdr.header.version = htons ( tls->version );
|
||||||
authhdr.header.length = htons ( len );
|
authhdr.header.length = htons ( len );
|
||||||
|
|
||||||
/* Calculate MAC */
|
/* Calculate padding length */
|
||||||
tls_hmac ( cipherspec, &authhdr, data, len, mac );
|
plaintext_len += suite->mac_len;
|
||||||
|
if ( is_block_cipher ( cipher ) ) {
|
||||||
/* Allocate and assemble plaintext struct */
|
padding_len = ( ( ( cipher->blocksize - 1 ) &
|
||||||
if ( is_stream_cipher ( cipher ) ) {
|
-( plaintext_len + 1 ) ) + 1 );
|
||||||
plaintext = tls_assemble_stream ( tls, data, len, mac,
|
|
||||||
&plaintext_len );
|
|
||||||
} else {
|
} else {
|
||||||
plaintext = tls_assemble_block ( tls, data, len, mac,
|
padding_len = 0;
|
||||||
&plaintext_len );
|
|
||||||
}
|
}
|
||||||
|
plaintext_len += padding_len;
|
||||||
|
|
||||||
|
/* Allocate plaintext */
|
||||||
|
plaintext = malloc ( plaintext_len );
|
||||||
if ( ! plaintext ) {
|
if ( ! plaintext ) {
|
||||||
DBGC ( tls, "TLS %p could not allocate %zd bytes for "
|
DBGC ( tls, "TLS %p could not allocate %zd bytes for "
|
||||||
"plaintext\n", tls, plaintext_len );
|
"plaintext\n", tls, plaintext_len );
|
||||||
@@ -2711,11 +2658,26 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Assemble plaintext */
|
||||||
|
tmp = plaintext;
|
||||||
|
memcpy ( tmp, data, len );
|
||||||
|
tmp += len;
|
||||||
|
if ( suite->mac_len )
|
||||||
|
tls_hmac ( cipherspec, &authhdr, data, len, mac );
|
||||||
|
memcpy ( tmp, mac, suite->mac_len );
|
||||||
|
tmp += suite->mac_len;
|
||||||
|
memset ( tmp, ( padding_len - 1 ), padding_len );
|
||||||
|
tmp += padding_len;
|
||||||
|
assert ( tmp == ( plaintext + plaintext_len ) );
|
||||||
DBGC2 ( tls, "Sending plaintext data:\n" );
|
DBGC2 ( tls, "Sending plaintext data:\n" );
|
||||||
DBGC2_HD ( tls, plaintext, plaintext_len );
|
DBGC2_HD ( tls, plaintext, plaintext_len );
|
||||||
|
|
||||||
|
/* Set initialisation vector */
|
||||||
|
cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) );
|
||||||
|
|
||||||
/* Allocate ciphertext */
|
/* Allocate ciphertext */
|
||||||
ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len );
|
ciphertext_len = ( sizeof ( *tlshdr ) + sizeof ( iv.record ) +
|
||||||
|
plaintext_len );
|
||||||
ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len );
|
ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len );
|
||||||
if ( ! ciphertext ) {
|
if ( ! ciphertext ) {
|
||||||
DBGC ( tls, "TLS %p could not allocate %zd bytes for "
|
DBGC ( tls, "TLS %p could not allocate %zd bytes for "
|
||||||
@@ -2728,9 +2690,12 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
|
|||||||
tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
|
tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
|
||||||
tlshdr->type = type;
|
tlshdr->type = type;
|
||||||
tlshdr->version = htons ( tls->version );
|
tlshdr->version = htons ( tls->version );
|
||||||
tlshdr->length = htons ( plaintext_len );
|
tlshdr->length = htons ( ciphertext_len - sizeof ( *tlshdr ) );
|
||||||
|
memcpy ( iob_put ( ciphertext, sizeof ( iv.record ) ), iv.record,
|
||||||
|
sizeof ( iv.record ) );
|
||||||
cipher_encrypt ( cipher, cipherspec->cipher_ctx, plaintext,
|
cipher_encrypt ( cipher, cipherspec->cipher_ctx, plaintext,
|
||||||
iob_put ( ciphertext, plaintext_len ), plaintext_len );
|
iob_put ( ciphertext, plaintext_len ), plaintext_len );
|
||||||
|
assert ( iob_len ( ciphertext ) == ciphertext_len );
|
||||||
|
|
||||||
/* Free plaintext as soon as possible to conserve memory */
|
/* Free plaintext as soon as possible to conserve memory */
|
||||||
free ( plaintext );
|
free ( plaintext );
|
||||||
@@ -2754,89 +2719,38 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split stream-ciphered record into data and MAC portions
|
* Verify block padding
|
||||||
*
|
*
|
||||||
* @v tls TLS connection
|
* @v tls TLS connection
|
||||||
* @v rx_data List of received data buffers
|
* @v iobuf Last received I/O buffer
|
||||||
* @v mac MAC to fill in
|
* @ret len Padding length, or negative error
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int tls_split_stream ( struct tls_connection *tls,
|
static int tls_verify_padding ( struct tls_connection *tls,
|
||||||
struct list_head *rx_data, void **mac ) {
|
struct io_buffer *iobuf ) {
|
||||||
size_t mac_len = tls->rx_cipherspec.suite->mac_len;
|
|
||||||
struct io_buffer *iobuf;
|
|
||||||
|
|
||||||
/* Extract MAC */
|
|
||||||
iobuf = list_last_entry ( rx_data, struct io_buffer, list );
|
|
||||||
assert ( iobuf != NULL );
|
|
||||||
if ( iob_len ( iobuf ) < mac_len ) {
|
|
||||||
DBGC ( tls, "TLS %p received underlength MAC\n", tls );
|
|
||||||
DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
|
||||||
return -EINVAL_STREAM;
|
|
||||||
}
|
|
||||||
iob_unput ( iobuf, mac_len );
|
|
||||||
*mac = iobuf->tail;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Split block-ciphered record into data and MAC portions
|
|
||||||
*
|
|
||||||
* @v tls TLS connection
|
|
||||||
* @v rx_data List of received data buffers
|
|
||||||
* @v mac MAC to fill in
|
|
||||||
* @ret rc Return status code
|
|
||||||
*/
|
|
||||||
static int tls_split_block ( struct tls_connection *tls,
|
|
||||||
struct list_head *rx_data, void **mac ) {
|
|
||||||
size_t mac_len = tls->rx_cipherspec.suite->mac_len;
|
|
||||||
size_t iv_len = tls->rx_cipherspec.suite->cipher->blocksize;
|
|
||||||
struct io_buffer *iobuf;
|
|
||||||
uint8_t *padding_final;
|
|
||||||
uint8_t *padding;
|
uint8_t *padding;
|
||||||
size_t padding_len;
|
unsigned int pad;
|
||||||
|
unsigned int i;
|
||||||
/* Sanity check */
|
size_t len;
|
||||||
assert ( iv_len == tls->rx_cipherspec.suite->record_iv_len );
|
|
||||||
|
|
||||||
/* Extract initialisation vector */
|
|
||||||
iobuf = list_first_entry ( rx_data, struct io_buffer, list );
|
|
||||||
if ( iob_len ( iobuf ) < iv_len ) {
|
|
||||||
DBGC ( tls, "TLS %p received underlength IV\n", tls );
|
|
||||||
DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
|
||||||
return -EINVAL_BLOCK;
|
|
||||||
}
|
|
||||||
iob_pull ( iobuf, iv_len );
|
|
||||||
|
|
||||||
/* Extract and verify padding */
|
/* Extract and verify padding */
|
||||||
iobuf = list_last_entry ( rx_data, struct io_buffer, list );
|
padding = ( iobuf->tail - 1 );
|
||||||
padding_final = ( iobuf->tail - 1 );
|
pad = *padding;
|
||||||
padding_len = *padding_final;
|
len = ( pad + 1 );
|
||||||
if ( ( padding_len + 1 ) > iob_len ( iobuf ) ) {
|
if ( len > iob_len ( iobuf ) ) {
|
||||||
DBGC ( tls, "TLS %p received underlength padding\n", tls );
|
DBGC ( tls, "TLS %p received underlength padding\n", tls );
|
||||||
DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
||||||
return -EINVAL_BLOCK;
|
return -EINVAL_PADDING;
|
||||||
}
|
}
|
||||||
iob_unput ( iobuf, ( padding_len + 1 ) );
|
for ( i = 0 ; i < pad ; i++ ) {
|
||||||
for ( padding = iobuf->tail ; padding < padding_final ; padding++ ) {
|
if ( *(--padding) != pad ) {
|
||||||
if ( *padding != padding_len ) {
|
|
||||||
DBGC ( tls, "TLS %p received bad padding\n", tls );
|
DBGC ( tls, "TLS %p received bad padding\n", tls );
|
||||||
DBGC_HD ( tls, padding, padding_len );
|
DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
||||||
return -EINVAL_PADDING;
|
return -EINVAL_PADDING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract MAC */
|
return len;
|
||||||
if ( iob_len ( iobuf ) < mac_len ) {
|
|
||||||
DBGC ( tls, "TLS %p received underlength MAC\n", tls );
|
|
||||||
DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
|
||||||
return -EINVAL_BLOCK;
|
|
||||||
}
|
|
||||||
iob_unput ( iobuf, mac_len );
|
|
||||||
*mac = iobuf->tail;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2854,47 +2768,91 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
|
|||||||
struct tls_cipher_suite *suite = cipherspec->suite;
|
struct tls_cipher_suite *suite = cipherspec->suite;
|
||||||
struct cipher_algorithm *cipher = suite->cipher;
|
struct cipher_algorithm *cipher = suite->cipher;
|
||||||
struct digest_algorithm *digest = suite->digest;
|
struct digest_algorithm *digest = suite->digest;
|
||||||
|
size_t len = ntohs ( tlshdr->length );
|
||||||
|
struct {
|
||||||
|
uint8_t fixed[suite->fixed_iv_len];
|
||||||
|
uint8_t record[suite->record_iv_len];
|
||||||
|
} __attribute__ (( packed )) iv;
|
||||||
struct tls_auth_header authhdr;
|
struct tls_auth_header authhdr;
|
||||||
uint8_t ctx[ hmac_ctxsize ( digest ) ];
|
|
||||||
uint8_t verify_mac[digest->digestsize];
|
uint8_t verify_mac[digest->digestsize];
|
||||||
|
struct io_buffer *first;
|
||||||
|
struct io_buffer *last;
|
||||||
struct io_buffer *iobuf;
|
struct io_buffer *iobuf;
|
||||||
void *mac;
|
void *mac;
|
||||||
size_t len = 0;
|
size_t check_len;
|
||||||
|
int pad_len;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Decrypt the received data */
|
/* Locate first and last data buffers */
|
||||||
list_for_each_entry ( iobuf, &tls->rx_data, list ) {
|
assert ( ! list_empty ( rx_data ) );
|
||||||
cipher_decrypt ( cipher, cipherspec->cipher_ctx,
|
first = list_first_entry ( rx_data, struct io_buffer, list );
|
||||||
iobuf->data, iobuf->data, iob_len ( iobuf ) );
|
last = list_last_entry ( rx_data, struct io_buffer, list );
|
||||||
}
|
|
||||||
|
|
||||||
/* Split record into content and MAC */
|
/* Extract initialisation vector */
|
||||||
if ( is_stream_cipher ( cipher ) ) {
|
if ( iob_len ( first ) < sizeof ( iv.record ) ) {
|
||||||
if ( ( rc = tls_split_stream ( tls, rx_data, &mac ) ) != 0 )
|
DBGC ( tls, "TLS %p received underlength IV\n", tls );
|
||||||
return rc;
|
DBGC_HD ( tls, first->data, iob_len ( first ) );
|
||||||
} else {
|
return -EINVAL_IV;
|
||||||
if ( ( rc = tls_split_block ( tls, rx_data, &mac ) ) != 0 )
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
|
||||||
|
memcpy ( iv.record, first->data, sizeof ( iv.record ) );
|
||||||
|
iob_pull ( first, sizeof ( iv.record ) );
|
||||||
|
len -= sizeof ( iv.record );
|
||||||
|
|
||||||
/* Calculate total length */
|
/* Construct authentication data */
|
||||||
DBGC2 ( tls, "Received plaintext data:\n" );
|
|
||||||
list_for_each_entry ( iobuf, rx_data, list ) {
|
|
||||||
DBGC2_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
|
||||||
len += iob_len ( iobuf );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verify MAC */
|
|
||||||
authhdr.seq = cpu_to_be64 ( tls->rx_seq );
|
authhdr.seq = cpu_to_be64 ( tls->rx_seq );
|
||||||
authhdr.header.type = tlshdr->type;
|
authhdr.header.type = tlshdr->type;
|
||||||
authhdr.header.version = tlshdr->version;
|
authhdr.header.version = tlshdr->version;
|
||||||
authhdr.header.length = htons ( len );
|
authhdr.header.length = htons ( len );
|
||||||
tls_hmac_init ( cipherspec, ctx, &authhdr );
|
|
||||||
list_for_each_entry ( iobuf, rx_data, list ) {
|
/* Set initialisation vector */
|
||||||
tls_hmac_update ( cipherspec, ctx, iobuf->data,
|
cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) );
|
||||||
iob_len ( iobuf ) );
|
|
||||||
|
/* Decrypt the received data */
|
||||||
|
check_len = 0;
|
||||||
|
list_for_each_entry ( iobuf, &tls->rx_data, list ) {
|
||||||
|
cipher_decrypt ( cipher, cipherspec->cipher_ctx,
|
||||||
|
iobuf->data, iobuf->data, iob_len ( iobuf ) );
|
||||||
|
check_len += iob_len ( iobuf );
|
||||||
}
|
}
|
||||||
tls_hmac_final ( cipherspec, ctx, verify_mac );
|
assert ( check_len == len );
|
||||||
|
|
||||||
|
/* Strip block padding, if applicable */
|
||||||
|
if ( is_block_cipher ( cipher ) ) {
|
||||||
|
pad_len = tls_verify_padding ( tls, last );
|
||||||
|
if ( pad_len < 0 ) {
|
||||||
|
rc = pad_len;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
iob_unput ( last, pad_len );
|
||||||
|
len -= pad_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract decrypted MAC */
|
||||||
|
if ( iob_len ( last ) < suite->mac_len ) {
|
||||||
|
DBGC ( tls, "TLS %p received underlength MAC\n", tls );
|
||||||
|
DBGC_HD ( tls, last->data, iob_len ( last ) );
|
||||||
|
return -EINVAL_MAC;
|
||||||
|
}
|
||||||
|
iob_unput ( last, suite->mac_len );
|
||||||
|
len -= suite->mac_len;
|
||||||
|
mac = last->tail;
|
||||||
|
|
||||||
|
/* Dump received data */
|
||||||
|
DBGC2 ( tls, "Received plaintext data:\n" );
|
||||||
|
check_len = 0;
|
||||||
|
list_for_each_entry ( iobuf, rx_data, list ) {
|
||||||
|
DBGC2_HD ( tls, iobuf->data, iob_len ( iobuf ) );
|
||||||
|
check_len += iob_len ( iobuf );
|
||||||
|
}
|
||||||
|
assert ( check_len == len );
|
||||||
|
|
||||||
|
/* Generate MAC */
|
||||||
|
authhdr.header.length = htons ( len );
|
||||||
|
if ( suite->mac_len )
|
||||||
|
tls_hmac_list ( cipherspec, &authhdr, rx_data, verify_mac );
|
||||||
|
|
||||||
|
/* Verify MAC */
|
||||||
if ( memcmp ( mac, verify_mac, suite->mac_len ) != 0 ) {
|
if ( memcmp ( mac, verify_mac, suite->mac_len ) != 0 ) {
|
||||||
DBGC ( tls, "TLS %p failed MAC verification\n", tls );
|
DBGC ( tls, "TLS %p failed MAC verification\n", tls );
|
||||||
return -EINVAL_MAC;
|
return -EINVAL_MAC;
|
||||||
|
|||||||
Reference in New Issue
Block a user