From 3716501690fb4e1ddd1af385b4d012cce2d107ac Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Fri, 19 Jun 2015 09:42:55 +0300 Subject: add buffer encryption to "boxes" --- src/box.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/box_aes.c | 72 ++++++++++++++ src/error.c | 8 ++ 3 files changed, 374 insertions(+) (limited to 'src') diff --git a/src/box.c b/src/box.c index 6d0f5d2..75d471d 100644 --- a/src/box.c +++ b/src/box.c @@ -8,6 +8,8 @@ #include +#include + static const AesNI_BoxAlgorithmInterface* aesni_box_algorithms[] = { &aesni_box_algorithm_aes128, @@ -253,3 +255,295 @@ AesNI_StatusCode aesni_box_decrypt_block( { return aesni_box_decrypt_block_in_mode[box->mode](box, input, output, err_details); } + +static AesNI_StatusCode aesni_box_get_encrypted_buffer_size( + AesNI_Box* box, + size_t src_size, + size_t* dest_size, + size_t* padding_size, + AesNI_ErrorDetails* err_details) +{ + AesNI_StatusCode status = AESNI_SUCCESS; + + switch (box->mode) + { + case AESNI_ECB: + case AESNI_CBC: + { + size_t block_size; + + if (aesni_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + *padding_size = block_size - src_size % block_size; + *dest_size = src_size + *padding_size; + return status; + } + + case AESNI_CFB: + case AESNI_OFB: + case AESNI_CTR: + *dest_size = src_size; + *padding_size = 0; + return status; + + default: + return aesni_error_not_implemented(err_details, "unsupported mode of operation"); + } +} + +static AesNI_StatusCode aesni_box_encrypt_buffer_block( + AesNI_Box* box, + const void* src, + void* dest, + AesNI_ErrorDetails* err_details) +{ + AesNI_StatusCode status = AESNI_SUCCESS; + + AesNI_BoxBlock plaintext; + + if (aesni_is_error(status = box->algorithm->load_block( + &plaintext, src, err_details))) + return status; + + AesNI_BoxBlock ciphertext; + + if (aesni_is_error(status = aesni_box_encrypt_block( + box, &plaintext, &ciphertext, err_details))) + return status; + + if (aesni_is_error(status = box->algorithm->store_block( + dest, &ciphertext, err_details))) + return status; + + return status; +} + +AesNI_StatusCode aesni_box_encrypt_buffer( + AesNI_Box* box, + const void* src, + size_t src_size, + void* dest, + size_t* dest_size, + AesNI_ErrorDetails* err_details) +{ + if (box == NULL) + return aesni_error_null_argument(err_details, "box"); + if (dest_size == NULL) + return aesni_error_null_argument(err_details, "dest_size"); + + AesNI_StatusCode status = AESNI_SUCCESS; + size_t padding_size = 0; + + if (aesni_is_error(status = aesni_box_get_encrypted_buffer_size( + box, src_size, dest_size, &padding_size, err_details))) + return status; + + if (dest == NULL) + return AESNI_SUCCESS; + if (src == NULL) + return aesni_error_null_argument(err_details, "src"); + + size_t block_size; + + if (aesni_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + const size_t src_len = src_size / block_size; + + for (size_t i = 0; i < src_len; ++i, (char*) src += block_size, (char*) dest += block_size) + if (aesni_is_error(status = aesni_box_encrypt_buffer_block( + box, src, dest, err_details))) + return status; + + if (padding_size == 0) + { + const size_t partial_block_size = src_size % block_size; + + if (partial_block_size != 0) + { + AesNI_BoxBlock plaintext; + + if (aesni_is_error(status = box->algorithm->load_partial_block( + &plaintext, src, partial_block_size, err_details))) + return status; + + AesNI_BoxBlock ciphertext; + + if (aesni_is_error(status = aesni_box_encrypt_block( + box, &plaintext, &ciphertext, err_details))) + return status; + + if (aesni_is_error(status = box->algorithm->store_partial_block( + dest, &ciphertext, partial_block_size, err_details))) + return status; + } + } + else + { + AesNI_BoxBlock plaintext; + + if (aesni_is_error(status = box->algorithm->load_block_with_padding( + &plaintext, src, src_size % block_size, err_details))) + return status; + + AesNI_BoxBlock ciphertext; + + if (aesni_is_error(status = aesni_box_encrypt_block( + box, &plaintext, &ciphertext, err_details))) + return status; + + if (aesni_is_error(status = box->algorithm->store_block( + dest, &ciphertext, err_details))) + return status; + } + + return status; +} + +static AesNI_StatusCode aesni_box_get_decrypted_buffer_size( + AesNI_Box* box, + size_t src_size, + size_t* dest_size, + size_t* max_padding_size, + AesNI_ErrorDetails* err_details) +{ + AesNI_StatusCode status = AESNI_SUCCESS; + + switch (box->mode) + { + case AESNI_ECB: + case AESNI_CBC: + { + size_t block_size; + + if (aesni_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + if (src_size % block_size != 0) + return aesni_error_invalid_plaintext_length(err_details); + + *dest_size = src_size; + *max_padding_size = block_size; + return status; + } + + case AESNI_CFB: + case AESNI_OFB: + case AESNI_CTR: + *dest_size = src_size; + *max_padding_size = 0; + return status; + + default: + return aesni_error_not_implemented(err_details, "unsupported mode of operation"); + } +} + +static AesNI_StatusCode aesni_box_decrypt_buffer_block( + AesNI_Box* box, + const void* src, + void* dest, + AesNI_ErrorDetails* err_details) +{ + AesNI_StatusCode status = AESNI_SUCCESS; + + AesNI_BoxBlock ciphertext; + + if (aesni_is_error(status = box->algorithm->load_block( + &ciphertext, src, err_details))) + return status; + + AesNI_BoxBlock plaintext; + + if (aesni_is_error(status = aesni_box_decrypt_block( + box, &ciphertext, &plaintext, err_details))) + return status; + + if (aesni_is_error(status = box->algorithm->store_block( + dest, &plaintext, err_details))) + return status; + + return status; +} + +AesNI_StatusCode aesni_box_decrypt_buffer( + AesNI_Box* box, + const void* src, + size_t src_size, + void* dest, + size_t* dest_size, + AesNI_ErrorDetails* err_details) +{ + if (box == NULL) + return aesni_error_null_argument(err_details, "box"); + if (dest_size == NULL) + return aesni_error_null_argument(err_details, "dest_size"); + + AesNI_StatusCode status = AESNI_SUCCESS; + size_t padding_size = 0; + + if (aesni_is_error(status = aesni_box_get_decrypted_buffer_size( + box, src_size, dest_size, &padding_size, err_details))) + return status; + + if (dest == NULL) + return AESNI_SUCCESS; + if (src == NULL) + return aesni_error_null_argument(err_details, "src"); + + size_t block_size; + + if (aesni_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + const size_t src_len = src_size / block_size; + + for (size_t i = 0; i < src_len; ++i, (char*) src += block_size, (char*) dest += block_size) + if (aesni_is_error(status = aesni_box_decrypt_buffer_block( + box, src, dest, err_details))) + return status; + + if (padding_size == 0) + { + const size_t partial_block_size = src_size % block_size; + + if (partial_block_size != 0) + { + AesNI_BoxBlock ciphertext; + + if (aesni_is_error(status = box->algorithm->load_partial_block( + &ciphertext, src, partial_block_size, err_details))) + return status; + + AesNI_BoxBlock plaintext; + + if (aesni_is_error(status = aesni_box_decrypt_block( + box, &ciphertext, &plaintext, err_details))) + return status; + + if (aesni_is_error(status = box->algorithm->store_partial_block( + dest, &plaintext, partial_block_size, err_details))) + return status; + } + } + else + { + padding_size = ((unsigned char*) dest)[-1]; + + if (padding_size > block_size) + return aesni_error_invalid_pkcs7_padding(err_details); + + for (size_t i = 1; i < padding_size; ++i) + if (((unsigned char*) dest)[0 - i] != padding_size) + return aesni_error_invalid_pkcs7_padding(err_details); + + *dest_size -= padding_size; + } + + return status; +} diff --git a/src/box_aes.c b/src/box_aes.c index 12f369d..49c5326 100644 --- a/src/box_aes.c +++ b/src/box_aes.c @@ -9,6 +9,7 @@ #include #include +#include static AesNI_StatusCode aesni_box_derive_params_aes128( const AesNI_BoxAlgorithmParams* algorithm_params, @@ -82,6 +83,62 @@ static AesNI_StatusCode aesni_box_get_block_size_aes( return AESNI_SUCCESS; } +static AesNI_StatusCode aesni_box_store_block_aes( + void* dest, + const AesNI_BoxBlock* src, + AesNI_ErrorDetails* err_details) +{ + aesni_store_block128(dest, src->aes_block); + return AESNI_SUCCESS; +} + +static AesNI_StatusCode aesni_box_store_partial_block_aes( + void* dest, + const AesNI_BoxBlock* src, + size_t src_size, + AesNI_ErrorDetails* err_details) +{ + __declspec(align(16)) unsigned char buf[16]; + aesni_store_block128(buf, src->aes_block); + memcpy(dest, buf, src_size); + return AESNI_SUCCESS; +} + +static AesNI_StatusCode aesni_box_load_block_aes( + AesNI_BoxBlock* dest, + const void* src, + AesNI_ErrorDetails* err_details) +{ + dest->aes_block = aesni_load_block128(src); + return AESNI_SUCCESS; +} + +static AesNI_StatusCode aesni_box_load_partial_block_aes( + AesNI_BoxBlock* dest, + const void* src, + size_t src_size, + AesNI_ErrorDetails* err_details) +{ + __declspec(align(16)) unsigned char buf[16]; + memset(buf, 0x00, 16); + memcpy(buf, src, src_size); + dest->aes_block = aesni_load_block128_aligned(buf); + return AESNI_SUCCESS; +} + +static AesNI_StatusCode aesni_box_load_block_with_padding_aes( + AesNI_BoxBlock* dest, + const void* src, + size_t src_size, + AesNI_ErrorDetails* err_details) +{ + __declspec(align(16)) unsigned char padding[16]; + memset(padding + src_size, 16 - src_size, 16 - src_size); + memcpy(padding, src, src_size); + dest->aes_block = aesni_load_block128_aligned(padding); + return AESNI_SUCCESS; +} + static AesNI_StatusCode aesni_box_encrypt_block_aes128( const AesNI_BoxBlock* input, const AesNI_BoxEncryptionParams* params, @@ -162,6 +219,11 @@ AesNI_BoxAlgorithmInterface aesni_box_algorithm_aes128 = &aesni_box_xor_block_aes, &aesni_box_next_counter_aes, &aesni_box_get_block_size_aes, + &aesni_box_store_block_aes, + &aesni_box_store_partial_block_aes, + &aesni_box_load_block_aes, + &aesni_box_load_partial_block_aes, + &aesni_box_load_block_with_padding_aes, }; AesNI_BoxAlgorithmInterface aesni_box_algorithm_aes192 = @@ -172,6 +234,11 @@ AesNI_BoxAlgorithmInterface aesni_box_algorithm_aes192 = &aesni_box_xor_block_aes, &aesni_box_next_counter_aes, &aesni_box_get_block_size_aes, + &aesni_box_store_block_aes, + &aesni_box_store_partial_block_aes, + &aesni_box_load_block_aes, + &aesni_box_load_partial_block_aes, + &aesni_box_load_block_with_padding_aes, }; AesNI_BoxAlgorithmInterface aesni_box_algorithm_aes256 = @@ -182,4 +249,9 @@ AesNI_BoxAlgorithmInterface aesni_box_algorithm_aes256 = &aesni_box_xor_block_aes, &aesni_box_next_counter_aes, &aesni_box_get_block_size_aes, + &aesni_box_store_block_aes, + &aesni_box_store_partial_block_aes, + &aesni_box_load_block_aes, + &aesni_box_load_partial_block_aes, + &aesni_box_load_block_with_padding_aes, }; diff --git a/src/error.c b/src/error.c index bf35668..fd9136a 100644 --- a/src/error.c +++ b/src/error.c @@ -26,6 +26,7 @@ static const char* aesni_strerror_messages[] = "Couldn't parse", "Invalid PKCS7 padding (wrong key?)", "Not implemented", + "Invalid plaintext length", }; const char* aesni_strerror(AesNI_StatusCode ec) @@ -103,6 +104,7 @@ static AesNI_ErrorFormatter err_formatters[] = &aesni_format_parse_error, &aesni_format_error_strerror, &aesni_format_not_implemented_error, + &aesni_format_error_strerror, }; size_t aesni_format_error( @@ -201,3 +203,9 @@ AesNI_StatusCode aesni_error_not_implemented( return status; } + +AesNI_StatusCode aesni_error_invalid_plaintext_length( + AesNI_ErrorDetails* err_details) +{ + return aesni_make_error(err_details, AESNI_INVALID_PLAINTEXT_LENGTH_ERROR); +} -- cgit v1.2.3