Implement limited symmetric cipher support 01/65601/8
authorMateusz Kulikowski <m.kulikowski@samsung.com>
Mon, 11 Apr 2016 13:35:20 +0000 (15:35 +0200)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Fri, 15 Apr 2016 12:10:05 +0000 (14:10 +0200)
Change-Id: I5b130e3e1d41cfcce1f730f3d1b316c088432677
Signed-off-by: Mateusz Kulikowski <m.kulikowski@samsung.com>
src/encrypt.c

index bfd730d2776b67de616f6da3e3ebe5fa2d930481..d1958001b985aece7c0d9dde8ca1e9e5c24148ed 100644 (file)
  */
 
 #include <assert.h>
+#include <string.h>
 
 #include <openssl/crypto.h>
 #include <openssl/rand.h>
 #include <openssl/evp.h>
 
 #include <yaca/crypto.h>
+#include <yaca/encrypt.h>
 #include <yaca/error.h>
+#include <yaca/key.h>
 
 #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,