diff --git a/include/mbedtls/ccm.h b/include/mbedtls/ccm.h index 2b9909e1a6..f9f8000fba 100644 --- a/include/mbedtls/ccm.h +++ b/include/mbedtls/ccm.h @@ -87,6 +87,9 @@ typedef struct mbedtls_ccm_context #MBEDTLS_CCM_DECRYPT or #MBEDTLS_CCM_STAR_ENCRYPT or #MBEDTLS_CCM_STAR_DECRYPT. */ + int MBEDTLS_PRIVATE(state); /*!< Working value holding context's + state. Used for chunked data + input */ } mbedtls_ccm_context; diff --git a/library/ccm.c b/library/ccm.c index 686eda5437..a2ca98600c 100644 --- a/library/ccm.c +++ b/library/ccm.c @@ -142,22 +142,105 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx ) (dst)[i] = (src)[i] ^ ctx->b[i]; \ } while( 0 ) -/* - * Authenticated encryption or decryption - */ -static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, - const unsigned char *iv, size_t iv_len, - const unsigned char *add, size_t add_len, - const unsigned char *input, unsigned char *output, - unsigned char *tag, size_t tag_len ) +#define CCM_STATE__CLEAR 0 +#define CCM_STATE__STARTED 0x0001 +#define CCM_STATE__LENGHTS_SET 0x0002 + +static void mbedtls_ccm_clear_state(mbedtls_ccm_context *ctx) { + ctx->state = CCM_STATE__CLEAR; + memset( ctx->b, 0, 16); + memset( ctx->y, 0, 16); + memset( ctx->ctr, 0, 16); +} + +static int mbedtls_ccm_calculate_first_block(mbedtls_ccm_context *ctx) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; unsigned char i; size_t len_left, olen; - const unsigned char *src; - unsigned char *dst; + + /* length calulcation can be done only after both + * mbedtls_ccm_starts() and mbedtls_ccm_set_lengths() have been executed + */ + if( !(ctx->state & CCM_STATE__STARTED) || !(ctx->state & CCM_STATE__LENGHTS_SET) ) + return 0; + + /* + * First block B_0: + * 0 .. 0 flags - set by: mbedtls_ccm_starts() and mbedtls_ccm_set_lenghts() + * 1 .. iv_len nonce (aka iv) - set by: mbedtls_ccm_starts() + * iv_len+1 .. 15 length - set by: mbedtls_ccm_calculate_first_block() + */ + for( i = 0, len_left = ctx->plaintext_len; i < ctx->q; i++, len_left >>= 8 ) + ctx->b[15-i] = (unsigned char)( len_left & 0xFF ); + + if( len_left > 0 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + + /* Start CBC-MAC with first block*/ + UPDATE_CBC_MAC; + + return (0); +} + +int mbedtls_ccm_starts( mbedtls_ccm_context *ctx, + int mode, + const unsigned char *iv, + size_t iv_len ) +{ + int ret; + + /* Also implies q is within bounds */ + if( iv_len < 7 || iv_len > 13 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); ctx->mode = mode; + ctx->q = 16 - 1 - (unsigned char) iv_len; + + /* + * Prepare counter block for encryption: + * 0 .. 0 flags + * 1 .. iv_len nonce (aka iv) + * iv_len+1 .. 15 counter (initially 1) + * + * With flags as (bits): + * 7 .. 3 0 + * 2 .. 0 q - 1 + */ + memset( ctx->ctr, 0, 16); + ctx->ctr[0] = ctx->q - 1; + memcpy( ctx->ctr + 1, iv, iv_len ); + memset( ctx->ctr + 1 + iv_len, 0, ctx->q ); + ctx->ctr[15] = 1; + + /* + * First block B_0: + * 0 .. 0 flags - set by: mbedtls_ccm_starts() and mbedtls_ccm_set_lenghts() + * 1 .. iv_len nonce (aka iv) - set by: mbedtls_ccm_starts() + * iv_len+1 .. 15 length - set by: mbedtls_ccm_calculate_first_block() + * + * With flags as (bits): + * 7 0 + * 6 add present? - set by: mbedtls_ccm_set_lengths() + * 5 .. 3 (t - 2) / 2 - set by: mbedtls_ccm_set_lengths() + * 2 .. 0 q - 1 - set by: mbedtls_ccm_starts() + */ + ctx->b[0] |= ctx->q - 1; + + memcpy( ctx->b + 1, iv, iv_len ); + + ctx->state |= CCM_STATE__STARTED; + ret = mbedtls_ccm_calculate_first_block(ctx); + + return ret; +} + +int mbedtls_ccm_set_lengths( mbedtls_ccm_context *ctx, + size_t total_ad_len, + size_t plaintext_len, + size_t tag_len ) +{ + int ret; /* * Check length requirements: SP800-38C A.1 @@ -169,44 +252,40 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, if( tag_len == 2 || tag_len > 16 || tag_len % 2 != 0 ) return( MBEDTLS_ERR_CCM_BAD_INPUT ); - /* Also implies q is within bounds */ - if( iv_len < 7 || iv_len > 13 ) + if( total_ad_len >= 0xFF00 ) return( MBEDTLS_ERR_CCM_BAD_INPUT ); - if( add_len >= 0xFF00 ) - return( MBEDTLS_ERR_CCM_BAD_INPUT ); - - ctx->q = 16 - 1 - (unsigned char) iv_len; - /* * First block B_0: - * 0 .. 0 flags - * 1 .. iv_len nonce (aka iv) - * iv_len+1 .. 15 length + * 0 .. 0 flags - set by: mbedtls_ccm_starts() and mbedtls_ccm_set_lenghts() + * 1 .. iv_len nonce (aka iv) - set by: mbedtls_ccm_starts() + * iv_len+1 .. 15 length - set by: mbedtls_ccm_calculate_first_block() * * With flags as (bits): * 7 0 - * 6 add present? - * 5 .. 3 (t - 2) / 2 - * 2 .. 0 q - 1 + * 6 add present? - set by: mbedtls_ccm_set_lengths() + * 5 .. 3 (t - 2) / 2 - set by: mbedtls_ccm_set_lengths() + * 2 .. 0 q - 1 - set by: mbedtls_ccm_starts() */ - ctx->b[0] = 0; - ctx->b[0] |= ( add_len > 0 ) << 6; + ctx->b[0] |= ( total_ad_len > 0 ) << 6; ctx->b[0] |= ( ( tag_len - 2 ) / 2 ) << 3; - ctx->b[0] |= ctx->q - 1; - memcpy( ctx->b + 1, iv, iv_len ); + ctx->plaintext_len = plaintext_len; - for( i = 0, len_left = length; i < ctx->q; i++, len_left >>= 8 ) - ctx->b[15-i] = (unsigned char)( len_left & 0xFF ); + ctx->state |= CCM_STATE__LENGHTS_SET; + ret = mbedtls_ccm_calculate_first_block(ctx); - if( len_left > 0 ) - return( MBEDTLS_ERR_CCM_BAD_INPUT ); + return ret; +} - - /* Start CBC-MAC with first block */ - memset( ctx->y, 0, 16 ); - UPDATE_CBC_MAC; +int mbedtls_ccm_update_ad( mbedtls_ccm_context *ctx, + const unsigned char *add, + size_t add_len ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char i; + size_t len_left, olen; + const unsigned char *src; /* * If there is additional data, update CBC-MAC with @@ -242,20 +321,24 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, } } - /* - * Prepare counter block for encryption: - * 0 .. 0 flags - * 1 .. iv_len nonce (aka iv) - * iv_len+1 .. 15 counter (initially 1) - * - * With flags as (bits): - * 7 .. 3 0 - * 2 .. 0 q - 1 - */ - ctx->ctr[0] = ctx->q - 1; - memcpy( ctx->ctr + 1, iv, iv_len ); - memset( ctx->ctr + 1 + iv_len, 0, ctx->q ); - ctx->ctr[15] = 1; + return (0); +} + +int mbedtls_ccm_update( mbedtls_ccm_context *ctx, + const unsigned char *input, size_t input_len, + unsigned char *output, size_t output_size, + size_t *output_len ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char i; + size_t len_left, olen; + const unsigned char *src; + unsigned char *dst; + + if( output_size < input_len ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + CCM_VALIDATE_RET( output_length != NULL ); + *output_len = input_len; /* * Authenticate and {en,de}crypt the message. @@ -263,7 +346,7 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, * The only difference between encryption and decryption is * the respective order of authentication and {en,de}cryption. */ - len_left = length; + len_left = input_len; src = input; dst = output; @@ -300,6 +383,16 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, break; } + return (0); +} + +int mbedtls_ccm_finish( mbedtls_ccm_context *ctx, + unsigned char *tag, size_t tag_len ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char i; + size_t olen; + /* * Authentication: reset counter and crypt/mask internal tag */ @@ -308,6 +401,38 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, CTR_CRYPT( ctx->y, ctx->y, 16 ); memcpy( tag, ctx->y, tag_len ); + mbedtls_ccm_clear_state(ctx); + + return( 0 ); +} + +/* + * Authenticated encryption or decryption + */ +static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen; + + if( ( ret = mbedtls_ccm_starts( ctx, mode, iv, iv_len ) ) != 0 ) + return( ret ); + + if( ( ret = mbedtls_ccm_set_lengths( ctx, add_len, length, tag_len ) ) != 0 ) + return( ret ); + + if( ( ret = mbedtls_ccm_update_ad( ctx, add, add_len ) ) != 0 ) + return( ret ); + + if( ( ret = mbedtls_ccm_update( ctx, input, length, + output, length, &olen ) ) != 0 ) + return( ret ); + + if( ( ret = mbedtls_ccm_finish( ctx, tag, tag_len ) ) != 0 ) + return( ret ); return( 0 ); }