diff options
Diffstat (limited to 'aes/src/box.c')
-rw-r--r-- | aes/src/box.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/aes/src/box.c b/aes/src/box.c new file mode 100644 index 0000000..c7e1e90 --- /dev/null +++ b/aes/src/box.c @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com> + * This file is part of the "AES tools" project. + * For details, see https://github.com/egor-tensin/aes-tools. + * Distributed under the MIT License. + */ + +#include <aes/all.h> + +#include <stdlib.h> +#include <string.h> + +static const AES_BoxAlgorithmInterface* aes_box_algorithms[] = +{ + &aes_box_algorithm_aes128, + &aes_box_algorithm_aes192, + &aes_box_algorithm_aes256, +}; + +AES_StatusCode aes_box_init( + AES_Box* box, + AES_Algorithm algorithm, + const AES_BoxKey* box_key, + AES_Mode mode, + const AES_BoxBlock* iv, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + box->algorithm = aes_box_algorithms[algorithm]; + + if (aes_is_error(status = box->algorithm->calc_round_keys( + box_key, + &box->encryption_keys, + &box->decryption_keys, + err_details))) + return status; + + box->mode = mode; + if (iv != NULL) + box->iv = *iv; + + return status; +} + +static AES_StatusCode aes_box_encrypt_block_ecb( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + return box->algorithm->encrypt_block( + input, &box->encryption_keys, output, err_details); +} + +static AES_StatusCode aes_box_encrypt_block_cbc( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + AES_BoxBlock xored_input = *input; + + if (aes_is_error(status = box->algorithm->xor_block( + &xored_input, &box->iv, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->encrypt_block( + &xored_input, &box->encryption_keys, output, err_details))) + return status; + + box->iv = *output; + return status; +} + +static AES_StatusCode aes_box_encrypt_block_cfb( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (aes_is_error(status = box->algorithm->encrypt_block( + &box->iv, &box->encryption_keys, output, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->xor_block( + output, input, err_details))) + return status; + + box->iv = *output; + return status; +} + +static AES_StatusCode aes_box_encrypt_block_ofb( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (aes_is_error(status = box->algorithm->encrypt_block( + &box->iv, &box->encryption_keys, &box->iv, err_details))) + return status; + + *output = box->iv; + + if (aes_is_error(status = box->algorithm->xor_block( + output, input, err_details))) + return status; + + return status; +} + +static AES_StatusCode aes_box_encrypt_block_ctr( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (aes_is_error(status = box->algorithm->encrypt_block( + &box->iv, &box->encryption_keys, output, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->xor_block( + output, input, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->inc_block( + &box->iv, err_details))) + return status; + + return status; +} + +typedef AES_StatusCode (*AES_BoxEncryptBlockInMode)( + AES_Box*, + const AES_BoxBlock*, + AES_BoxBlock*, + AES_ErrorDetails*); + +static AES_BoxEncryptBlockInMode aes_box_encrypt_block_in_mode[] = +{ + &aes_box_encrypt_block_ecb, + &aes_box_encrypt_block_cbc, + &aes_box_encrypt_block_cfb, + &aes_box_encrypt_block_ofb, + &aes_box_encrypt_block_ctr, +}; + +AES_StatusCode aes_box_encrypt_block( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + return aes_box_encrypt_block_in_mode[box->mode]( + box, input, output, err_details); +} + +static AES_StatusCode aes_box_decrypt_block_ecb( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + return box->algorithm->decrypt_block( + input, &box->decryption_keys, output, err_details); +} + +static AES_StatusCode aes_box_decrypt_block_cbc( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (aes_is_error(status = box->algorithm->decrypt_block( + input, &box->decryption_keys, output, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->xor_block( + output, &box->iv, err_details))) + return status; + + box->iv = *input; + return status; +} + +static AES_StatusCode aes_box_decrypt_block_cfb( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (aes_is_error(status = box->algorithm->encrypt_block( + &box->iv, &box->encryption_keys, output, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->xor_block( + output, input, err_details))) + return status; + + box->iv = *input; + return status; +} + +typedef AES_BoxEncryptBlockInMode AES_BoxDecryptBlockInMode; + +static AES_BoxDecryptBlockInMode aes_box_decrypt_block_in_mode[] = +{ + &aes_box_decrypt_block_ecb, + &aes_box_decrypt_block_cbc, + &aes_box_decrypt_block_cfb, + &aes_box_encrypt_block_ofb, + &aes_box_encrypt_block_ctr, +}; + +AES_StatusCode aes_box_decrypt_block( + AES_Box* box, + const AES_BoxBlock* input, + AES_BoxBlock* output, + AES_ErrorDetails* err_details) +{ + return aes_box_decrypt_block_in_mode[box->mode]( + box, input, output, err_details); +} + +static AES_StatusCode aes_box_get_encrypted_buffer_size( + AES_Box* box, + size_t src_size, + size_t* dest_size, + size_t* padding_size, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + switch (box->mode) + { + case AES_ECB: + case AES_CBC: + { + size_t block_size; + + if (aes_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 AES_CFB: + case AES_OFB: + case AES_CTR: + *dest_size = src_size; + *padding_size = 0; + return status; + + default: + return aes_error_not_implemented( + err_details, "unsupported mode of operation"); + } +} + +static AES_StatusCode aes_box_encrypt_buffer_block( + AES_Box* box, + const void* src, + void* dest, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + AES_BoxBlock plaintext; + + if (aes_is_error(status = box->algorithm->load_block( + &plaintext, src, err_details))) + return status; + + AES_BoxBlock ciphertext; + + if (aes_is_error(status = aes_box_encrypt_block( + box, &plaintext, &ciphertext, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->store_block( + dest, &ciphertext, err_details))) + return status; + + return status; +} + +static AES_StatusCode aes_box_encrypt_buffer_partial_block_with_padding( + AES_Box* box, + const void* src, + size_t src_size, + void* dest, + size_t padding_size, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + size_t block_size; + + if (aes_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + void* plaintext_buf = malloc(block_size); + + if (plaintext_buf == NULL) + return status = aes_error_memory_allocation(err_details); + + memcpy(plaintext_buf, src, src_size); + + if (aes_is_error(status = aes_fill_with_padding( + AES_PADDING_PKCS7, + (char*) plaintext_buf + src_size, + padding_size, + err_details))) + goto FREE_PLAINTEXT_BUF; + + if (aes_is_error(status = aes_box_encrypt_buffer_block( + box, plaintext_buf, dest, err_details))) + goto FREE_PLAINTEXT_BUF; + +FREE_PLAINTEXT_BUF: + free(plaintext_buf); + + return status; +} + +static AES_StatusCode aes_box_encrypt_buffer_partial_block( + AES_Box* box, + const void* src, + size_t src_size, + void* dest, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (src_size == 0) + return status; + + size_t block_size; + + if (aes_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + void* plaintext_buf = malloc(block_size); + + if (plaintext_buf == NULL) + return status = aes_error_memory_allocation(err_details); + + memset(plaintext_buf, 0x00, block_size); + memcpy(plaintext_buf, src, src_size); + + void* ciphertext_buf = malloc(block_size); + + if (ciphertext_buf == NULL) + { + status = aes_error_memory_allocation(err_details); + goto FREE_PLAINTEXT_BUF; + } + + if (aes_is_error(status = aes_box_encrypt_buffer_block( + box, plaintext_buf, ciphertext_buf, err_details))) + goto FREE_CIPHERTEXT_BUF; + + memcpy(dest, ciphertext_buf, src_size); + +FREE_CIPHERTEXT_BUF: + free(ciphertext_buf); + +FREE_PLAINTEXT_BUF: + free(plaintext_buf); + + return status; +} + +AES_StatusCode aes_box_encrypt_buffer( + AES_Box* box, + const void* src, + size_t src_size, + void* dest, + size_t* dest_size, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (box == NULL) + return aes_error_null_argument(err_details, "box"); + if (dest_size == NULL) + return aes_error_null_argument(err_details, "dest_size"); + + size_t padding_size = 0; + + if (aes_is_error(status = aes_box_get_encrypted_buffer_size( + box, src_size, dest_size, &padding_size, err_details))) + return status; + + if (dest == NULL) + return AES_SUCCESS; + if (src == NULL && src_size != 0) + return aes_error_null_argument(err_details, "src"); + + size_t block_size; + + if (aes_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) + { + if (aes_is_error(status = aes_box_encrypt_buffer_block( + box, src, dest, err_details))) + return status; + + src = (char*) src + block_size; + dest = (char*) dest + block_size; + } + + if (padding_size == 0) + return aes_box_encrypt_buffer_partial_block( + box, src, src_size % block_size, dest, err_details); + else + return aes_box_encrypt_buffer_partial_block_with_padding( + box, src, src_size % block_size, dest, padding_size, err_details); +} + +static AES_StatusCode aes_box_get_decrypted_buffer_size( + AES_Box* box, + size_t src_size, + size_t* dest_size, + size_t* max_padding_size, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + switch (box->mode) + { + case AES_ECB: + case AES_CBC: + { + size_t block_size; + + if (aes_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + if (src_size == 0 || src_size % block_size != 0) + return aes_error_missing_padding(err_details); + + *dest_size = src_size; + *max_padding_size = block_size; + return status; + } + + case AES_CFB: + case AES_OFB: + case AES_CTR: + *dest_size = src_size; + *max_padding_size = 0; + return status; + + default: + return aes_error_not_implemented( + err_details, "unsupported mode of operation"); + } +} + +static AES_StatusCode aes_box_decrypt_buffer_block( + AES_Box* box, + const void* src, + void* dest, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + AES_BoxBlock ciphertext; + + if (aes_is_error(status = box->algorithm->load_block( + &ciphertext, src, err_details))) + return status; + + AES_BoxBlock plaintext; + + if (aes_is_error(status = aes_box_decrypt_block( + box, &ciphertext, &plaintext, err_details))) + return status; + + if (aes_is_error(status = box->algorithm->store_block( + dest, &plaintext, err_details))) + return status; + + return status; +} + +static AES_StatusCode aes_box_decrypt_buffer_partial_block( + AES_Box* box, + const void* src, + size_t src_size, + void* dest, + AES_ErrorDetails* err_details) +{ + AES_StatusCode status = AES_SUCCESS; + + if (src_size == 0) + return status; + + size_t block_size; + + if (aes_is_error(status = box->algorithm->get_block_size( + &block_size, err_details))) + return status; + + void* ciphertext_buf = malloc(block_size); + + if (ciphertext_buf == NULL) + return status = aes_error_memory_allocation(err_details); + + memset(ciphertext_buf, 0x00, block_size); + memcpy(ciphertext_buf, src, src_size); + + void* plaintext_buf = malloc(block_size); + + if (plaintext_buf == NULL) + { + status = aes_error_memory_allocation(err_details); + goto FREE_CIPHERTEXT_BUF; + } + + if (aes_is_error(status = aes_box_decrypt_buffer_block( + box, ciphertext_buf, plaintext_buf, err_details))) + goto FREE_PLAINTEXT_BUF; + + memcpy(dest, plaintext_buf, src_size); + +FREE_PLAINTEXT_BUF: + free(plaintext_buf); + +FREE_CIPHERTEXT_BUF: + free(ciphertext_buf); + + return status; +} + +AES_StatusCode aes_box_decrypt_buffer( + AES_Box* box, + const void* src, + size_t src_size, + void* dest, + size_t* dest_size, + AES_ErrorDetails* err_details) +{ + if (box == NULL) + return aes_error_null_argument(err_details, "box"); + if (dest_size == NULL) + return aes_error_null_argument(err_details, "dest_size"); + + AES_StatusCode status = AES_SUCCESS; + size_t max_padding_size = 0; + + if (aes_is_error(status = aes_box_get_decrypted_buffer_size( + box, src_size, dest_size, &max_padding_size, err_details))) + return status; + + if (dest == NULL) + return AES_SUCCESS; + if (src == NULL) + return aes_error_null_argument(err_details, "src"); + + size_t block_size; + + if (aes_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) + { + if (aes_is_error(status = aes_box_decrypt_buffer_block( + box, src, dest, err_details))) + return status; + + src = (char*) src + block_size; + dest = (char*) dest + block_size; + } + + if (max_padding_size == 0) + { + return aes_box_decrypt_buffer_partial_block( + box, src, src_size % block_size, dest, err_details); + } + else + { + size_t padding_size; + + if (aes_is_error(status = aes_extract_padding_size( + AES_PADDING_PKCS7, + (char*) dest - block_size, + block_size, + &padding_size, + err_details))) + return status; + + *dest_size -= padding_size; + return status; + } +} + +AES_StatusCode aes_box_parse_block( + AES_BoxBlock* dest, + AES_Algorithm algorithm, + const char* src, + AES_ErrorDetails* err_details) +{ + if (dest == NULL) + return aes_error_null_argument(err_details, "dest"); + if (src == NULL) + return aes_error_null_argument(err_details, "src"); + + return aes_box_algorithms[algorithm]->parse_block( + dest, src, err_details); +} + +AES_StatusCode aes_box_parse_key( + AES_BoxKey* dest, + AES_Algorithm algorithm, + const char* src, + AES_ErrorDetails* err_details) +{ + if (dest == NULL) + return aes_error_null_argument(err_details, "dest"); + if (src == NULL) + return aes_error_null_argument(err_details, "src"); + + return aes_box_algorithms[algorithm]->parse_key( + dest, src, err_details); +} + +AES_StatusCode aes_box_format_block( + AES_BoxBlockString* dest, + AES_Algorithm algorithm, + const AES_BoxBlock* src, + AES_ErrorDetails* err_details) +{ + if (dest == NULL) + return aes_error_null_argument(err_details, "dest"); + if (src == NULL) + return aes_error_null_argument(err_details, "src"); + + return aes_box_algorithms[algorithm]->format_block( + dest, src, err_details); +} + +AES_StatusCode aes_box_format_key( + AES_BoxKeyString* dest, + AES_Algorithm algorithm, + const AES_BoxKey* src, + AES_ErrorDetails* err_details) +{ + if (dest == NULL) + return aes_error_null_argument(err_details, "dest"); + if (src == NULL) + return aes_error_null_argument(err_details, "src"); + + return aes_box_algorithms[algorithm]->format_key( + dest, src, err_details); +} |