Use aes-256-gcm rather than aes-128-cbc
authorNathaniel McCallum <npmccallum@redhat.com>
Tue, 10 Mar 2015 14:57:23 +0000 (10:57 -0400)
committerSimo Sorce <simo@redhat.com>
Tue, 10 Mar 2015 22:31:54 +0000 (18:31 -0400)
Also, remove all the manual HMAC code since it is no longer needed.

The end result should be faster and stronger authenticated encryption.

Closes #12
Reviewed-by: Simo Sorce <simo@redhat.com>
src/crypto.c
src/mod_auth_gssapi.c

index a5dea45..9e11deb 100644 (file)
@@ -6,15 +6,15 @@
 #include <stdbool.h>
 #include "crypto.h"
 
 #include <stdbool.h>
 #include "crypto.h"
 
+#define TAGSIZE 16
+
 struct seal_key {
     const EVP_CIPHER *cipher;
 struct seal_key {
     const EVP_CIPHER *cipher;
-    const EVP_MD *md;
     unsigned char *ekey;
     unsigned char *ekey;
-    unsigned char *hkey;
 };
 
 apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
 };
 
 apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
-                             struct databuf *keys)
+                             struct databuf *key)
 {
     struct seal_key *n;
     int keylen;
 {
     struct seal_key *n;
     int keylen;
@@ -23,7 +23,7 @@ apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
     n = apr_pcalloc(p, sizeof(*n));
     if (!n) return ENOMEM;
 
     n = apr_pcalloc(p, sizeof(*n));
     if (!n) return ENOMEM;
 
-    n->cipher = EVP_aes_128_cbc();
+    n->cipher = EVP_aes_256_gcm();
     if (!n->cipher) {
         ret = EFAULT;
         goto done;
     if (!n->cipher) {
         ret = EFAULT;
         goto done;
@@ -31,50 +31,30 @@ apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
 
     keylen = n->cipher->key_len;
 
 
     keylen = n->cipher->key_len;
 
-    n->md = EVP_sha256();
-    if (!n->md) {
-        ret = EFAULT;
-        goto done;
-    }
-
     n->ekey = apr_palloc(p, keylen);
     if (!n->ekey) {
         ret = ENOMEM;
         goto done;
     }
 
     n->ekey = apr_palloc(p, keylen);
     if (!n->ekey) {
         ret = ENOMEM;
         goto done;
     }
 
-    n->hkey = apr_palloc(p, keylen);
-    if (!n->hkey) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    if (keys) {
-        if (keys->length != (keylen * 2)) {
+    if (key) {
+        if (key->length < keylen) {
             ret = EINVAL;
             goto done;
         }
             ret = EINVAL;
             goto done;
         }
-        memcpy(n->ekey, keys->value, keylen);
-        memcpy(n->hkey, keys->value + keylen, keylen);
+        memcpy(n->ekey, key->value, keylen);
     } else {
         ret = apr_generate_random_bytes(n->ekey, keylen);
         if (ret != 0) {
             ret = EFAULT;
             goto done;
         }
     } else {
         ret = apr_generate_random_bytes(n->ekey, keylen);
         if (ret != 0) {
             ret = EFAULT;
             goto done;
         }
-
-        ret = apr_generate_random_bytes(n->hkey, keylen);
-        if (ret != 0) {
-            ret = EFAULT;
-            goto done;
-        }
     }
 
     ret = 0;
 done:
     if (ret) {
         free(n->ekey);
     }
 
     ret = 0;
 done:
     if (ret) {
         free(n->ekey);
-        free(n->hkey);
         free(n);
     } else {
         *skey = n;
         free(n);
     } else {
         *skey = n;
@@ -85,71 +65,58 @@ done:
 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
                          struct databuf *plain, struct databuf *cipher)
 {
 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
                          struct databuf *plain, struct databuf *cipher)
 {
-    int blksz = skey->cipher->block_size;
     apr_status_t err = EFAULT;
     apr_status_t err = EFAULT;
-    EVP_CIPHER_CTX ctx = { 0 };
-    HMAC_CTX hmac_ctx = { 0 };
-    uint8_t rbuf[blksz];
-    unsigned int len;
-    int outlen, totlen;
+    EVP_CIPHER_CTX ctx = {};
+    int minlen;
+    int outlen;
     int ret;
 
     EVP_CIPHER_CTX_init(&ctx);
 
     int ret;
 
     EVP_CIPHER_CTX_init(&ctx);
 
-    /* confounder to avoid exposing random numbers directly to clients
-     * as IVs */
-    ret = apr_generate_random_bytes(rbuf, sizeof(rbuf));
-    if (ret != 0) goto done;
-
-    if (cipher->length == 0) {
-        /* add space for confounder and padding and MAC */
-        cipher->length = (plain->length / blksz + 2) * blksz;
-        cipher->value = apr_palloc(p, cipher->length + skey->md->md_size);
+       /* Add space for padding, IV and tag. */
+    minlen = plain->length / skey->cipher->block_size + 1;
+    minlen *= skey->cipher->block_size;
+    minlen += skey->cipher->iv_len + TAGSIZE;
+    if (cipher->length < minlen) {
+        cipher->length = minlen;
+        cipher->value = apr_palloc(p, cipher->length);
         if (!cipher->value) {
             err = ENOMEM;
             goto done;
         }
     }
 
         if (!cipher->value) {
             err = ENOMEM;
             goto done;
         }
     }
 
-    ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
-    if (ret == 0) goto done;
-    totlen = 0;
-
-    outlen = cipher->length;
-    ret = EVP_EncryptUpdate(&ctx, cipher->value, &outlen, rbuf, sizeof(rbuf));
-    if (ret == 0) goto done;
-    totlen += outlen;
-
-    outlen = cipher->length - totlen;
-    ret = EVP_EncryptUpdate(&ctx, &cipher->value[totlen], &outlen,
-                            plain->value, plain->length);
-    if (ret == 0) goto done;
-    totlen += outlen;
-
-    outlen = cipher->length - totlen;
-    ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[totlen], &outlen);
-    if (ret == 0) goto done;
-    totlen += outlen;
-
-    /* now MAC the buffer */
-    HMAC_CTX_init(&hmac_ctx);
-
-    ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
-                       skey->cipher->key_len, skey->md, NULL);
-    if (ret == 0) goto done;
-
-    ret = HMAC_Update(&hmac_ctx, cipher->value, totlen);
-    if (ret == 0) goto done;
-
-    ret = HMAC_Final(&hmac_ctx, &cipher->value[totlen], &len);
-    if (ret == 0) goto done;
+    /* Generate IV. */
+    ret = apr_generate_random_bytes(cipher->value, skey->cipher->iv_len);
+    if (ret != 0) goto done;
+    cipher->length = skey->cipher->iv_len;
+
+    ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL,
+                             skey->ekey, cipher->value);
+    if (ret != 1) goto done;
+
+    /* Encrypt the data. */
+    outlen = 0;
+    ret = EVP_EncryptUpdate(&ctx, &cipher->value[cipher->length],
+                            &outlen, plain->value, plain->length);
+    if (ret != 1) goto done;
+    cipher->length += outlen;
+
+    outlen = 0;
+    ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[cipher->length], &outlen);
+    if (ret != 1) goto done;
+    cipher->length += outlen;
+
+    /* Get the tag */
+    ret = EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, TAGSIZE,
+                              &cipher->value[cipher->length]);
+    if (ret != 1) goto done;
+    cipher->length += TAGSIZE;
 
 
-    cipher->length = totlen + len;
     err = 0;
 
 done:
     EVP_CIPHER_CTX_cleanup(&ctx);
     err = 0;
 
 done:
     EVP_CIPHER_CTX_cleanup(&ctx);
-    HMAC_CTX_cleanup(&hmac_ctx);
     return err;
 }
 
     return err;
 }
 
@@ -157,41 +124,14 @@ apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
                            struct databuf *cipher, struct databuf *plain)
 {
     apr_status_t err = EFAULT;
                            struct databuf *cipher, struct databuf *plain)
 {
     apr_status_t err = EFAULT;
-    EVP_CIPHER_CTX ctx = { 0 };
-    HMAC_CTX hmac_ctx = { 0 };
-    unsigned char mac[skey->md->md_size];
-    unsigned int len;
-    int outlen, totlen;
-    volatile bool equal = true;
-    int ret, i;
-
-    /* check MAC first */
-    HMAC_CTX_init(&hmac_ctx);
-
-    ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
-                       skey->cipher->key_len, skey->md, NULL);
-    if (ret == 0) goto done;
-
-    cipher->length -= skey->md->md_size;
-
-    ret = HMAC_Update(&hmac_ctx, cipher->value, cipher->length);
-    if (ret == 0) goto done;
-
-    ret = HMAC_Final(&hmac_ctx, mac, &len);
-    if (ret == 0) goto done;
-
-    if (len != skey->md->md_size) goto done;
-    for (i = 0; i < skey->md->md_size; i++) {
-        if (cipher->value[cipher->length + i] != mac[i]) equal = false;
-        /* not breaking intentionally,
-         * or we would allow an oracle attack */
-    }
-    if (!equal) goto done;
+    EVP_CIPHER_CTX ctx = {};
+    int outlen;
+    int ret;
 
     EVP_CIPHER_CTX_init(&ctx);
 
 
     EVP_CIPHER_CTX_init(&ctx);
 
-    if (plain->length == 0) {
-        plain->length = cipher->length;
+    if (plain->length < cipher->length - skey->cipher->iv_len - TAGSIZE) {
+        plain->length = cipher->length - skey->cipher->iv_len - TAGSIZE;
         plain->value = apr_palloc(p, plain->length);
         if (!plain->value) {
             err = ENOMEM;
         plain->value = apr_palloc(p, plain->length);
         if (!plain->value) {
             err = ENOMEM;
@@ -199,30 +139,30 @@ apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
         }
     }
 
         }
     }
 
-    ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
-    if (ret == 0) goto done;
+    ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL,
+                             skey->ekey, cipher->value);
+    if (ret != 1) goto done;
+    plain->length = 0;
 
 
-    totlen = 0;
-    outlen = plain->length;
+    outlen = 0;
     ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
     ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
-                            cipher->value, cipher->length);
-    if (ret == 0) goto done;
+                            &cipher->value[skey->cipher->iv_len],
+                            cipher->length - skey->cipher->iv_len - TAGSIZE);
+    if (ret != 1) goto done;
+    plain->length += outlen;
 
 
-    totlen += outlen;
-    outlen = plain->length - totlen;
-    ret = EVP_DecryptFinal_ex(&ctx, plain->value, &outlen);
-    if (ret == 0) goto done;
+    ret = EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, TAGSIZE,
+                              &cipher->value[cipher->length - TAGSIZE]);
+    if (ret != 1) goto done;
 
 
-    totlen += outlen;
-    /* now remove the confounder */
-    totlen -= skey->cipher->block_size;
-    memmove(plain->value, plain->value + skey->cipher->block_size, totlen);
+    outlen = 0;
+    ret = EVP_DecryptFinal_ex(&ctx, &plain->value[plain->length], &outlen);
+    if (ret != 1) goto done;
+    plain->length += outlen;
 
 
-    plain->length = totlen;
     err = 0;
 
 done:
     EVP_CIPHER_CTX_cleanup(&ctx);
     err = 0;
 
 done:
     EVP_CIPHER_CTX_cleanup(&ctx);
-    HMAC_CTX_cleanup(&hmac_ctx);
     return err;
 }
     return err;
 }
index b5e6a2e..4f21123 100644 (file)
@@ -586,7 +586,7 @@ static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
 {
     struct mag_config *cfg = (struct mag_config *)mconfig;
 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
 {
     struct mag_config *cfg = (struct mag_config *)mconfig;
-    struct databuf keys;
+    struct databuf key;
     unsigned char *val;
     apr_status_t rc;
     const char *k;
     unsigned char *val;
     apr_status_t rc;
     const char *k;
@@ -607,16 +607,16 @@ static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
         return NULL;
     }
 
         return NULL;
     }
 
-    keys.length = (int)apr_base64_decode_binary(val, k);
-    keys.value = (unsigned char *)val;
+    key.length = (int)apr_base64_decode_binary(val, k);
+    key.value = (unsigned char *)val;
 
 
-    if (keys.length != 32) {
+    if (key.length < 32) {
         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
-                     "Invalid key lenght, expected 32 got %d", keys.length);
+                     "Invalid key length, expected >=32 got %d", key.length);
         return NULL;
     }
 
         return NULL;
     }
 
-    rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
+    rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &key);
     if (rc != OK) {
         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
                      "Failed to import sealing key!");
     if (rc != OK) {
         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
                      "Failed to import sealing key!");