Greetings!!!
We have created an engine in OpenSSL that provides our implementation of
AES. (currently it supports only AES but we will further enhance the
engine). We used the openssl engine to test whether the
encryption/decryption part is working properly. That was working fine. Now
we want the OpenVPN to use this engine for encryption/decryption
The server conf file is as shown below:
dev tun0
ifconfig 11.11.1.1 11.11.1.2
secret /usr/local/openvpn-2.0.2/test-conf/static.key
proto udp
cipher AES-128-CBC
the client conf file is as shown below:
remote 192.168.9.57
dev tun
ifconfig 11.11.1.2 11.11.1.1
secret /usr/local/openvpn-2.0.2/test-conf/static.key
cipher AES-128-CBC
We tried the following commands
server side command
openvpn --config /usr/local/openvpn2.0.2/test-conf/server.conf --engine
AES-CRYPTO
client side command
openvpn --config /usr/local/openvpn2.0.2/test-conf/client.conf --engine
AES-CRYPTO
the output is as shown below...
server side
Thu Nov 10 22:21:38 2005 Initializing OpenSSL support for engine 'AES-CRYPTO'
Thu Nov 10 22:21:38 2005 TUN/TAP device tun0 opened
Thu Nov 10 22:21:38 2005 /sbin/ifconfig tun0 11.11.1.1 pointopoint 11.11.1.2
mtu 1500
Thu Nov 10 22:21:38 2005 UDPv4 link local (bound): [undef]:1194
Thu Nov 10 22:21:38 2005 UDPv4 link remote: [undef]
Thu Nov 10 22:21:54 2005 Authenticate/Decrypt packet error: cipher final failed
Thu Nov 10 22:22:04 2005 Authenticate/Decrypt packet error: cipher final failed
client side
Thu Nov 10 22:26:02 2005 Initializing OpenSSL support for engine 'AES-CRYPTO'
Thu Nov 10 22:26:02 2005 TUN/TAP device tun0 opened
Thu Nov 10 22:26:02 2005 /sbin/ifconfig tun0 11.11.1.2 pointopoint 11.11.1.1
mtu 1500
Thu Nov 10 22:26:02 2005 UDPv4 link local (bound): [undef]:1194
Thu Nov 10 22:26:02 2005 UDPv4 link remote: 10.6.21.63:1194
The engine code is also attached along with the mail. Please let us know
what to do regarding this. We would be very grateful if any one of you
could help us in this matter...
Thanks & Regards
Nisha
#include <openssl/objects.h>
#include <openssl/engine.h>
#include <openssl/evp.h>
#include <include/openssl/obj_mac.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <openssl/aes.h>
#define CRYPTO_AES_128_CBC 1
#define CRYPTO_AES_192_CBC 2
#define CRYPTO_AES_256_CBC 3
#define CRYPTO_ALGORITHM_MAX 4
static int cryptodev_max_iv(int cipher);
static int cryptodev_key_length_valid(int cipher, int len);
static int cipher_nid_to_cryptodev(int nid);
static int get_cryptodev_ciphers(const int **cnids);
static int cryptodev_usable_ciphers(const int **nids);
static int cryptodev_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, unsigned int inl);
static int cryptodev_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
const unsigned char *iv, int enc);
static int cryptodev_cleanup(EVP_CIPHER_CTX *ctx);
static int cryptodev_engine_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
const int **nids, int nid);
void ENGINE_load_cryptodev(void);
AES_KEY *keystruct;
//making the array of cipher parameters. nid is defined here.
static struct {
int id;
int nid;
int ivmax;
int keylen;
} ciphers[] = {
{ CRYPTO_AES_128_CBC, NID_aes_128_cbc, 16, 16, },
{ CRYPTO_AES_192_CBC, NID_aes_192_cbc, 16, 24, },
{ CRYPTO_AES_256_CBC, NID_aes_256_cbc, 16, 32, },
{ 0, NID_undef, 0, 0, },
};
static int
cryptodev_max_iv(int cipher)
{
int i;
for (i = 0; ciphers[i].id; i++)
if (ciphers[i].id == cipher)
return (ciphers[i].ivmax);
return (0);
}
static int
cryptodev_key_length_valid(int cipher, int len)
{
int i;
for (i = 0; ciphers[i].id; i++)
if (ciphers[i].id == cipher)
return (ciphers[i].keylen == len);
return (0);
}
/* convert libcrypto nids to cryptodev */
static int cipher_nid_to_cryptodev(int nid)
{
int i;
for (i = 0; ciphers[i].id; i++)
if (ciphers[i].nid == nid)
return (ciphers[i].id);
return (0);
}
/*
* Find out what ciphers /dev/crypto will let us have a session for.
* XXX note, that some of these openssl doesn't deal with yet!
* returning them here is harmless, as long as we return NULL
* when asked for a handler in the cryptodev_engine_ciphers routine
*/
static int get_cryptodev_ciphers(const int **cnids)
{
static int nids[CRYPTO_ALGORITHM_MAX];
int i, count = 0;
for (i = 0; ciphers[i].id && count < CRYPTO_ALGORITHM_MAX; i++) {
if (ciphers[i].nid == NID_undef)
continue;
nids[count++] = ciphers[i].nid;
}
if (count > 0)
*cnids = nids;
else
*cnids = NULL;
return (count);
}
/*
* Find the useable ciphers|digests from dev/crypto - this is the first
* thing called by the engine init crud which determines what it
* can use for ciphers from this engine. We want to return
* only what we can do, anythine else is handled by software.
*
* If we can't initialize the device to do anything useful for
* any reason, we want to return a NULL array, and 0 length,
* which forces everything to be done in software. By putting
* the initalization of the device in here, we ensure we can
* use this engine as the default, and if for whatever reason
* /dev/crypto won't do what we want it will just be done in
* software
*
* This can (should) be greatly expanded to perhaps take into
* account speed of the device, and what we want to do.
* (although the disabling of particular alg's could be controlled
* by the device driver with sysctl's.) - this is where we
* want most of the decisions made about what we actually want
* to use from /dev/crypto.
*/
static int cryptodev_usable_ciphers(const int **nids)
{
return (get_cryptodev_ciphers(nids));
}
static int cryptodev_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, unsigned int inl)
{
unsigned char *iv_fm_ssl=(unsigned char *)malloc(16 * sizeof(char));
iv_fm_ssl = (unsigned char *)ctx->iv;
AES_cbc_encrypt(in, out, inl, keystruct, iv_fm_ssl, ctx->encrypt);
return (1);
}
static int cryptodev_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
const unsigned char *iv, int enc)
{
keystruct=(AES_KEY *)malloc(sizeof(AES_KEY));
int cipher;
if ((cipher = cipher_nid_to_cryptodev(ctx->cipher->nid)) == NID_undef)
return (0);
if (ctx->cipher->iv_len > cryptodev_max_iv(cipher))
return (0);
if (!cryptodev_key_length_valid(cipher, ctx->key_len))
return (0);
if ((ctx->cipher->flags & EVP_CIPH_MODE) == EVP_CIPH_CFB_MODE
|| (ctx->cipher->flags & EVP_CIPH_MODE) == EVP_CIPH_OFB_MODE
|| enc)
AES_set_encrypt_key(key, ctx->key_len * 8, keystruct);
else
AES_set_decrypt_key(key, ctx->key_len * 8, keystruct);
return 1;
}
/*
* free anything we allocated earlier when initting a
* session, and close the session.
*/
static int cryptodev_cleanup(EVP_CIPHER_CTX *ctx)
{
return (1);
}
//this one is the struct that says which method to call when init, encrypt and
cleanup.
/*IMPLEMENT_BLOCK_CIPHER(aes_128, ks, AES, EVP_AES_KEY,
NID_aes_128, 16, 16, 16, 128,
0, cryptodev_init_key, cryptodev_cleanup,
EVP_CIPHER_set_asn1_iv,
EVP_CIPHER_get_asn1_iv,
NULL)
IMPLEMENT_BLOCK_CIPHER(aes_192, ks, AES, EVP_AES_KEY,
NID_aes_192, 16, 24, 16, 128,
0, cryptodev_init_key, cryptodev_cleanup,
EVP_CIPHER_set_asn1_iv,
EVP_CIPHER_get_asn1_iv,
NULL)
IMPLEMENT_BLOCK_CIPHER(aes_256, ks, AES, EVP_AES_KEY,
NID_aes_256, 16, 32, 16, 128,
0, cryptodev_init_key, cryptodev_cleanup,
EVP_CIPHER_set_asn1_iv,
EVP_CIPHER_get_asn1_iv,
NULL)
*/
const EVP_CIPHER cryptodev_aes_128_cbc = {
NID_aes_128_cbc,
16, 16, 16,
EVP_CIPH_CBC_MODE,
cryptodev_init_key,
cryptodev_cipher,
cryptodev_cleanup,
sizeof(AES_KEY),
EVP_CIPHER_set_asn1_iv,
EVP_CIPHER_get_asn1_iv,
NULL,
NULL
};
const EVP_CIPHER cryptodev_aes_192_cbc = {
NID_aes_192_cbc,
16, 24, 16,
EVP_CIPH_CBC_MODE,
cryptodev_init_key,
cryptodev_cipher,
cryptodev_cleanup,
sizeof(AES_KEY),
EVP_CIPHER_set_asn1_iv,
EVP_CIPHER_get_asn1_iv,
NULL,
NULL
};
const EVP_CIPHER cryptodev_aes_256_cbc = {
NID_aes_256_cbc,
16, 32, 16,
EVP_CIPH_CBC_MODE,
cryptodev_init_key,
cryptodev_cipher,
cryptodev_cleanup,
sizeof(AES_KEY),
EVP_CIPHER_set_asn1_iv,
EVP_CIPHER_get_asn1_iv,
NULL,
NULL
};
/*
* Registered by the ENGINE when used to find out how to deal with
* a particular NID in the ENGINE. this says what we'll do at the
* top level - note, that list is restricted by what we answer with
*/
static int
cryptodev_engine_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
const int **nids, int nid)
{
if (!cipher)
return (cryptodev_usable_ciphers(nids));
switch (nid) {
case NID_aes_128_cbc:
*cipher = &cryptodev_aes_128_cbc;
break;
case NID_aes_192_cbc:
*cipher = &cryptodev_aes_192_cbc;
break;
case NID_aes_256_cbc:
*cipher = &cryptodev_aes_256_cbc;
break;
default:
*cipher = NULL;
break;
}
return (*cipher != NULL);
}
static const char *engine_crypto_id = "AES-CRYPTO";
static const char *engine_crypto_name = "Engine for AES software
implementation";
static ENGINE *engine_crypto(void)
{
ENGINE *ret = ENGINE_new();
if(!ret)
return NULL;
if(!ENGINE_set_id(ret, engine_crypto_id) || !ENGINE_set_name(ret,
engine_crypto_name) ||!ENGINE_set_ciphers(ret,cryptodev_engine_ciphers))
{
ENGINE_free(ret);
return NULL;
}
return ret;
}
void ENGINE_load_cryptodev(void)
{
ENGINE *engine = engine_crypto();
ENGINE_add(engine);
ENGINE_set_default(engine, ENGINE_METHOD_CIPHERS);
ENGINE_register_ciphers(engine);
ENGINE_free(engine);
ERR_clear_error();
}