From 4db7c072c298f0b816a8fe079279fb9a104d653d Mon Sep 17 00:00:00 2001 From: Mateusz Kulikowski Date: Mon, 11 Apr 2016 15:35:20 +0200 Subject: [PATCH] Implement limited symmetric cipher support Change-Id: I5b130e3e1d41cfcce1f730f3d1b316c088432677 Signed-off-by: Mateusz Kulikowski --- src/encrypt.c | 260 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 247 insertions(+), 13 deletions(-) diff --git a/src/encrypt.c b/src/encrypt.c index bfd730d..d195800 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -17,17 +17,75 @@ */ #include +#include #include #include #include #include +#include #include +#include #include "internal.h" -static const char *symmetric_algo_to_str(yaca_enc_algo_e algo) +enum encrypt_op_type { + OP_ENCRYPT = 0, + OP_DECRYPT = 1 +}; + +struct yaca_encrypt_ctx_s +{ + struct yaca_ctx_s ctx; + + EVP_CIPHER_CTX *cipher_ctx; + enum encrypt_op_type op_type; /* Operation context was created for */ +}; + +static struct yaca_encrypt_ctx_s *get_encrypt_ctx(const yaca_ctx_h ctx) +{ + if (ctx == YACA_CTX_NULL) + return NULL; + + switch (ctx->type) + { + case YACA_CTX_ENCRYPT: + return (struct yaca_encrypt_ctx_s *)ctx; + default: + return NULL; + } +} + +static void destroy_encrypt_ctx(const yaca_ctx_h ctx) +{ + struct yaca_encrypt_ctx_s *nc = get_encrypt_ctx(ctx); + + if (nc == NULL) + return; + + EVP_CIPHER_CTX_free(nc->cipher_ctx); + nc->cipher_ctx = NULL; +} + +static int get_encrypt_output_length(const yaca_ctx_h ctx, size_t input_len) +{ + struct yaca_encrypt_ctx_s *nc = get_encrypt_ctx(ctx); + int block_size; + + if (nc == NULL || nc->cipher_ctx == NULL) + return YACA_ERROR_INVALID_ARGUMENT; + + block_size = EVP_CIPHER_CTX_block_size(nc->cipher_ctx); + if (block_size == 0) + return YACA_ERROR_OPENSSL_FAILURE; // TODO: extract openssl error here + + if (input_len > 0) + return block_size + input_len - 1; + return block_size; +} + +static const char *encrypt_algo_to_str(yaca_enc_algo_e algo) { switch(algo) { @@ -74,13 +132,13 @@ static const char *bcm_to_str(yaca_block_cipher_mode_e bcm) } } -int get_symmetric_algorithm(yaca_enc_algo_e algo, - yaca_block_cipher_mode_e bcm, - unsigned key_bits, - const EVP_CIPHER **cipher) +int get_encrypt_algorithm(yaca_enc_algo_e algo, + yaca_block_cipher_mode_e bcm, + unsigned key_bits, + const EVP_CIPHER **cipher) { char cipher_name[32]; - const char *algo_name = symmetric_algo_to_str(algo); + const char *algo_name = encrypt_algo_to_str(algo); const char *bcm_name = bcm_to_str(bcm); const EVP_CIPHER *lcipher; int ret; @@ -104,6 +162,178 @@ int get_symmetric_algorithm(yaca_enc_algo_e algo, return 0; } +static int encrypt_init(yaca_ctx_h *ctx, + yaca_enc_algo_e algo, + yaca_block_cipher_mode_e bcm, + const yaca_key_h sym_key, + const yaca_key_h iv, + enum encrypt_op_type op_type) +{ + const struct yaca_key_simple_s *lkey; + const struct yaca_key_simple_s *liv; + struct yaca_encrypt_ctx_s *nc; + const EVP_CIPHER *cipher; + int key_bits; + int iv_bits; + int ret; + + if (ctx == NULL || sym_key == YACA_KEY_NULL) + return YACA_ERROR_INVALID_ARGUMENT; + + lkey = key_get_simple(sym_key); + if (lkey == NULL) + return YACA_ERROR_INVALID_ARGUMENT; + + nc = yaca_malloc(sizeof(struct yaca_encrypt_ctx_s)); + if (nc == NULL) + return YACA_ERROR_OUT_OF_MEMORY; + + memset(nc, 0, sizeof(struct yaca_encrypt_ctx_s)); + + nc->ctx.type = YACA_CTX_ENCRYPT; + nc->ctx.ctx_destroy = destroy_encrypt_ctx; + nc->ctx.get_output_length = get_encrypt_output_length; + nc->op_type = op_type; + + // TODO: handling of algorithms with variable key length + ret = yaca_key_get_length(sym_key); + if (ret < 0) + goto err_free; + key_bits = ret; + + ret = get_encrypt_algorithm(algo, bcm, key_bits, &cipher); + if (ret != 0) + goto err_free; + + ret = EVP_CIPHER_iv_length(cipher); + if (ret < 0) + goto err_free; + + iv_bits = ret * 8; + if (iv_bits == 0 && iv != NULL) { /* 0 -> cipher doesn't use iv, but it was provided */ + ret = YACA_ERROR_INVALID_ARGUMENT; + goto err_free; + } + + liv = key_get_simple(iv); + if (ret != 0 && liv == NULL) { /* cipher requires iv, but none was provided */ + ret = YACA_ERROR_INVALID_ARGUMENT; + goto err_free; + } + + // TODO: handling of algorithms with variable IV length + if (iv_bits != yaca_key_get_length(iv)) { /* IV length doesn't match cipher */ + ret = YACA_ERROR_INVALID_ARGUMENT; + goto err_free; + } + + nc->cipher_ctx = EVP_CIPHER_CTX_new(); + if (nc->cipher_ctx == NULL) { + ret = YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret); + goto err_free; + } + + switch (op_type) { + case OP_ENCRYPT: + ret = EVP_EncryptInit(nc->cipher_ctx, cipher, + (unsigned char*)lkey->d, + (unsigned char*)liv->d); + break; + case OP_DECRYPT: + ret = EVP_DecryptInit(nc->cipher_ctx, cipher, + (unsigned char*)lkey->d, + (unsigned char*)liv->d); + break; + default: + ret = YACA_ERROR_INVALID_ARGUMENT; + goto err_ctx; + } + + if (ret != 1) { + ret = YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret); + goto err_ctx; + } + + *ctx = (yaca_ctx_h)nc; + return 0; + +err_ctx: + EVP_CIPHER_CTX_free(nc->cipher_ctx); +err_free: + yaca_free(nc); + return ret; +} + +static int encrypt_update(yaca_ctx_h ctx, + const unsigned char *input, + size_t input_len, + unsigned char *output, + size_t *output_len, + enum encrypt_op_type op_type) +{ + struct yaca_encrypt_ctx_s *c = get_encrypt_ctx(ctx); + int ret; + int loutput_len; + + if (c == NULL || input == NULL || input_len == 0 || + output == NULL || output_len == NULL || op_type != c->op_type) + return YACA_ERROR_INVALID_ARGUMENT; + + loutput_len = *output_len; + + switch (op_type) { + case OP_ENCRYPT: + ret = EVP_EncryptUpdate(c->cipher_ctx, output, &loutput_len, + input, input_len); + break; + case OP_DECRYPT: + ret = EVP_DecryptUpdate(c->cipher_ctx, output, &loutput_len, + input, input_len); + break; + default: + return YACA_ERROR_INVALID_ARGUMENT; + } + + if (ret != 1) + return YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret); + + *output_len = loutput_len; + return 0; +} + +static int encrypt_final(yaca_ctx_h ctx, + unsigned char *output, + size_t *output_len, + enum encrypt_op_type op_type) +{ + struct yaca_encrypt_ctx_s *c = get_encrypt_ctx(ctx); + int ret; + int loutput_len; + + if (c == NULL || output == NULL || output_len == NULL || + op_type != c->op_type) + return YACA_ERROR_INVALID_ARGUMENT; + + loutput_len = *output_len; + + switch (op_type) { + case OP_ENCRYPT: + ret = EVP_EncryptFinal(c->cipher_ctx, output, &loutput_len); + break; + case OP_DECRYPT: + ret = EVP_DecryptFinal(c->cipher_ctx, output, &loutput_len); + break; + default: + return YACA_ERROR_INVALID_ARGUMENT; + } + + if (ret != 1) + return YACA_ERROR_OPENSSL_FAILURE; // TODO: yaca_get_error_code_from_openssl(ret); + + *output_len = loutput_len; + return 0; +} + API int yaca_get_iv_bits(yaca_enc_algo_e algo, yaca_block_cipher_mode_e bcm, size_t key_bits) @@ -111,7 +341,7 @@ API int yaca_get_iv_bits(yaca_enc_algo_e algo, const EVP_CIPHER *cipher; int ret; - ret = get_symmetric_algorithm(algo, bcm, key_bits, &cipher); + ret = get_encrypt_algorithm(algo, bcm, key_bits, &cipher); if (ret < 0) return ret; @@ -124,7 +354,7 @@ API int yaca_encrypt_init(yaca_ctx_h *ctx, const yaca_key_h sym_key, const yaca_key_h iv) { - return YACA_ERROR_NOT_IMPLEMENTED; + return encrypt_init(ctx, algo, bcm, sym_key, iv, OP_ENCRYPT); } API int yaca_encrypt_update(yaca_ctx_h ctx, @@ -133,14 +363,16 @@ API int yaca_encrypt_update(yaca_ctx_h ctx, char *cipher, size_t *cipher_len) { - return YACA_ERROR_NOT_IMPLEMENTED; + return encrypt_update(ctx, (const unsigned char*)plain, plain_len, + (unsigned char*)cipher, cipher_len, OP_ENCRYPT); } API int yaca_encrypt_final(yaca_ctx_h ctx, char *cipher, size_t *cipher_len) { - return YACA_ERROR_NOT_IMPLEMENTED; + return encrypt_final(ctx, (unsigned char*)cipher, + cipher_len, OP_ENCRYPT); } API int yaca_decrypt_init(yaca_ctx_h *ctx, @@ -149,7 +381,7 @@ API int yaca_decrypt_init(yaca_ctx_h *ctx, const yaca_key_h sym_key, const yaca_key_h iv) { - return YACA_ERROR_NOT_IMPLEMENTED; + return encrypt_init(ctx, algo, bcm, sym_key, iv, OP_DECRYPT); } API int yaca_decrypt_update(yaca_ctx_h ctx, @@ -158,14 +390,16 @@ API int yaca_decrypt_update(yaca_ctx_h ctx, char *plain, size_t *plain_len) { - return YACA_ERROR_NOT_IMPLEMENTED; + return encrypt_update(ctx, (const unsigned char*)cipher, cipher_len, + (unsigned char*)plain, plain_len, OP_DECRYPT); } API int yaca_decrypt_final(yaca_ctx_h ctx, char *plain, size_t *plain_len) { - return YACA_ERROR_NOT_IMPLEMENTED; + return encrypt_final(ctx,(unsigned char*)plain, plain_len, + OP_DECRYPT); } API int yaca_seal_init(yaca_ctx_h *ctx, -- 2.7.4