From: Martin Willi Date: Wed, 25 Mar 2015 16:30:58 +0000 (+0100) Subject: aesni: Implement a AES-NI based CBC crypter using the key schedule X-Git-Tag: 5.3.1dr1~17^2~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8f1fab93e93ca315c4aaa0fb8b5940da84115c56;p=thirdparty%2Fstrongswan.git aesni: Implement a AES-NI based CBC crypter using the key schedule --- diff --git a/src/libstrongswan/plugins/aesni/Makefile.am b/src/libstrongswan/plugins/aesni/Makefile.am index 817afa40ae..c7c8e6942d 100644 --- a/src/libstrongswan/plugins/aesni/Makefile.am +++ b/src/libstrongswan/plugins/aesni/Makefile.am @@ -13,6 +13,7 @@ endif libstrongswan_aesni_la_SOURCES = \ aesni_key.h aesni_key.c \ + aesni_cbc.h aesni_cbc.c \ aesni_plugin.h aesni_plugin.c libstrongswan_aesni_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/aesni/aesni_cbc.c b/src/libstrongswan/plugins/aesni/aesni_cbc.c new file mode 100644 index 0000000000..ca35d2737e --- /dev/null +++ b/src/libstrongswan/plugins/aesni/aesni_cbc.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2015 Martin Willi + * Copyright (C) 2015 revosec AG + * + * 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 . + * + * 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 "aesni_cbc.h" +#include "aesni_key.h" + +typedef struct private_aesni_cbc_t private_aesni_cbc_t; + +/** + * CBC en/decryption method type + */ +typedef void (*aesni_cbc_fn_t)(aesni_key_t*, u_int, u_char*, u_char*, u_char*); + +/** + * Private data of an aesni_cbc_t object. + */ +struct private_aesni_cbc_t { + + /** + * Public aesni_cbc_t interface. + */ + aesni_cbc_t public; + + /** + * Key size + */ + u_int key_size; + + /** + * Encryption key schedule + */ + aesni_key_t *ekey; + + /** + * Decryption key schedule + */ + aesni_key_t *dkey; + + /** + * Encryption method + */ + aesni_cbc_fn_t encrypt; + + /** + * Decryption method + */ + aesni_cbc_fn_t decrypt; +}; + +/** + * Generic CBC encryption + */ +static void encrypt_cbc(aesni_key_t *key, u_int blocks, u_char *in, + u_char *iv, u_char *out) +{ + __m128i t, fb, *bi, *bo; + int i, round; + + bi = (__m128i*)in; + bo = (__m128i*)out; + + fb = _mm_loadu_si128((__m128i*)iv); + for (i = 0; i < blocks; i++) + { + t = _mm_loadu_si128(bi + i); + fb = _mm_xor_si128(t, fb); + fb = _mm_xor_si128(fb, key->schedule[0]); + for (round = 1; round < key->rounds; round++) + { + fb = _mm_aesenc_si128(fb, key->schedule[round]); + } + fb = _mm_aesenclast_si128(fb, key->schedule[key->rounds]); + _mm_storeu_si128(bo + i, fb); + } +} + +/** + * Generic CBC decryption + */ +static void decrypt_cbc(aesni_key_t *key, u_int blocks, u_char *in, + u_char *iv, u_char *out) +{ + __m128i t, fb, last, *bi, *bo; + int i, round; + + bi = (__m128i*)in; + bo = (__m128i*)out; + + fb = _mm_loadu_si128((__m128i*)iv); + for (i = 0; i < blocks; i++) + { + last = _mm_loadu_si128(bi + i); + t = _mm_xor_si128(last, key->schedule[0]); + for (round = 1; round < key->rounds; round++) + { + t = _mm_aesdec_si128(t, key->schedule[round]); + } + t = _mm_aesdeclast_si128(t, key->schedule[key->rounds]); + t = _mm_xor_si128(t, fb); + _mm_storeu_si128(bo + i, t); + fb = last; + } +} + +/** + * Do inline or allocated de/encryption using key schedule + */ +static bool crypt(aesni_cbc_fn_t fn, aesni_key_t *key, + chunk_t data, chunk_t iv, chunk_t *out) +{ + u_char *buf; + + if (!key || iv.len != AES_BLOCK_SIZE || data.len % AES_BLOCK_SIZE) + { + return FALSE; + } + if (out) + { + *out = chunk_alloc(data.len); + buf = out->ptr; + } + else + { + buf = data.ptr; + } + fn(key, data.len / AES_BLOCK_SIZE, data.ptr, iv.ptr, buf); + return TRUE; +} + +METHOD(crypter_t, encrypt, bool, + private_aesni_cbc_t *this, chunk_t data, chunk_t iv, chunk_t *encrypted) +{ + return crypt(this->encrypt, this->ekey, data, iv, encrypted); +} + +METHOD(crypter_t, decrypt, bool, + private_aesni_cbc_t *this, chunk_t data, chunk_t iv, chunk_t *decrypted) +{ + return crypt(this->decrypt, this->dkey, data, iv, decrypted); +} + +METHOD(crypter_t, get_block_size, size_t, + private_aesni_cbc_t *this) +{ + return AES_BLOCK_SIZE; +} + +METHOD(crypter_t, get_iv_size, size_t, + private_aesni_cbc_t *this) +{ + return AES_BLOCK_SIZE; +} + +METHOD(crypter_t, get_key_size, size_t, + private_aesni_cbc_t *this) +{ + return this->key_size; +} + +METHOD(crypter_t, set_key, bool, + private_aesni_cbc_t *this, chunk_t key) +{ + if (key.len != this->key_size) + { + return FALSE; + } + + DESTROY_IF(this->ekey); + DESTROY_IF(this->dkey); + + this->ekey = aesni_key_create(TRUE, key); + this->dkey = aesni_key_create(FALSE, key); + + return this->ekey && this->dkey; +} + +METHOD(crypter_t, destroy, void, + private_aesni_cbc_t *this) +{ + DESTROY_IF(this->ekey); + DESTROY_IF(this->dkey); + free(this); +} + +/** + * See header + */ +aesni_cbc_t *aesni_cbc_create(encryption_algorithm_t algo, size_t key_size) +{ + private_aesni_cbc_t *this; + + if (algo != ENCR_AES_CBC) + { + return NULL; + } + switch (key_size) + { + case 0: + key_size = 16; + break; + case 16: + case 24: + case 32: + break; + default: + return NULL; + } + + 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, + }, + }, + .key_size = key_size, + .encrypt = encrypt_cbc, + .decrypt = decrypt_cbc, + ); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/aesni/aesni_cbc.h b/src/libstrongswan/plugins/aesni/aesni_cbc.h new file mode 100644 index 0000000000..c004ec611d --- /dev/null +++ b/src/libstrongswan/plugins/aesni/aesni_cbc.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Martin Willi + * Copyright (C) 2015 revosec AG + * + * 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 . + * + * 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. + */ + +/** + * @defgroup aesni_cbc aesni_cbc + * @{ @ingroup aesni + */ + +#ifndef AESNI_CBC_H_ +#define AESNI_CBC_H_ + +#include + +typedef struct aesni_cbc_t aesni_cbc_t; + +/** + * CBC mode crypter using AES-NI + */ +struct aesni_cbc_t { + + /** + * Implements crypter interface + */ + crypter_t crypter; +}; + +/** + * Create a aesni_cbc instance. + * + * @param algo encryption algorithm, AES_ENCR_CBC + * @param key_size AES key size, in bytes + * @return AES-CBC crypter, NULL if not supported + */ +aesni_cbc_t *aesni_cbc_create(encryption_algorithm_t algo, size_t key_size); + +#endif /** AESNI_CBC_H_ @}*/ diff --git a/src/libstrongswan/plugins/aesni/aesni_plugin.c b/src/libstrongswan/plugins/aesni/aesni_plugin.c index 2e3ffaf5be..74645987df 100644 --- a/src/libstrongswan/plugins/aesni/aesni_plugin.c +++ b/src/libstrongswan/plugins/aesni/aesni_plugin.c @@ -14,6 +14,7 @@ */ #include "aesni_plugin.h" +#include "aesni_cbc.h" #include @@ -45,6 +46,10 @@ METHOD(plugin_t, get_features, int, private_aesni_plugin_t *this, plugin_feature_t *features[]) { static plugin_feature_t f[] = { + PLUGIN_REGISTER(CRYPTER, aesni_cbc_create), + PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 16), + PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 24), + PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 32), }; *features = f;