openssl: Provide AES-GCM implementation
authorTobias Brunner <tobias@strongswan.org>
Tue, 12 Feb 2013 15:46:56 +0000 (16:46 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 28 Feb 2013 17:17:42 +0000 (18:17 +0100)
src/libstrongswan/plugins/openssl/Makefile.am
src/libstrongswan/plugins/openssl/openssl_gcm.c [new file with mode: 0644]
src/libstrongswan/plugins/openssl/openssl_gcm.h [new file with mode: 0644]
src/libstrongswan/plugins/openssl/openssl_plugin.c

index f971a5e..e715673 100644 (file)
@@ -25,7 +25,8 @@ libstrongswan_openssl_la_SOURCES = \
        openssl_crl.c openssl_crl.h \
        openssl_pkcs7.c openssl_pkcs7.h \
        openssl_rng.c openssl_rng.h \
-       openssl_hmac.c openssl_hmac.h
+       openssl_hmac.c openssl_hmac.h \
+       openssl_gcm.c openssl_gcm.h
 
 libstrongswan_openssl_la_LDFLAGS = -module -avoid-version
 libstrongswan_openssl_la_LIBADD  = -lcrypto
diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.c b/src/libstrongswan/plugins/openssl/openssl_gcm.c
new file mode 100644 (file)
index 0000000..fde7ae7
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "openssl_gcm.h"
+
+#include <openssl/evp.h>
+
+/** as defined in RFC 4106 */
+#define IV_LEN         8
+#define SALT_LEN       4
+#define NONCE_LEN      (IV_LEN + SALT_LEN)
+
+typedef struct private_aead_t private_aead_t;
+
+/**
+ * Private data of aead_t
+ */
+struct private_aead_t {
+
+       /**
+        * Public interface
+        */
+       aead_t public;
+
+       /**
+        * The encryption key
+        */
+       chunk_t key;
+
+       /**
+        * Salt value
+        */
+       char salt[SALT_LEN];
+
+       /**
+        * Size of the integrity check value
+        */
+       size_t icv_size;
+
+       /**
+        * The cipher to use
+        */
+       const EVP_CIPHER *cipher;
+};
+
+/**
+ * Do the actual en/decryption in an EVP context
+ */
+static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv,
+                                 u_char *out, int enc)
+{
+       EVP_CIPHER_CTX ctx;
+       u_char nonce[NONCE_LEN];
+       bool success = FALSE;
+       int len;
+
+       memcpy(nonce, this->salt, SALT_LEN);
+       memcpy(nonce + SALT_LEN, iv.ptr, IV_LEN);
+
+       EVP_CIPHER_CTX_init(&ctx);
+       EVP_CIPHER_CTX_set_padding(&ctx, 0);
+       if (!EVP_CipherInit_ex(&ctx, this->cipher, NULL, NULL, NULL, enc) ||
+               !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, NULL) ||
+               !EVP_CipherInit_ex(&ctx, NULL, NULL, this->key.ptr, nonce, enc))
+       {
+               goto done;
+       }
+       if (!enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, this->icv_size,
+                                                                        data.ptr + data.len))
+       {       /* set ICV for verification on decryption */
+               goto done;
+       }
+       if (assoc.len && !EVP_CipherUpdate(&ctx, NULL, &len, assoc.ptr, assoc.len))
+       {       /* set AAD if specified */
+               goto done;
+       }
+       if (!EVP_CipherUpdate(&ctx, out, &len, data.ptr, data.len) ||
+               !EVP_CipherFinal_ex(&ctx, out + len, &len))
+       {       /* EVP_CipherFinal_ex fails if ICV is incorrect on decryption */
+               goto done;
+       }
+       if (enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, this->icv_size,
+                                                                       out + data.len))
+       {       /* copy back the ICV when encrypting */
+               goto done;
+       }
+       success = TRUE;
+
+done:
+       EVP_CIPHER_CTX_cleanup(&ctx);
+       return success;
+}
+
+METHOD(aead_t, encrypt, bool,
+       private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv,
+       chunk_t *encrypted)
+{
+       u_char *out;
+
+       out = plain.ptr;
+       if (encrypted)
+       {
+               *encrypted = chunk_alloc(plain.len + this->icv_size);
+               out = encrypted->ptr;
+       }
+       return crypt(this, plain, assoc, iv, out, 1);
+}
+
+METHOD(aead_t, decrypt, bool,
+       private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv,
+       chunk_t *plain)
+{
+       u_char *out;
+
+       if (encrypted.len < this->icv_size)
+       {
+               return FALSE;
+       }
+       encrypted.len -= this->icv_size;
+
+       out = encrypted.ptr;
+       if (plain)
+       {
+               *plain = chunk_alloc(encrypted.len);
+               out = plain->ptr;
+       }
+       return crypt(this, encrypted, assoc, iv, out, 0);
+}
+
+METHOD(aead_t, get_block_size, size_t,
+       private_aead_t *this)
+{
+       return this->cipher->block_size;
+}
+
+METHOD(aead_t, get_icv_size, size_t,
+       private_aead_t *this)
+{
+       return this->icv_size;
+}
+
+METHOD(aead_t, get_iv_size, size_t,
+       private_aead_t *this)
+{
+       return IV_LEN;
+}
+
+METHOD(aead_t, get_key_size, size_t,
+       private_aead_t *this)
+{
+       return this->key.len + SALT_LEN;
+}
+
+METHOD(aead_t, set_key, bool,
+       private_aead_t *this, chunk_t key)
+{
+       if (key.len != get_key_size(this))
+       {
+               return FALSE;
+       }
+       memcpy(this->salt, key.ptr + key.len - SALT_LEN, SALT_LEN);
+       memcpy(this->key.ptr, key.ptr, this->key.len);
+       return TRUE;
+}
+
+METHOD(aead_t, destroy, void,
+       private_aead_t *this)
+{
+       chunk_clear(&this->key);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size)
+{
+       private_aead_t *this;
+
+       INIT(this,
+               .public = {
+                       .encrypt = _encrypt,
+                       .decrypt = _decrypt,
+                       .get_block_size = _get_block_size,
+                       .get_icv_size = _get_icv_size,
+                       .get_iv_size = _get_iv_size,
+                       .get_key_size = _get_key_size,
+                       .set_key = _set_key,
+                       .destroy = _destroy,
+               },
+       );
+
+       switch (algo)
+       {
+               case ENCR_AES_GCM_ICV8:
+                       this->icv_size = 8;
+                       break;
+               case ENCR_AES_GCM_ICV12:
+                       this->icv_size = 12;
+                       break;
+               case ENCR_AES_GCM_ICV16:
+                       this->icv_size = 16;
+                       break;
+               default:
+                       free(this);
+                       return NULL;
+       }
+
+       switch (algo)
+       {
+               case ENCR_AES_GCM_ICV8:
+               case ENCR_AES_GCM_ICV12:
+               case ENCR_AES_GCM_ICV16:
+                       switch (key_size)
+                       {
+                               case 0:
+                                       key_size = 16;
+                                       /* FALL */
+                               case 16:
+                                       this->cipher = EVP_get_cipherbyname("aes-128-gcm");
+                                       break;
+                               case 24:
+                                       this->cipher = EVP_get_cipherbyname("aes-192-gcm");
+                                       break;
+                               case 32:
+                                       this->cipher = EVP_get_cipherbyname("aes-256-gcm");
+                                       break;
+                               default:
+                                       free(this);
+                                       return NULL;
+                       }
+                       break;
+               default:
+                       free(this);
+                       return NULL;
+       }
+
+       if (!this->cipher)
+       {
+               free(this);
+               return NULL;
+       }
+
+       this->key = chunk_alloc(key_size);
+
+       return &this->public;
+}
diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.h b/src/libstrongswan/plugins/openssl/openssl_gcm.h
new file mode 100644 (file)
index 0000000..12d2e8a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * Implements the aead_t interface using OpenSSL in GCM mode.
+ *
+ * @defgroup openssl_gcm openssl_gcm
+ * @{ @ingroup openssl_p
+ */
+
+#ifndef OPENSSL_GCM_H_
+#define OPENSSL_GCM_H_
+
+#include <crypto/aead.h>
+
+/**
+ * Constructor to create aead_t implementation.
+ *
+ * @param algo                 algorithm to implement
+ * @param key_size             key size in bytes
+ * @return                             aead_t object, NULL if not supported
+ */
+aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size);
+
+#endif /** OPENSSL_GCM_H_ @}*/
index 0f5b071..282fe2b 100644 (file)
@@ -43,6 +43,7 @@
 #include "openssl_pkcs7.h"
 #include "openssl_rng.h"
 #include "openssl_hmac.h"
+#include "openssl_gcm.h"
 
 typedef struct private_openssl_plugin_t private_openssl_plugin_t;
 
@@ -304,6 +305,19 @@ METHOD(plugin_t, get_features, int,
                        PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256),
 #endif
 #endif /* OPENSSL_NO_HMAC */
+#ifndef OPENSSL_NO_AES
+               /* AES GCM */
+               PLUGIN_REGISTER(AEAD, openssl_gcm_create),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 16),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 24),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 32),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 16),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 24),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 32),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 16),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 24),
+                       PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 32),
+#endif /* OPENSSL_NO_AES */
 #ifndef OPENSSL_NO_DH
                /* MODP DH groups */
                PLUGIN_REGISTER(DH, openssl_diffie_hellman_create),