Add GCM IV generator. Add some FIPS restrictions to GCM. Update fips_gcmtest.
[openssl.git] / crypto / evp / e_aes.c
index 34a350a..4206fd0 100644 (file)
@@ -59,6 +59,7 @@
 #include <openssl/aes.h>
 #include "evp_locl.h"
 #include <openssl/modes.h>
+#include <openssl/rand.h>
 
 static int aes_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
                                        const unsigned char *iv, int enc);
@@ -197,11 +198,15 @@ typedef struct
        int iv_set;
        /* Pointer to GCM128_CTX: FIXME actual structure later */
        GCM128_CONTEXT *gcm;
+       /* Temporary IV store */
+       unsigned char *iv;
        /* IV length */
        int ivlen;
        /* Tag to verify */
        unsigned char tag[16];
        int taglen;
+       /* It is OK to generate IVs */
+       int iv_gen;
        } EVP_AES_GCM_CTX;
 
 static int aes_gcm_cleanup(EVP_CIPHER_CTX *c)
@@ -209,9 +214,25 @@ static int aes_gcm_cleanup(EVP_CIPHER_CTX *c)
        EVP_AES_GCM_CTX *gctx = c->cipher_data;
        if (gctx->gcm)
                CRYPTO_gcm128_release(gctx->gcm);
+       if (gctx->iv != c->iv)
+               OPENSSL_free(gctx->iv);
        return 1;
        }
 
+/* increment counter (64-bit int) by 1 */
+static void ctr64_inc(unsigned char *counter) {
+       int n=8;
+       unsigned char  c;
+
+       do {
+               --n;
+               c = counter[n];
+               ++c;
+               counter[n] = c;
+               if (c) return;
+       } while (n);
+}
+
 static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
        {
        EVP_AES_GCM_CTX *gctx = c->cipher_data;
@@ -222,12 +243,28 @@ static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
                gctx->key_set = 0;
                gctx->iv_set = 0;
                gctx->ivlen = c->cipher->iv_len;
+               gctx->iv = c->iv;
                gctx->taglen = -1;
+               gctx->iv_gen = 0;
                return 1;
 
        case EVP_CTRL_GCM_SET_IVLEN:
                if (arg <= 0)
                        return 0;
+#ifdef OPENSSL_FIPS
+               if (FIPS_mode() && !(c->flags & EVP_CIPH_FLAG_NON_FIPS_ALLOW)
+                                                && arg < 12)
+                       return 0;
+#endif
+               /* Allocate memory for IV if needed */
+               if ((arg > EVP_MAX_IV_LENGTH) && (arg > gctx->ivlen))
+                       {
+                       if (gctx->iv != c->iv)
+                               OPENSSL_free(gctx->iv);
+                       gctx->iv = OPENSSL_malloc(arg);
+                       if (!gctx->iv)
+                               return 0;
+                       }
                gctx->ivlen = arg;
                return 1;
 
@@ -244,6 +281,39 @@ static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
                memcpy(ptr, gctx->tag, arg);
                return 1;
 
+       case EVP_CTRL_GCM_SET_IV_FIXED:
+               /* Special case: -1 length restores whole IV */
+               if (arg == -1)
+                       {
+                       memcpy(gctx->iv, ptr, gctx->ivlen);
+                       gctx->iv_gen = 1;
+                       return 1;
+                       }
+               /* Fixed field must be at least 4 bytes and invocation field
+                * at least 8.
+                */
+               if ((arg < 4) || (gctx->ivlen - arg) < 8)
+                       return 0;
+               if (arg)
+                       memcpy(gctx->iv, ptr, arg);
+               if (RAND_bytes(gctx->iv + arg, gctx->ivlen - arg) <= 0)
+                       return 0;
+               gctx->iv_gen = 1;
+               return 1;
+
+       case EVP_CTRL_GCM_IV_GEN:
+               if (gctx->iv_gen == 0 || gctx->key_set == 0)
+                       return 0;
+               CRYPTO_gcm128_setiv(gctx->gcm, gctx->iv, gctx->ivlen);
+               memcpy(ptr, gctx->iv, gctx->ivlen);
+               /* Invocation field will be at least 8 bytes in size and
+                * so no need to check wrap around or increment more than
+                * last 8 bytes.
+                */
+               ctr64_inc(gctx->iv + gctx->ivlen - 8);
+               gctx->iv_set = 1;
+               return 1;
+
        default:
                return -1;
 
@@ -272,7 +342,7 @@ static int aes_gcm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
                 * saved IV.
                 */
                if (iv == NULL && gctx->iv_set)
-                       iv = ctx->iv;
+                       iv = gctx->iv;
                if (iv)
                        {
                        CRYPTO_gcm128_setiv(gctx->gcm, iv, gctx->ivlen);
@@ -286,16 +356,9 @@ static int aes_gcm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
                if (gctx->key_set)
                        CRYPTO_gcm128_setiv(gctx->gcm, iv, gctx->ivlen);
                else
-                       {
-                       /* If IV is too large for EVP_CIPHER_CTX buffer
-                        * return an error. This can be avoided by either
-                        * setting the key first or key and iv simultaneously.
-                        */
-                       if (gctx->ivlen > EVP_MAX_IV_LENGTH)
-                               return 0;
-                       memcpy(ctx->iv, iv, gctx->ivlen);
-                       }
+                       memcpy(gctx->iv, iv, gctx->ivlen);
                gctx->iv_set = 1;
+               gctx->iv_gen = 0;
                }
        return 1;
        }
@@ -312,11 +375,20 @@ static int aes_gcm(EVP_CIPHER_CTX *ctx, unsigned char *out,
        if (in)
                {
                if (out == NULL)
-                       CRYPTO_gcm128_aad(gctx->gcm, in, len);
+                       {
+                       if (CRYPTO_gcm128_aad(gctx->gcm, in, len))
+                               return -1;
+                       }
                else if (ctx->encrypt)
-                       CRYPTO_gcm128_encrypt(gctx->gcm, in, out, len);
+                       {
+                       if (CRYPTO_gcm128_encrypt(gctx->gcm, in, out, len))
+                               return -1;
+                       }
                else
-                       CRYPTO_gcm128_decrypt(gctx->gcm, in, out, len);
+                       {
+                       if (CRYPTO_gcm128_decrypt(gctx->gcm, in, out, len))
+                               return -1;
+                       }
                return len;
                }
        else