openssl: Generalize the GCM implementation a bit
authorTobias Brunner <tobias@strongswan.org>
Mon, 4 Mar 2019 16:31:28 +0000 (17:31 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 8 Mar 2019 14:55:52 +0000 (15:55 +0100)
This will allow us to use the implementation also for other algorithms.

src/libstrongswan/plugins/openssl/Makefile.am
src/libstrongswan/plugins/openssl/openssl_aead.c [new file with mode: 0644]
src/libstrongswan/plugins/openssl/openssl_aead.h [new file with mode: 0644]
src/libstrongswan/plugins/openssl/openssl_gcm.c [deleted file]
src/libstrongswan/plugins/openssl/openssl_gcm.h [deleted file]
src/libstrongswan/plugins/openssl/openssl_plugin.c

index d484092..7b83890 100644 (file)
@@ -29,7 +29,7 @@ libstrongswan_openssl_la_SOURCES = \
        openssl_pkcs12.c openssl_pkcs12.h \
        openssl_rng.c openssl_rng.h \
        openssl_hmac.c openssl_hmac.h \
-       openssl_gcm.c openssl_gcm.h \
+       openssl_aead.c openssl_aead.h \
        openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h \
        openssl_ed_private_key.c openssl_ed_private_key.h \
        openssl_ed_public_key.c openssl_ed_public_key.h
diff --git a/src/libstrongswan/plugins/openssl/openssl_aead.c b/src/libstrongswan/plugins/openssl/openssl_aead.c
new file mode 100644 (file)
index 0000000..1d5b8fc
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2013-2019 Tobias Brunner
+ * HSR 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/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL
+
+#include "openssl_aead.h"
+
+#include <openssl/evp.h>
+#include <crypto/iv/iv_gen_seq.h>
+
+/* the generic AEAD identifiers were added with 1.1.0 */
+#ifndef EVP_CTRL_AEAD_SET_IVLEN
+#define EVP_CTRL_AEAD_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN
+#define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG
+#define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG
+#endif
+
+/** 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;
+
+       /**
+        * IV generator
+        */
+       iv_gen_t *iv_gen;
+
+       /**
+        * 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);
+
+       ctx = EVP_CIPHER_CTX_new();
+       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_AEAD_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_AEAD_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_AEAD_GET_TAG, this->icv_size,
+                                                                       out + data.len))
+       {       /* copy back the ICV when encrypting */
+               goto done;
+       }
+       success = TRUE;
+
+done:
+       EVP_CIPHER_CTX_free(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 EVP_CIPHER_block_size(this->cipher);
+}
+
+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_iv_gen, iv_gen_t*,
+       private_aead_t *this)
+{
+       return this->iv_gen;
+}
+
+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);
+       this->iv_gen->destroy(this->iv_gen);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+aead_t *openssl_aead_create(encryption_algorithm_t algo,
+                                                       size_t key_size, size_t salt_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_iv_gen = _get_iv_gen,
+                       .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;
+       }
+
+       if (salt_size && salt_size != SALT_LEN)
+       {
+               /* currently not supported */
+               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_aes_128_gcm();
+                                       break;
+                               case 24:
+                                       this->cipher = EVP_aes_192_gcm();
+                                       break;
+                               case 32:
+                                       this->cipher = EVP_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);
+       this->iv_gen = iv_gen_seq_create();
+
+       return &this->public;
+}
+
+#endif /* OPENSSL_VERSION_NUMBER */
diff --git a/src/libstrongswan/plugins/openssl/openssl_aead.h b/src/libstrongswan/plugins/openssl/openssl_aead.h
new file mode 100644 (file)
index 0000000..b820ed2
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013-2019 Tobias Brunner
+ * HSR 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.
+ *
+ * @defgroup openssl_aead openssl_aead
+ * @{ @ingroup openssl_p
+ */
+
+#ifndef OPENSSL_AEAD_H_
+#define OPENSSL_AEAD_H_
+
+#include <crypto/aead.h>
+
+/**
+ * Constructor to create aead_t implementation.
+ *
+ * @param algo                 algorithm to implement
+ * @param key_size             key size in bytes
+ * @param salt_size            size of implicit salt length
+ * @return                             aead_t object, NULL if not supported
+ */
+aead_t *openssl_aead_create(encryption_algorithm_t algo, size_t key_size,
+                                                       size_t salt_size);
+
+#endif /** OPENSSL_AEAD_H_ @}*/
diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.c b/src/libstrongswan/plugins/openssl/openssl_gcm.c
deleted file mode 100644 (file)
index 4b096f0..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2013 Tobias Brunner
- * HSR 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/opensslv.h>
-
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL
-
-#include "openssl_gcm.h"
-
-#include <openssl/evp.h>
-#include <crypto/iv/iv_gen_seq.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;
-
-       /**
-        * IV generator
-        */
-       iv_gen_t *iv_gen;
-
-       /**
-        * 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);
-
-       ctx = EVP_CIPHER_CTX_new();
-       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_free(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 EVP_CIPHER_block_size(this->cipher);
-}
-
-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_iv_gen, iv_gen_t*,
-       private_aead_t *this)
-{
-       return this->iv_gen;
-}
-
-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);
-       this->iv_gen->destroy(this->iv_gen);
-       free(this);
-}
-
-/*
- * Described in header
- */
-aead_t *openssl_gcm_create(encryption_algorithm_t algo,
-                                                  size_t key_size, size_t salt_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_iv_gen = _get_iv_gen,
-                       .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;
-       }
-
-       if (salt_size && salt_size != SALT_LEN)
-       {
-               /* currently not supported */
-               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_aes_128_gcm();
-                                       break;
-                               case 24:
-                                       this->cipher = EVP_aes_192_gcm();
-                                       break;
-                               case 32:
-                                       this->cipher = EVP_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);
-       this->iv_gen = iv_gen_seq_create();
-
-       return &this->public;
-}
-
-#endif /* OPENSSL_VERSION_NUMBER */
diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.h b/src/libstrongswan/plugins/openssl/openssl_gcm.h
deleted file mode 100644 (file)
index a64c901..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 Tobias Brunner
- * HSR 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
- * @param salt_size            size of implicit salt length
- * @return                             aead_t object, NULL if not supported
- */
-aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size,
-                                                       size_t salt_size);
-
-#endif /** OPENSSL_GCM_H_ @}*/
index cbeb6c3..0661fdb 100644 (file)
@@ -46,7 +46,7 @@
 #include "openssl_pkcs12.h"
 #include "openssl_rng.h"
 #include "openssl_hmac.h"
-#include "openssl_gcm.h"
+#include "openssl_aead.h"
 #include "openssl_x_diffie_hellman.h"
 #include "openssl_ed_public_key.h"
 #include "openssl_ed_private_key.h"
@@ -583,7 +583,7 @@ METHOD(plugin_t, get_features, int,
 #if OPENSSL_VERSION_NUMBER >= 0x1000100fL
 #ifndef OPENSSL_NO_AES
                /* AES GCM */
-               PLUGIN_REGISTER(AEAD, openssl_gcm_create),
+               PLUGIN_REGISTER(AEAD, openssl_aead_create),
                        PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 16),
                        PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 24),
                        PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 32),