diff options
Diffstat (limited to 'src/libstrongswan/plugins/gcm')
-rw-r--r-- | src/libstrongswan/plugins/gcm/Makefile.am | 16 | ||||
-rw-r--r-- | src/libstrongswan/plugins/gcm/gcm_aead.c | 403 | ||||
-rw-r--r-- | src/libstrongswan/plugins/gcm/gcm_aead.h | 51 | ||||
-rw-r--r-- | src/libstrongswan/plugins/gcm/gcm_plugin.c | 63 | ||||
-rw-r--r-- | src/libstrongswan/plugins/gcm/gcm_plugin.h | 42 |
5 files changed, 575 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/gcm/Makefile.am b/src/libstrongswan/plugins/gcm/Makefile.am new file mode 100644 index 000000000..ec733fbcc --- /dev/null +++ b/src/libstrongswan/plugins/gcm/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-gcm.la +else +plugin_LTLIBRARIES = libstrongswan-gcm.la +endif + +libstrongswan_gcm_la_SOURCES = \ + gcm_plugin.h gcm_plugin.c \ + gcm_aead.h gcm_aead.c + +libstrongswan_gcm_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/gcm/gcm_aead.c b/src/libstrongswan/plugins/gcm/gcm_aead.c new file mode 100644 index 000000000..7c0a2ba23 --- /dev/null +++ b/src/libstrongswan/plugins/gcm/gcm_aead.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 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 <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 "gcm_aead.h" + +#include <debug.h> + +#define BLOCK_SIZE 16 +#define NONCE_SIZE 12 +#define IV_SIZE 8 +#define SALT_SIZE (NONCE_SIZE - IV_SIZE) + +typedef struct private_gcm_aead_t private_gcm_aead_t; + +/** + * Private data of an gcm_aead_t object. + */ +struct private_gcm_aead_t { + + /** + * Public gcm_aead_t interface. + */ + gcm_aead_t public; + + /** + * Underlying CBC crypter. + */ + crypter_t *crypter; + + /** + * Size of the integrity check value + */ + size_t icv_size; + + /** + * Salt value + */ + char salt[SALT_SIZE]; + + /** + * GHASH subkey H + */ + char h[BLOCK_SIZE]; +}; + +/** + * Bitshift a block right by one bit + */ +static void sr_block(u_char *block) +{ + int i; + + for (i = BLOCK_SIZE - 1; i >= 0; i--) + { + block[i] = block[i] >> 1; + if (i != 0) + { + block[i] |= block[i - 1] << 7; + } + } +} + +/** + * Naive implementation of block multiplication in GF128, no tables + */ +static void mult_block(char *x, char *y, char *res) +{ + char z[BLOCK_SIZE], v[BLOCK_SIZE], r; + int bit, byte; + + r = 0xE1; + memset(z, 0, BLOCK_SIZE); + memcpy(v, y, BLOCK_SIZE); + + for (byte = 0; byte < BLOCK_SIZE; byte++) + { + for (bit = 7; bit >= 0; bit--) + { + if (x[byte] & (1 << bit)) + { + memxor(z, v, BLOCK_SIZE); + } + if (v[BLOCK_SIZE - 1] & 0x01) + { + sr_block(v); + v[0] ^= r; + } + else + { + sr_block(v); + } + } + } + memcpy(res, z, BLOCK_SIZE); +} + +/** + * GHASH function + */ +static void ghash(private_gcm_aead_t *this, chunk_t x, char *res) +{ + char y[BLOCK_SIZE]; + + memset(y, 0, BLOCK_SIZE); + + while (x.len) + { + memxor(y, x.ptr, BLOCK_SIZE); + mult_block(y, this->h, y); + x = chunk_skip(x, BLOCK_SIZE); + } + memcpy(res, y, BLOCK_SIZE); +} + +/** + * GCTR function, en-/decrypts x inline + */ +static void gctr(private_gcm_aead_t *this, char *icb, chunk_t x) +{ + char cb[BLOCK_SIZE], iv[BLOCK_SIZE], tmp[BLOCK_SIZE]; + + memset(iv, 0, BLOCK_SIZE); + memcpy(cb, icb, BLOCK_SIZE); + + while (x.len) + { + memcpy(tmp, cb, BLOCK_SIZE); + this->crypter->encrypt(this->crypter, chunk_from_thing(tmp), + chunk_from_thing(iv), NULL); + memxor(x.ptr, tmp, min(BLOCK_SIZE, x.len)); + chunk_increment(chunk_from_thing(cb)); + x = chunk_skip(x, BLOCK_SIZE); + } +} + +/** + * Generate the block J0 + */ +static void create_j(private_gcm_aead_t *this, char *iv, char *j) +{ + memcpy(j, this->salt, SALT_SIZE); + memcpy(j + SALT_SIZE, iv, IV_SIZE); + htoun32(j + SALT_SIZE + IV_SIZE, 1); +} + +/** + * Create GHASH subkey H + */ +static void create_h(private_gcm_aead_t *this, char *h) +{ + char zero[BLOCK_SIZE]; + + memset(zero, 0, BLOCK_SIZE); + memset(h, 0, BLOCK_SIZE); + + this->crypter->encrypt(this->crypter, chunk_create(h, BLOCK_SIZE), + chunk_from_thing(zero), NULL); +} + +/** + * Encrypt/decrypt + */ +static void crypt(private_gcm_aead_t *this, char *j, chunk_t in, chunk_t out) +{ + char icb[BLOCK_SIZE]; + + memcpy(icb, j, BLOCK_SIZE); + chunk_increment(chunk_from_thing(icb)); + + out.len = in.len; + if (in.ptr != out.ptr) + { + memcpy(out.ptr, in.ptr, in.len); + } + gctr(this, icb, out); +} + +/** + * Create ICV + */ +static void create_icv(private_gcm_aead_t *this, chunk_t assoc, chunk_t crypt, + char *j, char *icv) +{ + size_t assoc_pad, crypt_pad; + chunk_t chunk; + char s[BLOCK_SIZE], *pos; + + assoc_pad = (BLOCK_SIZE - (assoc.len % BLOCK_SIZE)) % BLOCK_SIZE; + crypt_pad = (BLOCK_SIZE - (crypt.len % BLOCK_SIZE)) % BLOCK_SIZE; + + /* concatenate data to a new chunk */ + chunk = chunk_alloc(assoc.len + assoc_pad + + crypt.len + crypt_pad + BLOCK_SIZE); + pos = chunk.ptr; + /* add associated data */ + memcpy(pos, assoc.ptr, assoc.len); + pos += assoc.len; + memset(pos, 0, assoc_pad); + pos += assoc_pad; + /* add encrypted data */ + memcpy(pos, crypt.ptr, crypt.len); + pos += crypt.len; + memset(pos, 0, crypt_pad); + pos += crypt_pad; + /* write associated len */ + memset(pos, 0, 4); + pos += 4; + htoun32(pos, assoc.len * 8); + pos += 4; + /* write encrypted length */ + memset(pos, 0, 4); + pos += 4; + htoun32(pos, crypt.len * 8); + pos += 4; + + ghash(this, chunk, s); + free(chunk.ptr); + gctr(this, j, chunk_from_thing(s)); + + memcpy(icv, s, this->icv_size); +} + +/** + * Verify the ICV value + */ +static bool verify_icv(private_gcm_aead_t *this, chunk_t assoc, chunk_t crypt, + char *j, char *icv) +{ + char tmp[this->icv_size]; + + create_icv(this, assoc, crypt, j, tmp); + + return memeq(tmp, icv, this->icv_size); +} + +METHOD(aead_t, encrypt, void, + private_gcm_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv, + chunk_t *encrypted) +{ + char j[BLOCK_SIZE]; + + create_j(this, iv.ptr, j); + + if (encrypted) + { + *encrypted = chunk_alloc(plain.len + this->icv_size); + crypt(this, j, plain, *encrypted); + create_icv(this, assoc, + chunk_create(encrypted->ptr, encrypted->len - this->icv_size), + j, encrypted->ptr + encrypted->len - this->icv_size); + } + else + { + crypt(this, j, plain, plain); + create_icv(this, assoc, plain, j, plain.ptr + plain.len); + } +} + +METHOD(aead_t, decrypt, bool, + private_gcm_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv, + chunk_t *plain) +{ + char j[BLOCK_SIZE]; + + if (encrypted.len < this->icv_size) + { + return FALSE; + } + + create_j(this, iv.ptr, j); + + encrypted.len -= this->icv_size; + if (!verify_icv(this, assoc, encrypted, j, encrypted.ptr + encrypted.len)) + { + return FALSE; + } + if (plain) + { + *plain = chunk_alloc(encrypted.len); + crypt(this, j, encrypted, *plain); + } + else + { + crypt(this, j, encrypted, encrypted); + } + return TRUE; +} + +METHOD(aead_t, get_block_size, size_t, + private_gcm_aead_t *this) +{ + return 1; +} + +METHOD(aead_t, get_icv_size, size_t, + private_gcm_aead_t *this) +{ + return this->icv_size; +} + +METHOD(aead_t, get_iv_size, size_t, + private_gcm_aead_t *this) +{ + return IV_SIZE; +} + +METHOD(aead_t, get_key_size, size_t, + private_gcm_aead_t *this) +{ + return this->crypter->get_key_size(this->crypter) + SALT_SIZE; +} + +METHOD(aead_t, set_key, void, + private_gcm_aead_t *this, chunk_t key) +{ + memcpy(this->salt, key.ptr + key.len - SALT_SIZE, SALT_SIZE); + key.len -= SALT_SIZE; + this->crypter->set_key(this->crypter, key); + create_h(this, this->h); +} + +METHOD(aead_t, destroy, void, + private_gcm_aead_t *this) +{ + this->crypter->destroy(this->crypter); + free(this); +} + +/** + * See header + */ +gcm_aead_t *gcm_aead_create(encryption_algorithm_t algo, size_t key_size) +{ + private_gcm_aead_t *this; + size_t icv_size; + + switch (key_size) + { + case 0: + key_size = 16; + break; + case 16: + case 24: + case 32: + break; + default: + return NULL; + } + switch (algo) + { + case ENCR_AES_GCM_ICV8: + algo = ENCR_AES_CBC; + icv_size = 8; + break; + case ENCR_AES_GCM_ICV12: + algo = ENCR_AES_CBC; + icv_size = 12; + break; + case ENCR_AES_GCM_ICV16: + algo = ENCR_AES_CBC; + icv_size = 16; + break; + default: + return NULL; + } + + INIT(this, + .public = { + .aead = { + .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, + }, + }, + .crypter = lib->crypto->create_crypter(lib->crypto, algo, key_size), + .icv_size = icv_size, + ); + + if (!this->crypter) + { + free(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libstrongswan/plugins/gcm/gcm_aead.h b/src/libstrongswan/plugins/gcm/gcm_aead.h new file mode 100644 index 000000000..db4be2442 --- /dev/null +++ b/src/libstrongswan/plugins/gcm/gcm_aead.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 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 <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. + */ + +/** + * @defgroup gcm_aead gcm_aead + * @{ @ingroup gcm + */ + +#ifndef GCM_AEAD_H_ +#define GCM_AEAD_H_ + +#include <crypto/aead.h> + +typedef struct gcm_aead_t gcm_aead_t; + +/** + * Galois/Counter Mode (GCM). + * + * Implements GCM as specified in NIST 800-38D, using AEAD semantics from + * RFC 5282, based on RFC4106. + */ +struct gcm_aead_t { + + /** + * Implements aead_t interface. + */ + aead_t aead; +}; + +/** + * Create a gcm_aead instance. + * + * @param key_size key size in bytes + * @param algo algorithm to implement, a gcm mode + * @return aead, NULL if not supported + */ +gcm_aead_t *gcm_aead_create(encryption_algorithm_t algo, size_t key_size); + +#endif /** GCM_AEAD_H_ @}*/ diff --git a/src/libstrongswan/plugins/gcm/gcm_plugin.c b/src/libstrongswan/plugins/gcm/gcm_plugin.c new file mode 100644 index 000000000..061001b30 --- /dev/null +++ b/src/libstrongswan/plugins/gcm/gcm_plugin.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 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 <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 "gcm_plugin.h" + +#include <library.h> + +#include "gcm_aead.h" + +typedef struct private_gcm_plugin_t private_gcm_plugin_t; + +/** + * private data of gcm_plugin + */ +struct private_gcm_plugin_t { + + /** + * public functions + */ + gcm_plugin_t public; +}; + +METHOD(plugin_t, destroy, void, + private_gcm_plugin_t *this) +{ + lib->crypto->remove_aead(lib->crypto, + (aead_constructor_t)gcm_aead_create); + + free(this); +} + +/* + * see header file + */ +plugin_t *gcm_plugin_create() +{ + private_gcm_plugin_t *this; + + INIT(this, + .public.plugin.destroy = _destroy, + ); + + lib->crypto->add_aead(lib->crypto, ENCR_AES_GCM_ICV8, + (aead_constructor_t)gcm_aead_create); + lib->crypto->add_aead(lib->crypto, ENCR_AES_GCM_ICV12, + (aead_constructor_t)gcm_aead_create); + lib->crypto->add_aead(lib->crypto, ENCR_AES_GCM_ICV16, + (aead_constructor_t)gcm_aead_create); + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/gcm/gcm_plugin.h b/src/libstrongswan/plugins/gcm/gcm_plugin.h new file mode 100644 index 000000000..52676708e --- /dev/null +++ b/src/libstrongswan/plugins/gcm/gcm_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 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 <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. + */ + +/** + * @defgroup gcm gcm + * @ingroup plugins + * + * @defgroup gcm_plugin gcm_plugin + * @{ @ingroup gcm + */ + +#ifndef GCM_PLUGIN_H_ +#define GCM_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct gcm_plugin_t gcm_plugin_t; + +/** + * Plugin providing GCM mode operation. + */ +struct gcm_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** GCM_PLUGIN_H_ @}*/ |