Add support to AES GCM/CCM. Update documentation. 66/69766/10
authorDariusz Michaluk <d.michaluk@samsung.com>
Mon, 16 May 2016 12:54:35 +0000 (14:54 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Fri, 27 May 2016 09:50:55 +0000 (02:50 -0700)
Change-Id: Ifecf73fe15061afe8ad28a9ad20abc79f518f387

api/yaca/encrypt.h
api/yaca/simple.h
api/yaca/types.h
src/encrypt.c

index cc07262..de7bc58 100644 (file)
@@ -36,8 +36,6 @@ extern "C" {
  *
  * TODO: extended description and examples.
  *
- * TODO: Let's describe how to set additional params (like GCM, CCM)
- *
  * @{
  */
 
index b43a60b..9c62314 100644 (file)
@@ -39,7 +39,7 @@ extern "C" {
  *  - All operations are single-shot (no streaming possible)
  *  - Context is not used
  *  - For now only digest and symmetric ciphers are supported
- *  - GCM chaining is not supported
+ *  - GCM and CCM chaining is not supported
  *  - All outputs are allocated by the library
  *
  * TODO: extended description and examples.
index 0baf11c..70a097d 100644 (file)
@@ -273,11 +273,27 @@ typedef enum {
        YACA_BCM_CBC,
 
        /**
-        * GCM block cipher mode, IV is needed.
+        * GCM block cipher mode.
+        * This is a variable IV length mode (recommended 96 bits IV).
+        *
         * Supported parameters:
-        * - #YACA_PARAM_GCM_TAG = GCM tag
-        * - #YACA_PARAM_GCM_TAG_LEN = GCM tag length
-        * - #YACA_PARAM_GCM_AAD = additional authentication data(optional)
+        * - #YACA_PARAM_GCM_TAG_LEN = GCM tag length\n
+        *   Supported tag lengths: @c 32, @c 64, @c 96, @c 104, @c 112, @c 120, @c 128,
+        *   (recommended 128 bits tag).\n
+        *   Set after yaca_encrypt_final() and before yaca_ctx_get_param(#YACA_PARAM_GCM_TAG)
+        *   in encryption operation.\n\n
+        *
+        * - #YACA_PARAM_GCM_TAG = GCM tag\n
+        *   Get after yaca_encrypt_final() in encryption operation.\n
+        *   Set before yaca_decrypt_final() in decryption operation.\n\n
+        *
+        * - #YACA_PARAM_GCM_AAD = additional authentication data (optional)\n
+        *   Set after yaca_encrypt_init() and before yaca_encrypt_update()
+        *   in encryption operation.\n
+        *   Set after yaca_decrypt_init() and before yaca_decrypt_update()
+        *   in decryption operation.\n\n
+        *
+        *   @see examples/encrypt_aes_gcm_ccm.c
         */
        YACA_BCM_GCM,
 
@@ -307,10 +323,33 @@ typedef enum {
 
        /**
         * CBC-MAC Mode (AES).
+        * This is a variable IV length mode.\n
+        * Supported IV lengths: 56-104 bits in steps of 8 bits (recommended 56 bits IV).\n\n
+        *
         * Supported parameters:
-        * - #YACA_PARAM_CCM_TAG = CCM tag
-        * - #YACA_PARAM_CCM_TAG_LEN = CCM tag length
-        * - #YACA_PARAM_CCM_AAD = additional authentication data(optional)
+        * - #YACA_PARAM_CCM_TAG_LEN = CCM tag length\n
+        *   Supported tag lengths: 32-128 bits in step of 16 bits (recommended 96 bits tag).\n
+        *   Set after yaca_encrypt_init() and before yaca_encrypt_update()
+        *   in encryption operation.\n\n
+        *
+        * - #YACA_PARAM_CCM_TAG = CCM tag\n
+        *   Get after yaca_encrypt_final() in encryption operation.\n
+        *   Set after yaca_decrypt_init() and before yaca_decrypt_update()
+        *   in decryption operation.\n\n
+        *
+        * - #YACA_PARAM_CCM_AAD = additional authentication data (optional)\n
+        *   The total plain text length must be passed to yaca_encrypt_update()
+        *   if AAD is used.\n
+        *   Set after yaca_encrypt_init() and before yaca_encrypt_update()
+        *   in encryption operation.\n
+        *   You can only call yaca_encrypt_update() once for AAD and once for the plain text.\n\n
+        *
+        *   The total encrypted text length must be passed to yaca_decrypt_update()
+        *   if AAD is used.\n
+        *   Set after yaca_decrypt_init() and before yaca_decrypt_update()
+        *   in decryption operation.\n\n
+        *
+        *   @see examples/encrypt_aes_gcm_ccm.c
         */
        YACA_BCM_CCM
 
index f4749cd..c3d93ee 100644 (file)
@@ -44,6 +44,7 @@ struct yaca_encrypt_ctx_s
 
        EVP_CIPHER_CTX *cipher_ctx;
        enum encrypt_op_type op_type; /* Operation context was created for */
+       size_t tag_len;
 };
 
 static struct yaca_encrypt_ctx_s *get_encrypt_ctx(const yaca_ctx_h ctx)
@@ -76,8 +77,9 @@ static int get_encrypt_output_length(const yaca_ctx_h ctx, size_t input_len, siz
        struct yaca_encrypt_ctx_s *nc = get_encrypt_ctx(ctx);
        int block_size;
 
-       if (nc == NULL || nc->cipher_ctx == NULL)
+       if (nc == NULL)
                return YACA_ERROR_INVALID_ARGUMENT;
+       assert(nc->cipher_ctx != NULL);
 
        block_size = EVP_CIPHER_CTX_block_size(nc->cipher_ctx);
        if (block_size <= 0) {
@@ -97,6 +99,107 @@ static int get_encrypt_output_length(const yaca_ctx_h ctx, size_t input_len, siz
        return 0;
 }
 
+static int set_encrypt_param(yaca_ctx_h ctx,
+                             yaca_ex_param_e param,
+                             const void *value,
+                             size_t value_len)
+{
+       struct yaca_encrypt_ctx_s *c = get_encrypt_ctx(ctx);
+       int len;
+
+       if (c == NULL || value == NULL)
+               return YACA_ERROR_INVALID_ARGUMENT;
+       assert(c->cipher_ctx != NULL);
+
+       switch(param)
+       {
+       case YACA_PARAM_GCM_AAD:
+       case YACA_PARAM_CCM_AAD:
+               if (EVP_EncryptUpdate(c->cipher_ctx, NULL, &len, value, value_len) != 1) {
+                       ERROR_DUMP(YACA_ERROR_INTERNAL);
+                       return YACA_ERROR_INTERNAL;
+               }
+               break;
+       case YACA_PARAM_GCM_TAG:
+               if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
+                                       EVP_CTRL_GCM_SET_TAG,
+                                       value_len, (void*)value) != 1) {
+                       ERROR_DUMP(YACA_ERROR_INTERNAL);
+                       return YACA_ERROR_INTERNAL;
+               }
+               break;
+       case YACA_PARAM_GCM_TAG_LEN:
+               c->tag_len = *(int*)value;
+               break;
+       case YACA_PARAM_CCM_TAG:
+               // TODO Rebuild context
+               if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
+                                       EVP_CTRL_CCM_SET_TAG,
+                                       value_len, (void*)value) != 1) {
+                       ERROR_DUMP(YACA_ERROR_INTERNAL);
+                       return YACA_ERROR_INTERNAL;
+               }
+               break;
+       case YACA_PARAM_CCM_TAG_LEN:
+               //TODO Rebuild context
+               if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
+                                       EVP_CTRL_CCM_SET_TAG,
+                                       value_len, NULL) != 1) {
+                       ERROR_DUMP(YACA_ERROR_INTERNAL);
+                       return YACA_ERROR_INTERNAL;
+               }
+               c->tag_len = *(int*)value;
+               break;
+       default:
+               return YACA_ERROR_INVALID_ARGUMENT;
+       }
+       return 0;
+}
+
+static int get_encrypt_param(const yaca_ctx_h ctx,
+                             yaca_ex_param_e param,
+                             void **value,
+                             size_t *value_len)
+{
+       struct yaca_encrypt_ctx_s *c = get_encrypt_ctx(ctx);
+
+       if (c == NULL || value == NULL || value_len == NULL)
+               return YACA_ERROR_INVALID_ARGUMENT;
+       assert(c->cipher_ctx != NULL);
+
+       switch(param)
+       {
+       case YACA_PARAM_GCM_TAG:
+               if (c->tag_len == 0)
+                       return YACA_ERROR_INVALID_ARGUMENT;
+
+               if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
+                                       EVP_CTRL_GCM_GET_TAG,
+                                       c->tag_len, value) != 1) {
+                       ERROR_DUMP(YACA_ERROR_INTERNAL);
+                       return YACA_ERROR_INTERNAL;
+               }
+               *value_len = c->tag_len;
+               break;
+       case YACA_PARAM_CCM_TAG:
+               if (c->tag_len == 0)
+                       return YACA_ERROR_INVALID_ARGUMENT;
+
+               if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
+                                       EVP_CTRL_CCM_GET_TAG,
+                                       c->tag_len, value) != 1) {
+                       ERROR_DUMP(YACA_ERROR_INTERNAL);
+                       return YACA_ERROR_INTERNAL;
+               }
+               *value_len = c->tag_len;
+               break;
+       default:
+               return YACA_ERROR_INVALID_ARGUMENT;
+               break;
+       }
+       return 0;
+}
+
 static const char *encrypt_algo_to_str(yaca_enc_algo_e algo)
 {
        switch(algo)
@@ -240,7 +343,10 @@ static int encrypt_init(yaca_ctx_h *ctx,
        nc->ctx.type = YACA_CTX_ENCRYPT;
        nc->ctx.ctx_destroy = destroy_encrypt_ctx;
        nc->ctx.get_output_length = get_encrypt_output_length;
+       nc->ctx.set_param = set_encrypt_param;
+       nc->ctx.get_param = get_encrypt_param;
        nc->op_type = op_type;
+       nc->tag_len = 0;
 
        ret = yaca_key_get_bits(sym_key, &key_bits);
        if (ret != 0)
@@ -274,7 +380,10 @@ static int encrypt_init(yaca_ctx_h *ctx,
                        ret = YACA_ERROR_INVALID_ARGUMENT;
                        goto err_free;
                }
-               if (iv_bits != iv_bits_check) { /* IV length doesn't match cipher */
+               /* IV length doesn't match cipher (GCM & CCM supports variable IV length) */
+               if (iv_bits != iv_bits_check &&
+                   bcm != YACA_BCM_GCM &&
+                   bcm != YACA_BCM_CCM) {
                        ret = YACA_ERROR_INVALID_ARGUMENT;
                        goto err_free;
                }
@@ -291,39 +400,56 @@ static int encrypt_init(yaca_ctx_h *ctx,
        switch (op_type) {
        case OP_ENCRYPT:
                ret = EVP_EncryptInit_ex(nc->cipher_ctx, cipher, NULL, NULL, NULL);
-               if (ret != 1)
-                       break;
+               break;
+       case OP_DECRYPT:
+               ret = EVP_DecryptInit_ex(nc->cipher_ctx, cipher, NULL, NULL, NULL);
+               break;
+       default:
+               ret = YACA_ERROR_INVALID_ARGUMENT;
+               goto err_ctx;
+       }
+
+       if (ret != 1) {
+               ret = YACA_ERROR_INTERNAL;
+               ERROR_DUMP(ret);
+               goto err_ctx;
+       }
+
+       /* Handling of algorithms with variable key length */
+       ret = EVP_CIPHER_CTX_set_key_length(nc->cipher_ctx, key_bits / 8);
+       if (ret != 1) {
+               ret = YACA_ERROR_INVALID_ARGUMENT;
+               ERROR_DUMP(ret);
+               goto err_ctx;
+       }
+
+       /* Handling of algorithms with variable IV length */
+       if (iv_bits != iv_bits_check) {
+               if (bcm == YACA_BCM_GCM)
+                       ret = EVP_CIPHER_CTX_ctrl(nc->cipher_ctx, EVP_CTRL_GCM_SET_IVLEN,
+                                                 iv_bits_check / 8, NULL);
+
+               if (bcm == YACA_BCM_CCM)
+                       ret = EVP_CIPHER_CTX_ctrl(nc->cipher_ctx, EVP_CTRL_CCM_SET_IVLEN,
+                                                 iv_bits_check / 8, NULL);
 
-               /* Handling of algorithms with variable key length */
-               ret = EVP_CIPHER_CTX_set_key_length(nc->cipher_ctx, key_bits / 8);
                if (ret != 1) {
                        ret = YACA_ERROR_INVALID_ARGUMENT;
                        ERROR_DUMP(ret);
                        goto err_ctx;
                }
+       }
 
+       switch (op_type) {
+       case OP_ENCRYPT:
                ret = EVP_EncryptInit_ex(nc->cipher_ctx, NULL, NULL,
                                         (unsigned char*)lkey->d,
                                         iv_data);
-
                break;
        case OP_DECRYPT:
-               ret = EVP_DecryptInit_ex(nc->cipher_ctx, cipher, NULL, NULL, NULL);
-               if (ret != 1)
-                       break;
-
-               /* Handling of algorithms with variable key length */
-               ret = EVP_CIPHER_CTX_set_key_length(nc->cipher_ctx, key_bits / 8);
-               if (ret != 1) {
-                       ret = YACA_ERROR_INVALID_ARGUMENT;
-                       ERROR_DUMP(ret);
-                       goto err_ctx;
-               }
-
                ret = EVP_DecryptInit_ex(nc->cipher_ctx, NULL, NULL,
                                         (unsigned char*)lkey->d,
                                         iv_data);
-
                break;
        default:
                ret = YACA_ERROR_INVALID_ARGUMENT;
@@ -357,8 +483,7 @@ static int encrypt_update(yaca_ctx_h ctx,
        int ret;
        int loutput_len;
 
-       if (c == NULL || input == NULL || input_len == 0 ||
-           output == NULL || output_len == NULL || op_type != c->op_type)
+       if (c == NULL || input_len == 0 || output_len == NULL || op_type != c->op_type)
                return YACA_ERROR_INVALID_ARGUMENT;
 
        loutput_len = *output_len;