GCM and CCM fixes. 57/86757/7
authorDariusz Michaluk <d.michaluk@samsung.com>
Fri, 2 Sep 2016 13:58:07 +0000 (15:58 +0200)
committerDariusz Michaluk <d.michaluk@samsung.com>
Fri, 9 Sep 2016 13:01:21 +0000 (15:01 +0200)
Change-Id: I0ddb50041c871bac78f44f34844df2259618a01f

api/yaca/yaca_encrypt.h
api/yaca/yaca_seal.h
api/yaca/yaca_types.h
examples/encrypt_aes_gcm_ccm.c
examples/seal.c
src/encrypt.c

index 97bfe58..72b2236 100755 (executable)
@@ -199,7 +199,8 @@ int yaca_decrypt_initialize(yaca_context_h *ctx,
  * @return #YACA_ERROR_NONE on success, negative on error
  * @retval #YACA_ERROR_NONE Successful
  * @retval #YACA_ERROR_INVALID_PARAMETER Required parameters have incorrect values (NULL, 0,
- *                                       invalid context)
+ *                                       invalid context), wrong #YACA_PROPERTY_CCM_AAD or
+ *                                       wrong #YACA_PROPERTY_CCM_TAG was used
  * @retval #YACA_ERROR_INTERNAL Internal error
  *
  * @see yaca_decrypt_initialize()
@@ -229,7 +230,8 @@ int yaca_decrypt_update(yaca_context_h ctx,
  * @return #YACA_ERROR_NONE on success, negative on error
  * @retval #YACA_ERROR_NONE Successful
  * @retval #YACA_ERROR_INVALID_PARAMETER Required parameters have incorrect values (NULL,
- *                                       invalid context)
+ *                                       invalid context), wrong #YACA_PROPERTY_GCM_AAD or
+ *                                       wrong #YACA_PROPERTY_GCM_TAG was used
  * @retval #YACA_ERROR_INTERNAL Internal error
  *
  * @see yaca_decrypt_initialize()
index 910d6aa..c06a8b7 100755 (executable)
@@ -207,7 +207,8 @@ int yaca_open_initialize(yaca_context_h *ctx,
  * @return #YACA_ERROR_NONE on success, negative on error
  * @retval #YACA_ERROR_NONE Successful
  * @retval #YACA_ERROR_INVALID_PARAMETER Required parameters have incorrect values (NULL, 0,
- *                                       invalid context)
+ *                                       invalid context), wrong #YACA_PROPERTY_CCM_AAD or
+ *                                       wrong #YACA_PROPERTY_CCM_TAG was used
  * @retval #YACA_ERROR_INTERNAL Internal error
  *
  * @see yaca_open_initialize()
@@ -237,7 +238,8 @@ int yaca_open_update(yaca_context_h ctx,
  * @return #YACA_ERROR_NONE on success, negative on error
  * @retval #YACA_ERROR_NONE Successful
  * @retval #YACA_ERROR_INVALID_PARAMETER Required parameters have incorrect values (NULL,
- *                                       invalid context)
+ *                                       invalid context), wrong #YACA_PROPERTY_GCM_AAD or
+ *                                       wrong #YACA_PROPERTY_GCM_TAG was used
  * @retval #YACA_ERROR_INTERNAL Internal error
  *
  * @see yaca_open_initialize()
index 3afb1ca..0f0ae33 100755 (executable)
@@ -484,9 +484,9 @@ typedef enum {
         * This is a variable Initialization Vector length mode (recommended 96-bits).
         *
         * Supported properties:
-        * - #YACA_PROPERTY_GCM_TAG_LEN = GCM tag length\n
-        *   Supported tag lengths: @c 4, @c 8, @c 12, @c 13, @c 14, @c 15, @c 16,
-        *   (recommended 16 bytes tag).\n
+        * - #YACA_PROPERTY_GCM_TAG_LEN = GCM tag length (optional)\n
+        *   Supported tag lengths: @c 4, @c 8, @c 12, @c 13, @c 14, @c 15, @c 16
+        *   (16 bytes tag by default).\n
         *   Set after yaca_encrypt_finalize() / yaca_seal_finalize() and before
         *   yaca_context_get_property(#YACA_PROPERTY_GCM_TAG) in encryption / seal operation.\n
         *   The @a value should be a size_t variable.\n
@@ -494,7 +494,8 @@ typedef enum {
         *
         * - #YACA_PROPERTY_GCM_TAG = GCM tag\n
         *   Get after yaca_encrypt_finalize() / yaca_seal_finalize() in encryption / seal operation.\n
-        *   Set before yaca_decrypt_finalize() / yaca_open_finalize() in decryption / open operation.\n\n
+        *   Set after yaca_decrypt_update() / yaca_open_update() and before
+        *   yaca_decrypt_finalize() / yaca_open_finalize() in decryption / open operation.\n\n
         *
         * - #YACA_PROPERTY_GCM_AAD = additional authentication data (optional)\n
         *   Set after yaca_encrypt_initialize() / yaca_seal_initialize() and before
@@ -503,6 +504,8 @@ typedef enum {
         *   yaca_decrypt_update() / yaca_open_update() in decryption / open operation.\n\n
         *
         *   @see yaca_context_set_property()
+        *   @see yaca_context_get_property()
+        *
         */
        YACA_BCM_GCM,
 
@@ -541,8 +544,8 @@ typedef enum {
         * (recommended 56-bits).\n\n
         *
         * Supported properties:
-        * - #YACA_PROPERTY_CCM_TAG_LEN = CCM tag length\n
-        *   Supported tag lengths: 4-16 bytes in steps of 2 bytes (recommended 12 bytes tag).\n
+        * - #YACA_PROPERTY_CCM_TAG_LEN = CCM tag length (optional)\n
+        *   Supported tag lengths: 4-16 bytes in steps of 2 bytes (12 bytes tag by default).\n
         *   Set after yaca_encrypt_initialize() / yaca_seal_initialize() and before
         *   yaca_encrypt_update() / yaca_seal_update() in encryption / seal operation.\n
         *   The @a value should be a size_t variable.\n
@@ -564,9 +567,13 @@ typedef enum {
         *   The total encrypted text length must be passed to yaca_decrypt_update() /
         *   yaca_open_update() if AAD is used.\n
         *   Set after yaca_decrypt_initialize() / yaca_open_initialize() and before
-        *   yaca_decrypt_update() / yaca_open_update() in decryption / open operation.\n\n
+        *   yaca_decrypt_update() / yaca_open_update() in decryption / open operation.\n
+        *   You can only call yaca_decrypt_update() / yaca_open_update() once for AAD
+        *   and once for the encrypted text.\n\n
         *
         *   @see yaca_context_set_property()
+        *   @see yaca_context_get_property()
+        *
         */
        YACA_BCM_CCM,
 
index 64a689f..05f2cfd 100644 (file)
@@ -298,13 +298,16 @@ void encrypt_decrypt_aes_ccm(void)
                if (yaca_malloc(dec_len, (void**)&dec) != YACA_ERROR_NONE)
                        goto exit;
 
-               /* The tag verify is performed when you call the final yaca_decrypt_update(),
-                * there is no call to yaca_decrypt_finalize() */
                if (yaca_decrypt_update(ctx, enc, enc_len, dec, &written_len) != YACA_ERROR_NONE)
                        goto exit;
 
                dec_len = written_len;
 
+               if (yaca_decrypt_finalize(ctx, dec + written_len, &written_len) != YACA_ERROR_NONE)
+                       goto exit;
+
+               dec_len += written_len;
+
                printf("Decrypted data (16 of %zu bytes): %.16s\n\n", dec_len, dec);
        }
 
index c0feacf..494c416 100644 (file)
@@ -403,13 +403,16 @@ void encrypt_seal_aes_ccm(void)
                if (yaca_malloc(dec_len, (void**)&dec) != YACA_ERROR_NONE)
                        goto exit;
 
-               /* The tag verify is performed when you call the final yaca_open_update(),
-                * there is no call to yaca_open_finalize() */
                if (yaca_open_update(ctx, enc, enc_len, dec, &written_len) != YACA_ERROR_NONE)
                        goto exit;
 
                dec_len = written_len;
 
+               if (yaca_open_finalize(ctx, dec + written_len, &written_len) != YACA_ERROR_NONE)
+                       goto exit;
+
+               dec_len += written_len;
+
                printf("Decrypted data (16 of %zu bytes): %.16s\n\n", dec_len, dec);
        }
 
index 7e9fd58..d343efe 100644 (file)
@@ -205,6 +205,30 @@ static const size_t VALID_GCM_TAG_LENGTHS[] = { 4, 8, 12, 13, 14, 15, 16 };
 static const size_t VALID_GCM_TAG_LENGTHS_LENGTH =
                sizeof(VALID_GCM_TAG_LENGTHS) / sizeof(VALID_GCM_TAG_LENGTHS[0]);
 
+static const size_t VALID_CCM_TAG_LENGTHS[] = { 4, 6, 8, 10, 12, 14, 16 };
+static const size_t VALID_CCM_TAG_LENGTHS_LENGTH =
+               sizeof(VALID_CCM_TAG_LENGTHS) / sizeof(VALID_CCM_TAG_LENGTHS[0]);
+
+static bool is_valid_tag_len(int mode, size_t tag_len)
+{
+       switch (mode) {
+       case EVP_CIPH_GCM_MODE:
+               for (size_t i = 0; i < VALID_GCM_TAG_LENGTHS_LENGTH; i++) {
+                       if (tag_len == VALID_GCM_TAG_LENGTHS[i])
+                               return true;
+               }
+               return false;
+       case EVP_CIPH_CCM_MODE:
+               for (size_t i = 0; i < VALID_CCM_TAG_LENGTHS_LENGTH; i++) {
+                       if (tag_len == VALID_CCM_TAG_LENGTHS[i])
+                               return true;
+               }
+               return false;
+       default:
+               return false;
+       }
+}
+
 struct yaca_encrypt_context_s *get_encrypt_context(const yaca_context_h ctx)
 {
        if (ctx == YACA_CONTEXT_NULL)
@@ -556,9 +580,6 @@ static int encrypt_ctx_set_ccm_tag_len(struct yaca_encrypt_context_s *c, size_t
        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;
@@ -585,9 +606,6 @@ static int encrypt_ctx_set_ccm_tag(struct yaca_encrypt_context_s *c, char *tag,
        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;
@@ -650,10 +668,8 @@ int set_encrypt_property(yaca_context_h ctx,
 
        switch (property) {
        case YACA_PROPERTY_GCM_AAD:
-               if (mode != EVP_CIPH_GCM_MODE)
-                       return YACA_ERROR_INVALID_PARAMETER;
-
-               if (!verify_state_change(c, STATE_AAD_UPDATED))
+               if (mode != EVP_CIPH_GCM_MODE ||
+                   !verify_state_change(c, STATE_AAD_UPDATED))
                        return YACA_ERROR_INVALID_PARAMETER;
 
                if (EVP_CipherUpdate(c->cipher_ctx, NULL, &len, value, value_len) != 1) {
@@ -663,10 +679,8 @@ int set_encrypt_property(yaca_context_h ctx,
                c->state = STATE_AAD_UPDATED;
                break;
        case YACA_PROPERTY_CCM_AAD:
-               if (mode != EVP_CIPH_CCM_MODE)
-                       return YACA_ERROR_INVALID_PARAMETER;
-
-               if (!verify_state_change(c, STATE_AAD_UPDATED))
+               if (mode != EVP_CIPH_CCM_MODE ||
+                   !verify_state_change(c, STATE_AAD_UPDATED))
                        return YACA_ERROR_INVALID_PARAMETER;
 
                if (EVP_CipherUpdate(c->cipher_ctx, NULL, &len, value, value_len) != 1) {
@@ -677,16 +691,11 @@ int set_encrypt_property(yaca_context_h ctx,
                break;
        case YACA_PROPERTY_GCM_TAG:
                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 (!verify_state_change(c, STATE_TAG_SET))
+                   !is_valid_tag_len(mode, value_len) ||
+                   !verify_state_change(c, STATE_TAG_SET))
                        return YACA_ERROR_INVALID_PARAMETER;
 
-               if (EVP_CIPHER_CTX_ctrl(c->cipher_ctx,
-                                       EVP_CTRL_GCM_SET_TAG,
-                                       value_len,
-                                       (void*)value) != 1) {
+               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;
                }
@@ -695,30 +704,17 @@ int set_encrypt_property(yaca_context_h ctx,
        case YACA_PROPERTY_GCM_TAG_LEN:
                if (value_len != sizeof(size_t) || mode != EVP_CIPH_GCM_MODE ||
                    !is_encryption_op(c->op_type) ||
-                   *(size_t*)value == 0 || *(size_t*)value > INT_MAX)
+                   !is_valid_tag_len(mode, *(size_t*)value) ||
+                   !verify_state_change(c, STATE_TAG_LENGTH_SET))
                        return YACA_ERROR_INVALID_PARAMETER;
 
-               if (!verify_state_change(c, STATE_TAG_LENGTH_SET))
-                       return YACA_ERROR_INVALID_PARAMETER;
-
-               /* check allowed tag lengths */
-               {
-                       size_t tag_len = *(size_t*)value;
-                       for (size_t i = 0; i < VALID_GCM_TAG_LENGTHS_LENGTH; i++) {
-                               if (tag_len == VALID_GCM_TAG_LENGTHS[i]) {
-                                       c->tag_len = tag_len;
-                                       c->state = STATE_TAG_LENGTH_SET;
-                                       return YACA_ERROR_NONE;
-                               }
-                       }
-                       return YACA_ERROR_INVALID_PARAMETER;
-               }
+               c->tag_len = *(size_t*)value;
+               c->state = STATE_TAG_LENGTH_SET;
                break;
        case YACA_PROPERTY_CCM_TAG:
-               if (mode != EVP_CIPH_CCM_MODE || is_encryption_op(c->op_type))
-                       return YACA_ERROR_INVALID_PARAMETER;
-
-               if (!verify_state_change(c, STATE_TAG_SET))
+               if (mode != EVP_CIPH_CCM_MODE || is_encryption_op(c->op_type) ||
+                   !is_valid_tag_len(mode, value_len) ||
+                   !verify_state_change(c, STATE_TAG_SET))
                        return YACA_ERROR_INVALID_PARAMETER;
 
                ret = encrypt_ctx_set_ccm_tag(c, (char*)value, value_len);
@@ -729,10 +725,9 @@ int set_encrypt_property(yaca_context_h ctx,
                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;
-
-               if (!verify_state_change(c, STATE_TAG_LENGTH_SET))
+                   !is_encryption_op(c->op_type) ||
+                   !is_valid_tag_len(mode, *(size_t*)value) ||
+                   !verify_state_change(c, STATE_TAG_LENGTH_SET))
                        return YACA_ERROR_INVALID_PARAMETER;
 
                ret = encrypt_ctx_set_ccm_tag_len(c, *(size_t*)value);
@@ -1034,8 +1029,17 @@ int encrypt_update(yaca_context_h ctx,
 
        ret = EVP_CipherUpdate(c->cipher_ctx, output, &loutput_len, input, input_len);
        if (ret != 1 || loutput_len < 0) {
-               ret = YACA_ERROR_INTERNAL;
-               ERROR_DUMP(ret);
+               if (mode == EVP_CIPH_CCM_MODE && op_type == OP_DECRYPT) {
+                       /* A non positive return value from EVP_CipherUpdate should be considered as
+                        * a failure to authenticate ciphertext and/or AAD.
+                        * It does not necessarily indicate a more serious error.
+                        * There is no call to EVP_CipherFinal.
+                        */
+                       ret = YACA_ERROR_INVALID_PARAMETER;
+               } else {
+                       ret = YACA_ERROR_INTERNAL;
+                       ERROR_DUMP(ret);
+               }
                return ret;
        }
 
@@ -1056,6 +1060,7 @@ int encrypt_finalize(yaca_context_h ctx,
 {
        struct yaca_encrypt_context_s *c = get_encrypt_context(ctx);
        int ret;
+       int mode;
        int loutput_len = 0;
 
        if (c == NULL || output == NULL || output_len == NULL || op_type != c->op_type)
@@ -1064,10 +1069,19 @@ int encrypt_finalize(yaca_context_h ctx,
        if (!verify_state_change(c, STATE_FINALIZED))
                return YACA_ERROR_INVALID_PARAMETER;
 
-       if (EVP_CIPHER_CTX_mode(c->cipher_ctx) != EVP_CIPH_WRAP_MODE) {
+       mode = EVP_CIPHER_CTX_mode(c->cipher_ctx);
+       if (mode != EVP_CIPH_WRAP_MODE && mode != EVP_CIPH_CCM_MODE) {
                ret = EVP_CipherFinal(c->cipher_ctx, output, &loutput_len);
-               if (ret != 1 || loutput_len < 0)
-                       return ERROR_HANDLE();
+               if (ret != 1 || loutput_len < 0) {
+                       if (mode == EVP_CIPH_GCM_MODE && op_type == OP_DECRYPT)
+                       /* A non positive return value from EVP_CipherFinal should be considered as
+                        * a failure to authenticate ciphertext and/or AAD.
+                        * It does not necessarily indicate a more serious error.
+                        */
+                               return YACA_ERROR_INVALID_PARAMETER;
+                       else
+                               return ERROR_HANDLE();
+               }
        }
 
        *output_len = loutput_len;