Add custom tag length support in CCM 45/77945/27
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 12 Jul 2016 11:05:17 +0000 (13:05 +0200)
committerDariusz Michaluk <d.michaluk@samsung.com>
Tue, 19 Jul 2016 15:21:49 +0000 (08:21 -0700)
Change-Id: Iff8432d47b371ded3e95927fd90c5800be038597

src/encrypt.c
src/internal.h
src/seal.c

index 1c18b88..283a843 100644 (file)
@@ -24,6 +24,7 @@
 #include <assert.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <limits.h>
 
 #include <openssl/evp.h>
 
@@ -34,6 +35,9 @@
 
 #include "internal.h"
 
+static const size_t DEFAULT_GCM_TAG_LEN = 16;
+static const size_t DEFAULT_CCM_TAG_LEN = 12;
+
 static bool is_encryption_op(enum encrypt_op_type_e op_type)
 {
        return (op_type == OP_ENCRYPT || op_type == OP_SEAL);
@@ -59,6 +63,13 @@ void destroy_encrypt_context(const yaca_context_h ctx)
        if (nc == NULL)
                return;
 
+       if (nc->backup_ctx != NULL) {
+               yaca_key_destroy(nc->backup_ctx->iv);
+               yaca_key_destroy(nc->backup_ctx->sym_key);
+               yaca_free(nc->backup_ctx);
+               nc->backup_ctx = NULL;
+       }
+
        EVP_CIPHER_CTX_free(nc->cipher_ctx);
        nc->cipher_ctx = NULL;
 }
@@ -94,14 +105,304 @@ int get_encrypt_output_length(const yaca_context_h ctx, size_t input_len, size_t
        return YACA_ERROR_NONE;
 }
 
-int set_encrypt_property(yaca_context_h ctx, yaca_property_e property,
-                         const void *value, size_t value_len)
+static int encrypt_ctx_create(struct yaca_encrypt_context_s **c,
+                              enum encrypt_op_type_e op_type,
+                              const EVP_CIPHER *cipher)
+{
+       int ret;
+       int mode;
+       struct yaca_encrypt_context_s *nc;
+
+       assert(c != NULL);
+       assert(cipher != NULL);
+
+       ret = yaca_zalloc(sizeof(struct yaca_encrypt_context_s), (void**)&nc);
+       if (ret != YACA_ERROR_NONE)
+               return ret;
+
+       nc->ctx.type = YACA_CONTEXT_ENCRYPT;
+       nc->backup_ctx = NULL;
+       nc->ctx.context_destroy = destroy_encrypt_context;
+       nc->ctx.get_output_length = get_encrypt_output_length;
+       nc->ctx.set_property = set_encrypt_property;
+       nc->ctx.get_property = get_encrypt_property;
+       nc->op_type = op_type;
+       nc->tag_len = 0;
+
+       mode = EVP_CIPHER_flags(cipher) & EVP_CIPH_MODE;
+
+       /* set default tag length for GCM and CCM */
+       if (mode == EVP_CIPH_GCM_MODE)
+               nc->tag_len = DEFAULT_GCM_TAG_LEN;
+       else if (mode == EVP_CIPH_CCM_MODE)
+               nc->tag_len = DEFAULT_CCM_TAG_LEN;
+
+       nc->cipher_ctx = EVP_CIPHER_CTX_new();
+       if (nc->cipher_ctx == NULL) {
+               ret =  YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               goto exit;
+       }
+
+       *c = nc;
+       nc = NULL;
+
+       ret = YACA_ERROR_NONE;
+
+exit:
+       yaca_free(nc);
+       return ret;
+}
+
+static int encrypt_ctx_init(struct yaca_encrypt_context_s *c,
+                            const EVP_CIPHER *cipher,
+                            size_t key_bit_len)
+{
+       int ret;
+
+       assert(c != NULL);
+       assert(cipher != NULL);
+
+       if (key_bit_len / 8 > INT_MAX)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       ret = EVP_CipherInit_ex(c->cipher_ctx,
+                               cipher,
+                               NULL,
+                               NULL,
+                               NULL,
+                               is_encryption_op(c->op_type) ? 1 : 0);
+       if (ret != 1) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+
+       /* Handling of algorithms with variable key length */
+       ret = EVP_CIPHER_CTX_set_key_length(c->cipher_ctx, key_bit_len / 8);
+       if (ret != 1) {
+               ret = YACA_ERROR_INVALID_PARAMETER;
+               ERROR_CLEAR();
+               return ret;
+       }
+
+       return YACA_ERROR_NONE;
+}
+
+static int encrypt_ctx_setup_iv(struct yaca_encrypt_context_s *c,
+                                const EVP_CIPHER *cipher,
+                                const struct yaca_key_simple_s *iv)
+{
+       int ret;
+       size_t default_iv_bit_len;
+
+       assert(c != NULL);
+       assert(cipher != NULL);
+
+       ret = EVP_CIPHER_iv_length(cipher);
+       if (ret < 0) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+
+       default_iv_bit_len = ret * 8;
+
+       /* 0 -> cipher doesn't use iv, but it was provided */
+       if (default_iv_bit_len == 0 && iv != NULL)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       if (default_iv_bit_len != 0) { /* cipher requires iv */
+                /* iv was not provided */
+               if (iv == NULL || iv->key.type != YACA_KEY_TYPE_IV)
+                       return YACA_ERROR_INVALID_PARAMETER;
+
+               if (iv->bit_len / 8 > INT_MAX)
+                       return YACA_ERROR_INVALID_PARAMETER;
+
+               /* IV length doesn't match cipher (GCM & CCM supports variable IV length) */
+               if (default_iv_bit_len != iv->bit_len) {
+                       int mode = EVP_CIPHER_CTX_mode(c->cipher_ctx);
+
+                       if (mode == EVP_CIPH_GCM_MODE) {
+                               ret = EVP_CIPHER_CTX_ctrl(c->cipher_ctx, EVP_CTRL_GCM_SET_IVLEN,
+                                                         iv->bit_len / 8, NULL);
+                       } else if (mode == EVP_CIPH_CCM_MODE) {
+                               ret = EVP_CIPHER_CTX_ctrl(c->cipher_ctx, EVP_CTRL_CCM_SET_IVLEN,
+                                                         iv->bit_len / 8, NULL);
+                       } else {
+                               return YACA_ERROR_INVALID_PARAMETER;
+                       }
+
+                       if (ret != 1)
+                               return YACA_ERROR_INVALID_PARAMETER;
+               }
+       }
+
+       return YACA_ERROR_NONE;
+}
+
+static int encrypt_ctx_setup(struct yaca_encrypt_context_s *c,
+                             const yaca_key_h key,
+                             const yaca_key_h iv)
+{
+       int ret;
+       unsigned char *iv_data = NULL;
+       const struct yaca_key_simple_s *lkey;
+       const struct yaca_key_simple_s *liv;
+
+       assert(c != NULL);
+       assert(key != YACA_KEY_NULL);
+
+       const EVP_CIPHER *cipher = EVP_CIPHER_CTX_cipher(c->cipher_ctx);
+
+       if (cipher == NULL)
+               return YACA_ERROR_INTERNAL;
+
+       lkey = key_get_simple(key);
+       if (lkey == NULL)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       liv = key_get_simple(iv);
+
+       ret = encrypt_ctx_setup_iv(c, cipher, liv);
+       if (ret != YACA_ERROR_NONE)
+               return ret;
+
+       if (liv != NULL)
+               iv_data = (unsigned char*)liv->d;
+
+       ret = EVP_CipherInit_ex(c->cipher_ctx,
+                               NULL,
+                               NULL,
+                               (unsigned char*)lkey->d,
+                               iv_data,
+                               is_encryption_op(c->op_type) ? 1 : 0);
+       if (ret != 1) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+
+       return YACA_ERROR_NONE;
+}
+
+static int encrypt_ctx_backup(struct yaca_encrypt_context_s *c,
+                              const EVP_CIPHER *cipher,
+                              const yaca_key_h sym_key,
+                              const yaca_key_h iv)
+{
+       int ret;
+       struct yaca_backup_context_s *bc;
+
+       assert(c != NULL);
+       assert(cipher != NULL);
+       assert(sym_key != YACA_KEY_NULL);
+       assert(c->backup_ctx == NULL);
+
+       ret = yaca_zalloc(sizeof(struct yaca_backup_context_s), (void**)&bc);
+       if (ret != YACA_ERROR_NONE)
+               return ret;
+
+       bc->cipher = cipher;
+       bc->sym_key = key_copy(sym_key);
+       bc->iv = key_copy(iv);
+
+       c->backup_ctx = bc;
+
+       return YACA_ERROR_NONE;
+}
+
+static int encrypt_ctx_restore(struct yaca_encrypt_context_s *c)
+{
+       int ret;
+       struct yaca_key_simple_s *key;
+
+       assert(c != NULL);
+       assert(c->backup_ctx != NULL);
+
+       ret = EVP_CIPHER_CTX_cleanup(c->cipher_ctx);
+       if (ret != 1) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+
+       key = key_get_simple(c->backup_ctx->sym_key);
+       assert(key != NULL);
+
+       ret = encrypt_ctx_init(c, c->backup_ctx->cipher, key->bit_len);
+       assert(ret != YACA_ERROR_INVALID_PARAMETER);
+       return ret;
+}
+
+static int encrypt_ctx_set_ccm_tag_len(struct yaca_encrypt_context_s *c, size_t tag_len)
+{
+       int ret;
+
+       assert(c != NULL);
+       assert(c->backup_ctx != NULL);
+       assert(is_encryption_op(c->op_type));
+
+       if (tag_len == 0 || tag_len > INT_MAX)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       ret = encrypt_ctx_restore(c);
+       if (ret != YACA_ERROR_NONE)
+               return ret;
+
+       c->tag_len = tag_len;
+       ret = EVP_CIPHER_CTX_ctrl(c->cipher_ctx, EVP_CTRL_CCM_SET_TAG, tag_len, NULL);
+       if (ret != 1) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+
+       ret = encrypt_ctx_setup(c, c->backup_ctx->sym_key, c->backup_ctx->iv);
+       assert(ret != YACA_ERROR_INVALID_PARAMETER);
+       return ret;
+}
+
+static int encrypt_ctx_set_ccm_tag(struct yaca_encrypt_context_s *c, char *tag, size_t tag_len)
+{
+       int ret;
+
+       assert(c != NULL);
+       assert(c->backup_ctx != NULL);
+       assert(!is_encryption_op(c->op_type));
+       assert(tag != NULL);
+
+       if (tag_len == 0 || tag_len > INT_MAX)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       ret = encrypt_ctx_restore(c);
+       if (ret != YACA_ERROR_NONE)
+               return ret;
+
+       ret = EVP_CIPHER_CTX_ctrl(c->cipher_ctx, EVP_CTRL_CCM_SET_TAG, tag_len, tag);
+       if (ret != 1) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+
+       ret = encrypt_ctx_setup(c, c->backup_ctx->sym_key, c->backup_ctx->iv);
+       assert(ret != YACA_ERROR_INVALID_PARAMETER);
+       return ret;
+}
+
+int set_encrypt_property(yaca_context_h ctx,
+                         yaca_property_e property,
+                         const void *value,
+                         size_t value_len)
 {
        struct yaca_encrypt_context_s *c = get_encrypt_context(ctx);
        int len;
+       int ret = YACA_ERROR_NONE;
        int mode;
 
-       if (c == NULL || value == NULL)
+       if (c == NULL || value == NULL || value_len == 0)
                return YACA_ERROR_INVALID_PARAMETER;
        assert(c->cipher_ctx != NULL);
 
@@ -127,7 +428,8 @@ int set_encrypt_property(yaca_context_h ctx, yaca_property_e property,
                }
                break;
        case YACA_PROPERTY_GCM_TAG:
-               if (mode != EVP_CIPH_GCM_MODE || is_encryption_op(c->op_type))
+               if (mode != EVP_CIPH_GCM_MODE || is_encryption_op(c->op_type) ||
+                   value_len == 0 || value_len > INT_MAX)
                        return YACA_ERROR_INVALID_PARAMETER;
 
                if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
@@ -140,7 +442,8 @@ int set_encrypt_property(yaca_context_h ctx, yaca_property_e property,
                break;
        case YACA_PROPERTY_GCM_TAG_LEN:
                if (value_len != sizeof(size_t) || mode != EVP_CIPH_GCM_MODE ||
-                   !is_encryption_op(c->op_type))
+                   !is_encryption_op(c->op_type) ||
+                   *(size_t*)value == 0 || *(size_t*)value > INT_MAX)
                        return YACA_ERROR_INVALID_PARAMETER;
 
                c->tag_len = *(size_t*)value;
@@ -149,22 +452,20 @@ int set_encrypt_property(yaca_context_h ctx, yaca_property_e property,
                if (mode != EVP_CIPH_CCM_MODE || is_encryption_op(c->op_type))
                        return YACA_ERROR_INVALID_PARAMETER;
 
-               // TODO rebuild context and set the tag
-
+               ret = encrypt_ctx_set_ccm_tag(c, (char*)value, value_len);
                break;
        case YACA_PROPERTY_CCM_TAG_LEN:
                if (value_len != sizeof(size_t) || mode != EVP_CIPH_CCM_MODE ||
                    !is_encryption_op(c->op_type))
                        return YACA_ERROR_INVALID_PARAMETER;
 
-               // TODO rebuild context and set the tag len
-
+               ret = encrypt_ctx_set_ccm_tag_len(c, *(size_t*)value);
                break;
        default:
                return YACA_ERROR_INVALID_PARAMETER;
        }
 
-       return YACA_ERROR_NONE;
+       return ret;
 }
 
 int get_encrypt_property(const yaca_context_h ctx, yaca_property_e property,
@@ -184,9 +485,12 @@ int get_encrypt_property(const yaca_context_h ctx, yaca_property_e property,
                if (value_len == NULL || !is_encryption_op(c->op_type) || mode != EVP_CIPH_GCM_MODE)
                        return YACA_ERROR_INVALID_PARAMETER;
 
+               assert(c->tag_len <= INT_MAX);
+
                if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
                                        EVP_CTRL_GCM_GET_TAG,
-                                       c->tag_len, value) != 1) {
+                                       c->tag_len,
+                                       value) != 1) {
                        ERROR_DUMP(YACA_ERROR_INTERNAL);
                        return YACA_ERROR_INTERNAL;
                }
@@ -196,9 +500,12 @@ int get_encrypt_property(const yaca_context_h ctx, yaca_property_e property,
                if (value_len == NULL || !is_encryption_op(c->op_type) || mode != EVP_CIPH_CCM_MODE)
                        return YACA_ERROR_INVALID_PARAMETER;
 
+               assert(c->tag_len <= INT_MAX);
+
                if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
                                        EVP_CTRL_CCM_GET_TAG,
-                                       c->tag_len, value) != 1) {
+                                       c->tag_len,
+                                       value) != 1) {
                        ERROR_DUMP(YACA_ERROR_INTERNAL);
                        return YACA_ERROR_INTERNAL;
                }
@@ -273,7 +580,9 @@ int encrypt_get_algorithm(yaca_encrypt_algorithm_e algo,
        const EVP_CIPHER *lcipher;
        int ret;
 
-       if (algo_name == NULL || bcm_name == NULL || key_bit_len == 0 || cipher == NULL)
+       assert(cipher != NULL);
+
+       if (algo_name == NULL || bcm_name == NULL || key_bit_len == 0)
                return YACA_ERROR_INVALID_PARAMETER;
 
        switch (algo) {
@@ -321,138 +630,41 @@ int encrypt_get_algorithm(yaca_encrypt_algorithm_e algo,
        return YACA_ERROR_NONE;
 }
 
-static int encrypt_initialize(yaca_context_h *ctx,
-                              yaca_encrypt_algorithm_e algo,
-                              yaca_block_cipher_mode_e bcm,
-                              const yaca_key_h sym_key,
-                              const yaca_key_h iv,
-                              enum encrypt_op_type_e op_type)
+int encrypt_initialize(yaca_context_h *ctx,
+                       const EVP_CIPHER *cipher,
+                       const yaca_key_h sym_key,
+                       const yaca_key_h iv,
+                       enum encrypt_op_type_e op_type)
 {
-       const struct yaca_key_simple_s *lkey;
-       const struct yaca_key_simple_s *liv;
        struct yaca_encrypt_context_s *nc;
-       const EVP_CIPHER *cipher;
-       size_t key_bit_len;
-       unsigned char *iv_data = NULL;
-       size_t iv_bit_len;
-       size_t iv_bit_len_check;
+       struct yaca_key_simple_s *lsym_key;
        int ret;
-
-       assert(op_type == OP_ENCRYPT || op_type == OP_DECRYPT);
+       int mode;
 
        if (ctx == NULL || sym_key == YACA_KEY_NULL)
                return YACA_ERROR_INVALID_PARAMETER;
 
-       lkey = key_get_simple(sym_key);
-       if (lkey == NULL)
+       lsym_key = key_get_simple(sym_key);
+       if (lsym_key == NULL)
                return YACA_ERROR_INVALID_PARAMETER;
 
-       ret = yaca_zalloc(sizeof(struct yaca_encrypt_context_s), (void**)&nc);
+       ret = encrypt_ctx_create(&nc, op_type, cipher);
        if (ret != YACA_ERROR_NONE)
                return ret;
 
-       nc->ctx.type = YACA_CONTEXT_ENCRYPT;
-       nc->ctx.context_destroy = destroy_encrypt_context;
-       nc->ctx.get_output_length = get_encrypt_output_length;
-       nc->ctx.set_property = set_encrypt_property;
-       nc->ctx.get_property = get_encrypt_property;
-       nc->op_type = op_type;
-       nc->tag_len = 0;
-
-       ret = yaca_key_get_bit_length(sym_key, &key_bit_len);
+       ret = encrypt_ctx_init(nc, cipher, lsym_key->bit_len);
        if (ret != YACA_ERROR_NONE)
                goto exit;
 
-       ret = encrypt_get_algorithm(algo, bcm, key_bit_len, &cipher);
+       ret = encrypt_ctx_setup(nc, sym_key, iv);
        if (ret != YACA_ERROR_NONE)
                goto exit;
 
-       ret = EVP_CIPHER_iv_length(cipher);
-       if (ret < 0) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
-               goto exit;
-       }
-
-       iv_bit_len = ret * 8;
-       if (iv_bit_len == 0 && iv != NULL) { /* 0 -> cipher doesn't use iv, but it was provided */
-               ret = YACA_ERROR_INVALID_PARAMETER;
-               goto exit;
-       }
-
-       if (iv_bit_len != 0) { /* cipher requires iv*/
-               liv = key_get_simple(iv);
-               if (liv == NULL) { /* iv was not provided */
-                       ret = YACA_ERROR_INVALID_PARAMETER;
+       mode = EVP_CIPHER_CTX_mode(nc->cipher_ctx);
+       if (mode == EVP_CIPH_CCM_MODE) {
+               ret = encrypt_ctx_backup(nc, cipher, sym_key, iv);
+               if (ret != YACA_ERROR_NONE)
                        goto exit;
-               }
-               ret = yaca_key_get_bit_length(iv, &iv_bit_len_check);
-               if (ret != YACA_ERROR_NONE) {
-                       ret = YACA_ERROR_INVALID_PARAMETER;
-                       goto exit;
-               }
-               /* IV length doesn't match cipher (GCM & CCM supports variable IV length) */
-               if (iv_bit_len != iv_bit_len_check &&
-                   bcm != YACA_BCM_GCM &&
-                   bcm != YACA_BCM_CCM) {
-                       ret = YACA_ERROR_INVALID_PARAMETER;
-                       goto exit;
-               }
-               iv_data = (unsigned char*)liv->d;
-       }
-
-       nc->cipher_ctx = EVP_CIPHER_CTX_new();
-       if (nc->cipher_ctx == NULL) {
-               ret =  YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
-               goto exit;
-       }
-
-       ret = EVP_CipherInit_ex(nc->cipher_ctx,
-                               cipher,
-                               NULL,
-                               NULL,
-                               NULL,
-                               is_encryption_op(op_type) ? 1 : 0);
-       if (ret != 1) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
-               goto exit;
-       }
-
-       /* Handling of algorithms with variable key length */
-       ret = EVP_CIPHER_CTX_set_key_length(nc->cipher_ctx, key_bit_len / 8);
-       if (ret != 1) {
-               ret = YACA_ERROR_INVALID_PARAMETER;
-               ERROR_CLEAR();
-               goto exit;
-       }
-
-       /* Handling of algorithms with variable IV length */
-       if (iv_bit_len != iv_bit_len_check) {
-               if (bcm == YACA_BCM_GCM)
-                       ret = EVP_CIPHER_CTX_ctrl(nc->cipher_ctx, EVP_CTRL_GCM_SET_IVLEN,
-                                                 iv_bit_len_check / 8, NULL);
-
-               if (bcm == YACA_BCM_CCM)
-                       ret = EVP_CIPHER_CTX_ctrl(nc->cipher_ctx, EVP_CTRL_CCM_SET_IVLEN,
-                                                 iv_bit_len_check / 8, NULL);
-
-               if (ret != 1) {
-                       ret = YACA_ERROR_INVALID_PARAMETER;
-                       ERROR_DUMP(ret);
-                       goto exit;
-               }
-       }
-
-       ret = EVP_CipherInit_ex(nc->cipher_ctx, NULL, NULL,
-                               (unsigned char*)lkey->d,
-                               iv_data,
-                               is_encryption_op(op_type) ? 1 : 0);
-       if (ret != 1) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
-               goto exit;
        }
 
        *ctx = (yaca_context_h)nc;
@@ -545,7 +757,17 @@ API int yaca_encrypt_initialize(yaca_context_h *ctx,
                                 const yaca_key_h sym_key,
                                 const yaca_key_h iv)
 {
-       return encrypt_initialize(ctx, algo, bcm, sym_key, iv, OP_ENCRYPT);
+       const EVP_CIPHER *cipher;
+       struct yaca_key_simple_s *key = key_get_simple(sym_key);
+
+       if (key == NULL)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       int ret = encrypt_get_algorithm(algo, bcm, key->bit_len, &cipher);
+       if (ret != YACA_ERROR_NONE)
+               return ret;
+
+       return encrypt_initialize(ctx, cipher, sym_key, iv, OP_ENCRYPT);
 }
 
 API int yaca_encrypt_update(yaca_context_h ctx,
@@ -571,7 +793,17 @@ API int yaca_decrypt_initialize(yaca_context_h *ctx,
                                 const yaca_key_h sym_key,
                                 const yaca_key_h iv)
 {
-       return encrypt_initialize(ctx, algo, bcm, sym_key, iv, OP_DECRYPT);
+       const EVP_CIPHER *cipher;
+       struct yaca_key_simple_s *key = key_get_simple(sym_key);
+
+       if (key == NULL)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       int ret = encrypt_get_algorithm(algo, bcm, key->bit_len, &cipher);
+       if (ret != YACA_ERROR_NONE)
+               return ret;
+
+       return encrypt_initialize(ctx, cipher, sym_key, iv, OP_DECRYPT);
 }
 
 API int yaca_decrypt_update(yaca_context_h ctx,
index 762c019..4b6fdcd 100644 (file)
@@ -60,8 +60,15 @@ struct yaca_context_s {
                            void **value, size_t *value_len);
 };
 
+struct yaca_backup_context_s {
+       const EVP_CIPHER *cipher;
+       yaca_key_h sym_key;
+       yaca_key_h iv;
+};
+
 struct yaca_encrypt_context_s {
        struct yaca_context_s ctx;
+       struct yaca_backup_context_s *backup_ctx;
 
        EVP_CIPHER_CTX *cipher_ctx;
        enum encrypt_op_type_e op_type; /* Operation context was created for */
@@ -119,6 +126,12 @@ int encrypt_get_algorithm(yaca_encrypt_algorithm_e algo,
                           size_t key_bit_len,
                           const EVP_CIPHER **cipher);
 
+int encrypt_initialize(yaca_context_h *ctx,
+                       const EVP_CIPHER *cipher,
+                       const yaca_key_h sym_key,
+                       const yaca_key_h iv,
+                       enum encrypt_op_type_e op_type);
+
 int encrypt_update(yaca_context_h ctx,
                    const unsigned char *input, size_t input_len,
                    unsigned char *output, size_t *output_len,
index 7ff74d1..623d2e8 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <assert.h>
 #include <stdint.h>
+#include <string.h>
 
 #include <openssl/evp.h>
 
 
 #include "internal.h"
 
-API int yaca_seal_initialize(yaca_context_h *ctx,
-                             const yaca_key_h pub_key,
-                             yaca_encrypt_algorithm_e algo,
-                             yaca_block_cipher_mode_e bcm,
-                             size_t sym_key_bit_len,
-                             yaca_key_h *sym_key,
-                             yaca_key_h *iv)
+static int seal_generate_sym_key(const EVP_CIPHER *cipher, yaca_key_h *sym_key)
+{
+       int ret;
+       int key_len;
+
+       assert(sym_key != NULL);
+       assert(cipher != NULL);
+
+       ret = EVP_CIPHER_key_length(cipher);
+       if (ret <= 0) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+       key_len = ret;
+
+       return yaca_key_generate(YACA_KEY_TYPE_SYMMETRIC, key_len * 8, sym_key);
+}
+
+static int seal_generate_iv(const EVP_CIPHER *cipher, yaca_key_h *iv)
+{
+       int ret;
+       int iv_len;
+
+       assert(iv != NULL);
+       assert(cipher != NULL);
+
+       ret = EVP_CIPHER_iv_length(cipher);
+       if (ret < 0) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               return ret;
+       }
+
+       iv_len = ret;
+       if (iv_len == 0) {
+               *iv = YACA_KEY_NULL;
+               return YACA_ERROR_NONE;
+       }
+
+       return yaca_key_generate(YACA_KEY_TYPE_IV, iv_len * 8, iv);
+}
+
+/* used for asymmetric encryption and decryption */
+static int seal_encrypt_decrypt_key(const yaca_key_h asym_key,
+                                    const yaca_key_h in_key,
+                                    yaca_key_h *out_key)
 {
-       struct yaca_key_evp_s *lpub;
-       struct yaca_key_simple_s *lkey = NULL;
-       struct yaca_key_simple_s *liv = NULL;
-       struct yaca_encrypt_context_s *nc;
-       const EVP_CIPHER *cipher;
-       int pub_key_length;
-       unsigned char *key_data = NULL;
-       int key_data_length;
-       unsigned char *iv_data = NULL;
-       int iv_length;
        int ret;
+       const struct yaca_key_evp_s *lasym_key;
+       const struct yaca_key_simple_s *lin_key;
+       struct yaca_key_simple_s *lout_key;
+       size_t output_len;
+
+       lin_key = key_get_simple(in_key);
+       if (lin_key == NULL)
+               return YACA_ERROR_INVALID_PARAMETER;
 
-       if (ctx == NULL || pub_key == YACA_KEY_NULL)
+       if (asym_key->type != YACA_KEY_TYPE_RSA_PRIV && asym_key->type != YACA_KEY_TYPE_RSA_PUB)
                return YACA_ERROR_INVALID_PARAMETER;
 
-       if (pub_key->type != YACA_KEY_TYPE_RSA_PUB)
+       lasym_key = key_get_evp(asym_key);
+       if (lasym_key == NULL)
                return YACA_ERROR_INVALID_PARAMETER;
-       lpub = key_get_evp(pub_key);
-       assert(lpub);
 
-       ret = yaca_zalloc(sizeof(struct yaca_encrypt_context_s), (void**)&nc);
+       ret = EVP_PKEY_size(lasym_key->evp);
+       if (ret <= 0)
+               return YACA_ERROR_INTERNAL;
+
+       output_len = ret;
+
+       ret = yaca_zalloc(sizeof(struct yaca_key_simple_s) + output_len, (void**)&lout_key);
        if (ret != YACA_ERROR_NONE)
                return ret;
 
-       nc->ctx.type = YACA_CONTEXT_ENCRYPT;
-       nc->ctx.context_destroy = destroy_encrypt_context;
-       nc->ctx.get_output_length = get_encrypt_output_length;
-       nc->ctx.set_property = set_encrypt_property;
-       nc->ctx.get_property = get_encrypt_property;
-       nc->op_type = OP_SEAL;
-       nc->tag_len = 0;
-
-       nc->cipher_ctx = EVP_CIPHER_CTX_new();
-       if (nc->cipher_ctx == NULL) {
-               ret =  YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
-               goto exit;
-       }
+       lout_key->key.type = YACA_KEY_TYPE_SYMMETRIC;
+       lout_key->bit_len = output_len * 8;
+
+       if (asym_key->type == YACA_KEY_TYPE_RSA_PRIV)
+               ret = EVP_PKEY_decrypt_old((unsigned char*)lout_key->d,
+                                          (unsigned char*)lin_key->d,
+                                          lin_key->bit_len / 8,
+                                          lasym_key->evp);
+       else
+               ret = EVP_PKEY_encrypt_old((unsigned char*)lout_key->d,
+                                          (unsigned char*)lin_key->d,
+                                          lin_key->bit_len / 8,
+                                          lasym_key->evp);
 
-       ret = EVP_PKEY_size(lpub->evp);
        if (ret <= 0) {
                ret = YACA_ERROR_INTERNAL;
                ERROR_DUMP(ret);
                goto exit;
        }
 
-       pub_key_length = ret;
-       ret = yaca_zalloc(sizeof(struct yaca_key_simple_s) + pub_key_length, (void**)&lkey);
+       output_len = ret;
+
+       /* Update the key length just in case */
+       lout_key->bit_len = output_len * 8;
+
+       *out_key = (yaca_key_h)lout_key;
+       lout_key = NULL;
+
+       ret = YACA_ERROR_NONE;
+
+exit:
+       yaca_key_destroy((yaca_key_h)lout_key);
+
+       return ret;
+}
+
+API int yaca_seal_initialize(yaca_context_h *ctx,
+                             const yaca_key_h pub_key,
+                             yaca_encrypt_algorithm_e algo,
+                             yaca_block_cipher_mode_e bcm,
+                             size_t bit_len,
+                             yaca_key_h *enc_sym_key,
+                             yaca_key_h *iv)
+{
+       int ret;
+       const EVP_CIPHER *cipher;
+       yaca_key_h lsym_key = YACA_KEY_NULL;
+       yaca_key_h liv = YACA_KEY_NULL;
+       yaca_key_h lenc_sym_key = YACA_KEY_NULL;
+
+       if (pub_key == YACA_KEY_NULL || pub_key->type != YACA_KEY_TYPE_RSA_PUB ||
+           enc_sym_key == NULL)
+               return YACA_ERROR_INVALID_PARAMETER;
+
+       ret = encrypt_get_algorithm(algo, bcm, bit_len, &cipher);
        if (ret != YACA_ERROR_NONE)
                goto exit;
-       key_data = (unsigned char*)lkey->d;
 
-       ret = encrypt_get_algorithm(algo, bcm, sym_key_bit_len, &cipher);
+       ret = seal_generate_sym_key(cipher, &lsym_key);
        if (ret != YACA_ERROR_NONE)
                goto exit;
 
-       ret = EVP_CIPHER_iv_length(cipher);
-       if (ret < 0) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
+       ret = seal_generate_iv(cipher, &liv);
+       if (ret != YACA_ERROR_NONE)
                goto exit;
-       }
 
-       iv_length = ret;
-       if (iv_length > 0) {
-               ret = yaca_zalloc(sizeof(struct yaca_key_simple_s) + iv_length, (void**)&liv);
-               if (ret != YACA_ERROR_NONE)
-                       goto exit;
-               iv_data = (unsigned char*)liv->d;
+       if (liv != YACA_KEY_NULL && iv == NULL) {
+               ret = YACA_ERROR_INVALID_PARAMETER;
+               goto exit;
        }
 
-       ret = EVP_SealInit(nc->cipher_ctx,
-                          cipher,
-                          &key_data,
-                          &key_data_length,
-                          iv_data,
-                          &lpub->evp,
-                          1);
+       /* using public key will make it encrypt the symmetric key */
+       ret = seal_encrypt_decrypt_key(pub_key, lsym_key, &lenc_sym_key);
+       if (ret != YACA_ERROR_NONE)
+               goto exit;
 
-       if (ret != 1) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
+       ret = encrypt_initialize(ctx, cipher, lsym_key, liv, OP_SEAL);
+       if (ret != YACA_ERROR_NONE)
                goto exit;
-       }
 
-       lkey->bit_len = key_data_length * 8;
-       lkey->key.type = YACA_KEY_TYPE_SYMMETRIC;
-       *sym_key = (yaca_key_h)lkey;
-       lkey = NULL;
-
-       if (iv_length > 0) {
-               liv->bit_len = iv_length * 8;
-               liv->key.type = YACA_KEY_TYPE_IV;
-               *iv = (yaca_key_h)liv;
-               liv = NULL;
-       } else {
-               *iv = NULL;
-       }
-       *ctx = (yaca_context_h)nc;
-       nc = NULL;
+       *enc_sym_key = lenc_sym_key;
+       lenc_sym_key = YACA_KEY_NULL;
+       *iv = liv;
+       liv = YACA_KEY_NULL;
        ret = YACA_ERROR_NONE;
 
 exit:
-       yaca_free(liv);
-       yaca_free(lkey);
-       yaca_context_destroy((yaca_context_h)nc);
+       yaca_key_destroy(liv);
+       yaca_key_destroy(lsym_key);
+       yaca_key_destroy(lenc_sym_key);
 
        return ret;
 }
@@ -172,105 +224,35 @@ API int yaca_open_initialize(yaca_context_h *ctx,
                              const yaca_key_h prv_key,
                              yaca_encrypt_algorithm_e algo,
                              yaca_block_cipher_mode_e bcm,
-                             size_t sym_key_bit_len,
-                             const yaca_key_h sym_key,
+                             size_t bit_len,
+                             const yaca_key_h enc_sym_key,
                              const yaca_key_h iv)
 {
-       const struct yaca_key_evp_s *lprv;
-       const struct yaca_key_simple_s *lkey;
-       const struct yaca_key_simple_s *liv;
-       struct yaca_encrypt_context_s *nc;
-       const EVP_CIPHER *cipher;
-       unsigned char *iv_data = NULL;
-       size_t iv_bit_len;
-       size_t iv_bit_len_check;
        int ret;
+       const EVP_CIPHER *cipher;
+       yaca_key_h lsym_key = YACA_KEY_NULL;
 
-       if (ctx == NULL || prv_key == YACA_KEY_NULL || sym_key == YACA_KEY_NULL)
-               return YACA_ERROR_INVALID_PARAMETER;
-
-       if (prv_key->type != YACA_KEY_TYPE_RSA_PRIV)
-               return YACA_ERROR_INVALID_PARAMETER;
-       lprv = key_get_evp(prv_key);
-       assert(lprv);
-
-       lkey = key_get_simple(sym_key);
-       if (lkey == NULL || lkey->key.type != YACA_KEY_TYPE_SYMMETRIC)
+       if (prv_key == YACA_KEY_NULL || prv_key->type != YACA_KEY_TYPE_RSA_PRIV ||
+           enc_sym_key == YACA_KEY_NULL)
                return YACA_ERROR_INVALID_PARAMETER;
 
-       ret = yaca_zalloc(sizeof(struct yaca_encrypt_context_s), (void**)&nc);
+       ret = encrypt_get_algorithm(algo, bcm, bit_len, &cipher);
        if (ret != YACA_ERROR_NONE)
-               return ret;
-
-       nc->ctx.type = YACA_CONTEXT_ENCRYPT;
-       nc->ctx.context_destroy = destroy_encrypt_context;
-       nc->ctx.get_output_length = get_encrypt_output_length;
-       nc->ctx.set_property = set_encrypt_property;
-       nc->ctx.get_property = get_encrypt_property;
-       nc->op_type = OP_OPEN;
-       nc->tag_len = 0;
-
-       ret = encrypt_get_algorithm(algo, bcm, sym_key_bit_len, &cipher);
-       if (ret != YACA_ERROR_NONE)
-               goto exit;
-
-       ret = EVP_CIPHER_iv_length(cipher);
-       if (ret < 0) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
                goto exit;
-       }
 
-       iv_bit_len = ret * 8;
-       if (iv_bit_len == 0 && iv != NULL) { /* 0 -> cipher doesn't use iv, but it was provided */
-               ret = YACA_ERROR_INVALID_PARAMETER;
+       /* using private key will make it decrypt the symmetric key */
+       ret = seal_encrypt_decrypt_key(prv_key, enc_sym_key, &lsym_key);
+       if (ret != YACA_ERROR_NONE)
                goto exit;
-       }
-
-       if (iv_bit_len > 0) { /* cipher requires iv*/
-               liv = key_get_simple(iv);
-               if (liv == NULL || liv->key.type != YACA_KEY_TYPE_IV) { /* iv was not provided */
-                       ret = YACA_ERROR_INVALID_PARAMETER;
-                       goto exit;
-               }
-               ret = yaca_key_get_bit_length(iv, &iv_bit_len_check);
-               if (ret != YACA_ERROR_NONE) {
-                       ret = YACA_ERROR_INVALID_PARAMETER;
-                       goto exit;
-               }
-               /* IV length doesn't match cipher */
-               if (iv_bit_len != iv_bit_len_check) {
-                       ret = YACA_ERROR_INVALID_PARAMETER;
-                       goto exit;
-               }
-               iv_data = (unsigned char*)liv->d;
-       }
 
-       nc->cipher_ctx = EVP_CIPHER_CTX_new();
-       if (nc->cipher_ctx == NULL) {
-               ret =  YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
-               goto exit;
-       }
-
-       ret = EVP_OpenInit(nc->cipher_ctx, cipher,
-                          (unsigned char*)lkey->d,
-                          EVP_PKEY_size(lprv->evp),
-                          iv_data,
-                          lprv->evp);
-       if (ret != 1) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
+       ret = encrypt_initialize(ctx, cipher, lsym_key, iv, OP_OPEN);
+       if (ret != YACA_ERROR_NONE)
                goto exit;
-       }
 
-       *ctx = (yaca_context_h)nc;
-       nc = NULL;
        ret = YACA_ERROR_NONE;
 
 exit:
-       yaca_context_destroy((yaca_context_h)nc);
-
+       yaca_key_destroy(lsym_key);
        return ret;
 }