diff options
-rw-r--r-- | configure.in | 4 | ||||
-rw-r--r-- | src/libstrongswan/Makefile.am | 7 | ||||
-rw-r--r-- | src/libstrongswan/plugins/ccm/Makefile.am | 16 | ||||
-rw-r--r-- | src/libstrongswan/plugins/ccm/ccm_aead.c | 397 | ||||
-rw-r--r-- | src/libstrongswan/plugins/ccm/ccm_aead.h | 51 | ||||
-rw-r--r-- | src/libstrongswan/plugins/ccm/ccm_plugin.c | 69 | ||||
-rw-r--r-- | src/libstrongswan/plugins/ccm/ccm_plugin.h | 42 |
7 files changed, 586 insertions, 0 deletions
diff --git a/configure.in b/configure.in index 51dd8f25a..a30ac740e 100644 --- a/configure.in +++ b/configure.in @@ -148,6 +148,7 @@ ARG_ENABL_SET([gcrypt], [enables the libgcrypt plugin.]) ARG_ENABL_SET([agent], [enables the ssh-agent signing plugin.]) ARG_ENABL_SET([pkcs11], [enables the PKCS11 token support plugin.]) ARG_ENABL_SET([ctr], [enables the Counter Mode wrapper crypto plugin.]) +ARG_ENABL_SET([ccm], [enables the CCM AEAD wrapper crypto plugin.]) ARG_ENABL_SET([addrblock], [enables RFC 3779 address block constraint support.]) ARG_ENABL_SET([uci], [enable OpenWRT UCI configuration plugin.]) ARG_ENABL_SET([android], [enable Android specific plugin.]) @@ -716,6 +717,7 @@ ADD_PLUGIN([pkcs11], [s libcharon pki]) ADD_PLUGIN([xcbc], [s libcharon]) ADD_PLUGIN([hmac], [s libcharon pluto]) ADD_PLUGIN([ctr], [s libcharon]) +ADD_PLUGIN([ccm], [s libcharon]) ADD_PLUGIN([xauth], [p pluto]) ADD_PLUGIN([attr], [h libcharon pluto]) ADD_PLUGIN([attr-sql], [h libcharon pluto]) @@ -808,6 +810,7 @@ AM_CONDITIONAL(USE_GCRYPT, test x$gcrypt = xtrue) AM_CONDITIONAL(USE_AGENT, test x$agent = xtrue) AM_CONDITIONAL(USE_PKCS11, test x$pkcs11 = xtrue) AM_CONDITIONAL(USE_CTR, test x$ctr = xtrue) +AM_CONDITIONAL(USE_CCM, test x$ccm = xtrue) dnl charon plugins dnl ============== @@ -940,6 +943,7 @@ AC_OUTPUT( src/libstrongswan/plugins/agent/Makefile src/libstrongswan/plugins/pkcs11/Makefile src/libstrongswan/plugins/ctr/Makefile + src/libstrongswan/plugins/ccm/Makefile src/libstrongswan/plugins/test_vectors/Makefile src/libhydra/Makefile src/libhydra/plugins/attr/Makefile diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index e1e33c19f..3b7436b3d 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -331,6 +331,13 @@ if MONOLITHIC endif endif +if USE_CCM + SUBDIRS += plugins/ccm +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/ccm/libstrongswan-ccm.la +endif +endif + if USE_TEST_VECTORS SUBDIRS += plugins/test_vectors if MONOLITHIC diff --git a/src/libstrongswan/plugins/ccm/Makefile.am b/src/libstrongswan/plugins/ccm/Makefile.am new file mode 100644 index 000000000..bca1f0735 --- /dev/null +++ b/src/libstrongswan/plugins/ccm/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-ccm.la +else +plugin_LTLIBRARIES = libstrongswan-ccm.la +endif + +libstrongswan_ccm_la_SOURCES = \ + ccm_plugin.h ccm_plugin.c \ + ccm_aead.h ccm_aead.c + +libstrongswan_ccm_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/ccm/ccm_aead.c b/src/libstrongswan/plugins/ccm/ccm_aead.c new file mode 100644 index 000000000..7fee2b3c4 --- /dev/null +++ b/src/libstrongswan/plugins/ccm/ccm_aead.c @@ -0,0 +1,397 @@ +/* + * 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 "ccm_aead.h" + +#define BLOCK_SIZE 16 +#define SALT_SIZE 3 +#define IV_SIZE 8 +#define NONCE_SIZE (SALT_SIZE + IV_SIZE) /* 11 */ +#define Q_SIZE (BLOCK_SIZE - NONCE_SIZE - 1) /* 4 */ + +typedef struct private_ccm_aead_t private_ccm_aead_t; + +/** + * Private data of an ccm_aead_t object. + */ +struct private_ccm_aead_t { + + /** + * Public ccm_aead_t interface. + */ + ccm_aead_t public; + + /** + * Underlying CBC crypter. + */ + crypter_t *crypter; + + /** + * Length of the integrity check value + */ + size_t icv_size; + + /** + * salt to add to nonce + */ + u_char salt[SALT_SIZE]; +}; + +/** + * First block with control information + */ +typedef struct __attribute__((packed)) { + BITFIELD4(u_int8_t, + /* size of p length field q, as q-1 */ + q_len: 3, + /* size of our ICV t, as (t-2)/2 */ + t_len: 3, + /* do we have associated data */ + assoc: 1, + reserved: 1, + ) flags; + /* nonce value */ + struct __attribute__((packed)) { + u_char salt[SALT_SIZE]; + u_char iv[IV_SIZE]; + } nonce; + /* lenght of plain text, q */ + u_char q[Q_SIZE]; +} b0_t; + +/** + * Counter block + */ +typedef struct __attribute__((packed)) { + BITFIELD3(u_int8_t, + /* size of p length field q, as q-1 */ + q_len: 3, + zero: 3, + reserved: 2, + ) flags; + /* nonce value */ + struct __attribute__((packed)) { + u_char salt[SALT_SIZE]; + u_char iv[IV_SIZE]; + } nonce; + /* counter value */ + u_char i[Q_SIZE]; +} ctr_t; + +/** + * Build the first block B0 + */ +static void build_b0(private_ccm_aead_t *this, chunk_t plain, chunk_t assoc, + chunk_t iv, char *out) +{ + b0_t *block = (b0_t*)out; + + block->flags.reserved = 0; + block->flags.assoc = assoc.len ? 1 : 0; + block->flags.t_len = (this->icv_size - 2) / 2; + block->flags.q_len = Q_SIZE - 1; + memcpy(block->nonce.salt, this->salt, SALT_SIZE); + memcpy(block->nonce.iv, iv.ptr, IV_SIZE); + htoun32(block->q, plain.len); +} + +/** + * Build a counter block for counter i + */ +static void build_ctr(private_ccm_aead_t *this, u_int32_t i, chunk_t iv, + char *out) +{ + ctr_t *ctr = (ctr_t*)out; + + ctr->flags.reserved = 0; + ctr->flags.zero = 0; + ctr->flags.q_len = Q_SIZE - 1; + memcpy(ctr->nonce.salt, this->salt, SALT_SIZE); + memcpy(ctr->nonce.iv, iv.ptr, IV_SIZE); + htoun32(ctr->i, i); +} + +/** + * En-/Decrypt data + */ +static void crypt_data(private_ccm_aead_t *this, chunk_t iv, + chunk_t in, chunk_t out) +{ + char ctr[BLOCK_SIZE]; + char zero[BLOCK_SIZE]; + char block[BLOCK_SIZE]; + + build_ctr(this, 1, iv, ctr); + memset(zero, 0, BLOCK_SIZE); + + while (in.len > 0) + { + memcpy(block, ctr, BLOCK_SIZE); + this->crypter->encrypt(this->crypter, chunk_from_thing(block), + chunk_from_thing(zero), NULL); + chunk_increment(chunk_from_thing(ctr)); + + if (in.ptr != out.ptr) + { + memcpy(out.ptr, in.ptr, min(in.len, BLOCK_SIZE)); + } + memxor(out.ptr, block, min(in.len, BLOCK_SIZE)); + in = chunk_skip(in, BLOCK_SIZE); + out = chunk_skip(out, BLOCK_SIZE); + } +} + +/** + * En-/Decrypt the ICV + */ +static void crypt_icv(private_ccm_aead_t *this, chunk_t iv, char *icv) +{ + char ctr[BLOCK_SIZE]; + char zero[BLOCK_SIZE]; + + build_ctr(this, 0, iv, ctr); + memset(zero, 0, BLOCK_SIZE); + + this->crypter->encrypt(this->crypter, chunk_from_thing(ctr), + chunk_from_thing(zero), NULL); + memxor(icv, ctr, this->icv_size); +} + +/** + * Create the ICV + */ +static void create_icv(private_ccm_aead_t *this, chunk_t plain, chunk_t assoc, + chunk_t iv, char *icv) +{ + char zero[BLOCK_SIZE]; + chunk_t chunk; + char *pos; + int r, len; + + memset(zero, 0, BLOCK_SIZE); + + /* calculate number of blocks, including b0 */ + r = 1; + if (assoc.len) + { /* assoc gets a 2 byte length header, gets padded to BLOCK_SIZE */ + r += (2 + assoc.len + BLOCK_SIZE - 1) / BLOCK_SIZE; + } + /* plain text gets padded to BLOCK_SIZE */ + r += (plain.len + BLOCK_SIZE - 1) / BLOCK_SIZE; + + /* concatenate data to a new chunk */ + chunk = chunk_alloc(r * BLOCK_SIZE); + /* write control block */ + build_b0(this, plain, assoc, iv, chunk.ptr); + pos = chunk.ptr + BLOCK_SIZE; + /* append associated data, with length header */ + if (assoc.len) + { + /* currently we support two byte headers only (up to 2^16-2^8 bytes) */ + htoun16(pos, assoc.len); + memcpy(pos + 2, assoc.ptr, assoc.len); + pos += 2 + assoc.len; + /* padding */ + len = (BLOCK_SIZE - ((2 + assoc.len) % BLOCK_SIZE)) % BLOCK_SIZE; + memset(pos, 0, len); + pos += len; + } + /* write plain data */ + memcpy(pos, plain.ptr, plain.len); + pos += plain.len; + /* padding */ + len = (BLOCK_SIZE - (plain.len % BLOCK_SIZE)) % BLOCK_SIZE; + + memset(pos, 0, len); + + /* encrypt inline with CBC, zero IV */ + this->crypter->encrypt(this->crypter, chunk, chunk_from_thing(zero), NULL); + /* copy last icv_size bytes as ICV to output */ + memcpy(icv, chunk.ptr + chunk.len - BLOCK_SIZE, this->icv_size); + + /* encrypt the ICV value */ + crypt_icv(this, iv, icv); + + free(chunk.ptr); +} + +/** + * Verify the ICV + */ +static bool verify_icv(private_ccm_aead_t *this, chunk_t plain, chunk_t assoc, + chunk_t iv, char *icv) +{ + char buf[this->icv_size]; + + create_icv(this, plain, assoc, iv, buf); + + return memeq(buf, icv, this->icv_size); +} + +METHOD(aead_t, encrypt, void, + private_ccm_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv, + chunk_t *encrypted) +{ + if (encrypted) + { + *encrypted = chunk_alloc(plain.len + this->icv_size); + create_icv(this, plain, assoc, iv, encrypted->ptr + plain.len); + crypt_data(this, iv, plain, *encrypted); + } + else + { + create_icv(this, plain, assoc, iv, plain.ptr + plain.len); + crypt_data(this, iv, plain, plain); + } +} + +METHOD(aead_t, decrypt, bool, + private_ccm_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv, + chunk_t *plain) +{ + if (encrypted.len < this->icv_size) + { + return FALSE; + } + encrypted.len -= this->icv_size; + if (plain) + { + *plain = chunk_alloc(encrypted.len); + crypt_data(this, iv, encrypted, *plain); + return verify_icv(this, *plain, assoc, iv, + encrypted.ptr + encrypted.len); + } + else + { + crypt_data(this, iv, encrypted, encrypted); + return verify_icv(this, encrypted, assoc, iv, + encrypted.ptr + encrypted.len); + } +} + +METHOD(aead_t, get_block_size, size_t, + private_ccm_aead_t *this) +{ + return 1; +} + +METHOD(aead_t, get_icv_size, size_t, + private_ccm_aead_t *this) +{ + return this->icv_size; +} + +METHOD(aead_t, get_iv_size, size_t, + private_ccm_aead_t *this) +{ + return IV_SIZE; +} + +METHOD(aead_t, get_key_size, size_t, + private_ccm_aead_t *this) +{ + return this->crypter->get_key_size(this->crypter) + SALT_SIZE; +} + +METHOD(aead_t, set_key, void, + private_ccm_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); +} + +METHOD(aead_t, destroy, void, + private_ccm_aead_t *this) +{ + this->crypter->destroy(this->crypter); + free(this); +} + +/** + * See header + */ +ccm_aead_t *ccm_aead_create(encryption_algorithm_t algo, size_t key_size) +{ + private_ccm_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_CCM_ICV8: + algo = ENCR_AES_CBC; + icv_size = 8; + break; + case ENCR_AES_CCM_ICV12: + algo = ENCR_AES_CBC; + icv_size = 12; + break; + case ENCR_AES_CCM_ICV16: + algo = ENCR_AES_CBC; + icv_size = 16; + break; + case ENCR_CAMELLIA_CCM_ICV8: + algo = ENCR_CAMELLIA_CBC; + icv_size = 8; + break; + case ENCR_CAMELLIA_CCM_ICV12: + algo = ENCR_CAMELLIA_CBC; + icv_size = 12; + break; + case ENCR_CAMELLIA_CCM_ICV16: + algo = ENCR_CAMELLIA_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/ccm/ccm_aead.h b/src/libstrongswan/plugins/ccm/ccm_aead.h new file mode 100644 index 000000000..d5e302f94 --- /dev/null +++ b/src/libstrongswan/plugins/ccm/ccm_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 ccm_aead ccm_aead + * @{ @ingroup ccm + */ + +#ifndef CCM_AEAD_H_ +#define CCM_AEAD_H_ + +#include <crypto/aead.h> + +typedef struct ccm_aead_t ccm_aead_t; + +/** + * Counter with Cipher Block Chaining-Message Authentication Code (CCM). + * + * Implements CCM as specified in NIST 800-38B, using AEAD semantics from + * RFC 5282, based on RFC4309. + */ +struct ccm_aead_t { + + /** + * Implements aead_t interface. + */ + aead_t aead; +}; + +/** + * Create a ccm_aead instance. + * + * @param key_size key size in bytes + * @param algo algorithm to implement, a CCM mode + * @return aead, NULL if not supported + */ +ccm_aead_t *ccm_aead_create(encryption_algorithm_t algo, size_t key_size); + +#endif /** CCM_AEAD_H_ @}*/ diff --git a/src/libstrongswan/plugins/ccm/ccm_plugin.c b/src/libstrongswan/plugins/ccm/ccm_plugin.c new file mode 100644 index 000000000..5fc3b14d7 --- /dev/null +++ b/src/libstrongswan/plugins/ccm/ccm_plugin.c @@ -0,0 +1,69 @@ +/* + * 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 "ccm_plugin.h" + +#include <library.h> + +#include "ccm_aead.h" + +typedef struct private_ccm_plugin_t private_ccm_plugin_t; + +/** + * private data of ccm_plugin + */ +struct private_ccm_plugin_t { + + /** + * public functions + */ + ccm_plugin_t public; +}; + +METHOD(plugin_t, destroy, void, + private_ccm_plugin_t *this) +{ + lib->crypto->remove_aead(lib->crypto, + (aead_constructor_t)ccm_aead_create); + + free(this); +} + +/* + * see header file + */ +plugin_t *ccm_plugin_create() +{ + private_ccm_plugin_t *this; + + INIT(this, + .public.plugin.destroy = _destroy, + ); + + lib->crypto->add_aead(lib->crypto, ENCR_AES_CCM_ICV8, + (aead_constructor_t)ccm_aead_create); + lib->crypto->add_aead(lib->crypto, ENCR_AES_CCM_ICV12, + (aead_constructor_t)ccm_aead_create); + lib->crypto->add_aead(lib->crypto, ENCR_AES_CCM_ICV16, + (aead_constructor_t)ccm_aead_create); + lib->crypto->add_aead(lib->crypto, ENCR_CAMELLIA_CCM_ICV8, + (aead_constructor_t)ccm_aead_create); + lib->crypto->add_aead(lib->crypto, ENCR_CAMELLIA_CCM_ICV12, + (aead_constructor_t)ccm_aead_create); + lib->crypto->add_aead(lib->crypto, ENCR_CAMELLIA_CCM_ICV16, + (aead_constructor_t)ccm_aead_create); + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/ccm/ccm_plugin.h b/src/libstrongswan/plugins/ccm/ccm_plugin.h new file mode 100644 index 000000000..9a3252012 --- /dev/null +++ b/src/libstrongswan/plugins/ccm/ccm_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 ccm ccm + * @ingroup plugins + * + * @defgroup ccm_plugin ccm_plugin + * @{ @ingroup ccm + */ + +#ifndef CCM_PLUGIN_H_ +#define CCM_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct ccm_plugin_t ccm_plugin_t; + +/** + * Plugin providing CCM mode operation. + */ +struct ccm_plugin_t { + + /** + * Implements plugin interface. + */ + plugin_t plugin; +}; + +#endif /** CCM_PLUGIN_H_ @}*/ |