openssl: Update crypter API to OpenSSL 1.1.0
[strongswan.git] / src / libstrongswan / plugins / openssl / openssl_crypter.c
index 06a24b3..b9085f9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2008 Tobias Brunner
- * Hochschule fuer Technik Rapperswil 
+ * 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
@@ -11,8 +11,6 @@
  * 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.
- *
- * $Id$
  */
 
 #include "openssl_crypter.h"
@@ -25,17 +23,17 @@ typedef struct private_openssl_crypter_t private_openssl_crypter_t;
  * Private data of openssl_crypter_t
  */
 struct private_openssl_crypter_t {
-       
+
        /**
         * Public part of this class.
         */
        openssl_crypter_t public;
-       
+
        /*
         * the key
         */
        chunk_t key;
-       
+
        /*
         * the cipher to use
         */
@@ -43,182 +41,207 @@ struct private_openssl_crypter_t {
 };
 
 /**
- * Mapping from the algorithms defined in IKEv2 to
- * OpenSSL algorithm names and their key length
- */
-typedef struct {
-       /**
-        * Identifier specified in IKEv2
-        */
-       int ikev2_id;
-       
-       /**
-        * Name of the algorithm, as used in OpenSSL
-        */
-       char *name;
-       
-       /**
-        * Minimum valid key length in bytes
-        */
-       size_t key_size_min;
-       
-       /**
-        * Maximum valid key length in bytes
-        */
-       size_t key_size_max;
-} openssl_algorithm_t;
-
-#define END_OF_LIST -1
-
-/**
- * Algorithms for encryption
- */
-static openssl_algorithm_t encryption_algs[] = {
-/*     {ENCR_DES_IV64,         "***",                  0,      0}, */
-       {ENCR_DES,                      "des",                  8,      8},             /* 64 bits */
-       {ENCR_3DES,             "des3",                 24,     24},    /* 192 bits */
-       {ENCR_RC5,                      "rc5",                  5,      255},   /* 40 to 2040 bits, RFC 2451 */
-       {ENCR_IDEA,             "idea",                 16,     16},    /* 128 bits, RFC 2451 */
-       {ENCR_CAST,             "cast",                 5,      16},    /* 40 to 128 bits, RFC 2451 */
-       {ENCR_BLOWFISH,         "blowfish",             5,      56},    /* 40 to 448 bits, RFC 2451 */
-/*     {ENCR_3IDEA,            "***",                  0,      0}, */
-/*     {ENCR_DES_IV32,         "***",                  0,      0}, */
-/*     {ENCR_NULL,             "***",                  0,      0}, */ /* handled separately */
-/*     {ENCR_AES_CBC,          "***",                  0,      0}, */ /* handled separately */
-/*     {ENCR_AES_CTR,          "***",                  0,      0}, */ /* disabled in evp.h */
-       {END_OF_LIST,           NULL,                   0,      0},
-};
-
-/**
  * Look up an OpenSSL algorithm name and validate its key size
  */
-static char* lookup_algorithm(openssl_algorithm_t *openssl_algo, 
-                                          u_int16_t ikev2_algo, size_t key_size)
+static char* lookup_algorithm(uint16_t ikev2_algo, size_t *key_size)
 {
-       while (openssl_algo->ikev2_id != END_OF_LIST)
+       struct {
+               /* identifier specified in IKEv2 */
+               int ikev2_id;
+               /* name of the algorithm, as used in OpenSSL */
+               char *name;
+               /* default key size in bytes */
+               size_t key_def;
+               /* minimum key size */
+               size_t key_min;
+               /* maximum key size */
+               size_t key_max;
+       } mappings[] = {
+               {ENCR_DES,                      "des-cbc",               8,              8,               8},
+               {ENCR_3DES,             "des-ede3-cbc", 24,             24,              24},
+               {ENCR_RC5,                      "rc5-cbc",              16,              5,             255},
+               {ENCR_IDEA,             "idea-cbc",             16,             16,              16},
+               {ENCR_CAST,             "cast5-cbc",    16,              5,              16},
+               {ENCR_BLOWFISH,         "bf-cbc",               16,              5,              56},
+       };
+       int i;
+
+       for (i = 0; i < countof(mappings); i++)
        {
-               if (ikev2_algo == openssl_algo->ikev2_id)
+               if (ikev2_algo == mappings[i].ikev2_id)
                {
+                       /* set the key size if it is not set */
+                       if (*key_size == 0)
+                       {
+                               *key_size = mappings[i].key_def;
+                       }
                        /* validate key size */
-                       if (key_size < openssl_algo->key_size_min ||
-                               key_size > openssl_algo->key_size_max)
+                       if (*key_size < mappings[i].key_min ||
+                               *key_size > mappings[i].key_max)
                        {
                                return NULL;
                        }
-                       return openssl_algo->name;
+                       return mappings[i].name;
                }
-               openssl_algo++;
        }
        return NULL;
 }
 
-static void crypt(private_openssl_crypter_t *this, chunk_t data,
-                                       chunk_t iv, chunk_t *dst, int enc)
+/**
+ * Do the actual en/decryption in an EVP context
+ */
+static bool crypt(private_openssl_crypter_t *this, chunk_t data, chunk_t iv,
+                                 chunk_t *dst, int enc)
 {
-       int len, finlen;
-       unsigned char buf[data.len + this->cipher->block_size];
-       EVP_CIPHER_CTX ctx;
-       EVP_CIPHER_CTX_init(&ctx);
-       EVP_CipherInit_ex(&ctx, this->cipher, NULL, this->key.ptr, iv.ptr, enc);
-       EVP_CIPHER_CTX_set_padding(&ctx, 0); /* disable padding */
-       EVP_CipherUpdate(&ctx, buf, &len, data.ptr, data.len);
-       EVP_CipherFinal_ex(&ctx, buf + len, &finlen);
-       len += finlen;
-       *dst = chunk_alloc(len);
-       memcpy(dst->ptr, buf, len);
-       EVP_CIPHER_CTX_cleanup(&ctx);
-       /* TODO: because we don't use padding, we can simplify this a bit because
-        * EVP_CryptFinal_ex does not do anything but checking if any data is left.
-        * so we can work without buffer and fill the dst directly */
+       EVP_CIPHER_CTX *ctx;
+       int len;
+       u_char *out;
+       bool success = FALSE;
+
+       out = data.ptr;
+       if (dst)
+       {
+               *dst = chunk_alloc(data.len);
+               out = dst->ptr;
+       }
+       ctx = EVP_CIPHER_CTX_new();
+       if (EVP_CipherInit_ex(ctx, this->cipher, NULL, NULL, NULL, enc) &&
+               EVP_CIPHER_CTX_set_padding(ctx, 0) /* disable padding */ &&
+               EVP_CIPHER_CTX_set_key_length(ctx, this->key.len) &&
+               EVP_CipherInit_ex(ctx, NULL, NULL, this->key.ptr, iv.ptr, enc) &&
+               EVP_CipherUpdate(ctx, out, &len, data.ptr, data.len) &&
+               /* since padding is disabled this does nothing */
+               EVP_CipherFinal_ex(ctx, out + len, &len))
+       {
+               success = TRUE;
+       }
+       EVP_CIPHER_CTX_free(ctx);
+       return success;
 }
 
-/**
- * Implementation of crypter_t.decrypt.
- */
-static void decrypt(private_openssl_crypter_t *this, chunk_t data, 
-                                               chunk_t iv, chunk_t *dst)
+METHOD(crypter_t, decrypt, bool,
+       private_openssl_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst)
 {
-       crypt(this, data, iv, dst, 0);
+       return crypt(this, data, iv, dst, 0);
 }
 
+METHOD(crypter_t, encrypt, bool,
+       private_openssl_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst)
+{
+       return crypt(this, data, iv, dst, 1);
+}
 
-/**
- * Implementation of crypter_t.encrypt.
- */
-static void encrypt (private_openssl_crypter_t *this, chunk_t data, 
-                                                       chunk_t iv, chunk_t *dst)
+METHOD(crypter_t, get_block_size, size_t,
+       private_openssl_crypter_t *this)
 {
-       crypt(this, data, iv, dst, 1);
+       return EVP_CIPHER_block_size(this->cipher);
 }
 
-/**
- * Implementation of crypter_t.get_block_size.
- */
-static size_t get_block_size(private_openssl_crypter_t *this)
+METHOD(crypter_t, get_iv_size, size_t,
+       private_openssl_crypter_t *this)
 {
-       return this->cipher->block_size;
+       return EVP_CIPHER_iv_length(this->cipher);
 }
 
-/**
- * Implementation of crypter_t.get_key_size.
- */
-static size_t get_key_size(private_openssl_crypter_t *this)
+METHOD(crypter_t, get_key_size, size_t,
+       private_openssl_crypter_t *this)
 {
        return this->key.len;
 }
 
-/**
- * Implementation of crypter_t.set_key.
- */
-static void set_key(private_openssl_crypter_t *this, chunk_t key)
+METHOD(crypter_t, set_key, bool,
+       private_openssl_crypter_t *this, chunk_t key)
 {
        memcpy(this->key.ptr, key.ptr, min(key.len, this->key.len));
+       return TRUE;
 }
 
-/**
- * Implementation of crypter_t.destroy.
- */
-static void destroy (private_openssl_crypter_t *this)
+METHOD(crypter_t, destroy, void,
+       private_openssl_crypter_t *this)
 {
-       free(this->key.ptr);
+       chunk_clear(&this->key);
        free(this);
 }
 
 /*
  * Described in header
  */
-openssl_crypter_t *openssl_crypter_create(encryption_algorithm_t algo, 
+openssl_crypter_t *openssl_crypter_create(encryption_algorithm_t algo,
                                                                                                  size_t key_size)
 {
        private_openssl_crypter_t *this;
-       
-       this = malloc_thing(private_openssl_crypter_t);
-       
+
+       INIT(this,
+               .public = {
+                       .crypter = {
+                               .encrypt = _encrypt,
+                               .decrypt = _decrypt,
+                               .get_block_size = _get_block_size,
+                               .get_iv_size = _get_iv_size,
+                               .get_key_size = _get_key_size,
+                               .set_key = _set_key,
+                               .destroy = _destroy,
+                       },
+               },
+       );
+
        switch (algo)
        {
                case ENCR_NULL:
                        this->cipher = EVP_enc_null();
+                       key_size = 0;
                        break;
                case ENCR_AES_CBC:
                        switch (key_size)
                        {
+                               case 0:
+                                       key_size = 16;
+                                       /* FALL */
                                case 16:        /* AES 128 */
-                                       this->cipher = EVP_get_cipherbyname("aes128");
+                                       this->cipher = EVP_get_cipherbyname("aes-128-cbc");
                                        break;
                                case 24:        /* AES-192 */
-                                       this->cipher = EVP_get_cipherbyname("aes192");
+                                       this->cipher = EVP_get_cipherbyname("aes-192-cbc");
                                        break;
                                case 32:        /* AES-256 */
-                                       this->cipher = EVP_get_cipherbyname("aes256"); 
+                                       this->cipher = EVP_get_cipherbyname("aes-256-cbc");
                                        break;
                                default:
                                        free(this);
                                        return NULL;
                        }
                        break;
+               case ENCR_CAMELLIA_CBC:
+                       switch (key_size)
+                       {
+                               case 0:
+                                       key_size = 16;
+                                       /* FALL */
+                               case 16:        /* CAMELLIA 128 */
+                                       this->cipher = EVP_get_cipherbyname("camellia-128-cbc");
+                                       break;
+                               case 24:        /* CAMELLIA 192 */
+                                       this->cipher = EVP_get_cipherbyname("camellia-192-cbc");
+                                       break;
+                               case 32:        /* CAMELLIA 256 */
+                                       this->cipher = EVP_get_cipherbyname("camellia-256-cbc");
+                                       break;
+                               default:
+                                       free(this);
+                                       return NULL;
+                       }
+                       break;
+#ifndef OPENSSL_NO_DES
+               case ENCR_DES_ECB:
+                       key_size = 8;
+                       this->cipher = EVP_des_ecb();
+                       break;
+#endif
                default:
                {
-                       char* name = lookup_algorithm(encryption_algs, algo, key_size);
+                       char* name;
+
+                       name = lookup_algorithm(algo, &key_size);
                        if (!name)
                        {
                                /* algo unavailable or key_size invalid */
@@ -229,22 +252,15 @@ openssl_crypter_t *openssl_crypter_create(encryption_algorithm_t algo,
                        break;
                }
        }
-       
+
        if (!this->cipher)
        {
                /* OpenSSL does not support the requested algo */
                free(this);
                return NULL;
        }
-       
+
        this->key = chunk_alloc(key_size);
-       
-       this->public.crypter_interface.encrypt = (void (*) (crypter_t *, chunk_t,chunk_t, chunk_t *)) encrypt;
-       this->public.crypter_interface.decrypt = (void (*) (crypter_t *, chunk_t , chunk_t, chunk_t *)) decrypt;
-       this->public.crypter_interface.get_block_size = (size_t (*) (crypter_t *)) get_block_size;
-       this->public.crypter_interface.get_key_size = (size_t (*) (crypter_t *)) get_key_size;
-       this->public.crypter_interface.set_key = (void (*) (crypter_t *,chunk_t)) set_key;
-       this->public.crypter_interface.destroy = (void (*) (crypter_t *)) destroy;
-       
+
        return &this->public;
 }