From 20a95ef2731ce21acfe5c1ba97b40001217b0216 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 2 Nov 2013 14:34:01 +0100 Subject: Convert aes128-gcm into a generic gcm method --- CMakeLists.txt | 2 +- src/config.c | 4 +- src/crypto/cipher/ciphers.c.in | 11 + src/fastd.h | 5 +- src/methods/CMakeLists.txt | 6 +- src/methods/aes128_gcm/CMakeLists.txt | 6 - src/methods/aes128_gcm/aes128_gcm.c | 261 -------------------- src/methods/generic_gcm/CMakeLists.txt | 6 + src/methods/generic_gcm/generic_gcm.c | 283 ++++++++++++++++++++++ src/methods/methods.c.in | 4 +- src/methods/null/null.c | 2 +- src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c | 2 +- src/protocols/ec25519_fhmqvc/handshake.c | 2 +- 13 files changed, 314 insertions(+), 280 deletions(-) delete mode 100644 src/methods/aes128_gcm/CMakeLists.txt delete mode 100644 src/methods/aes128_gcm/aes128_gcm.c create mode 100644 src/methods/generic_gcm/CMakeLists.txt create mode 100644 src/methods/generic_gcm/generic_gcm.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 371530b..12daafa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ set(WITH_CIPHER_AES128_CTR_NACL TRUE CACHE BOOL "Include the AES128-CTR implemen set(WITH_CRYPTO_GHASH_BUILTIN TRUE CACHE BOOL "Include the built-in GHASH implementation") set(WITH_METHOD_XSALSA20_POLY1305 TRUE CACHE BOOL "Include xsalsa20-poly1305 method") -set(WITH_METHOD_AES128_GCM TRUE CACHE BOOL "Include aes128-gcm method") +set(WITH_METHOD_GENERIC_GCM TRUE CACHE BOOL "Include generic gcm method") set(USE_LIBSODIUM FALSE CACHE BOOL "Use libsodium instead of NaCl") diff --git a/src/config.c b/src/config.c index aa5ea59..8cf0dcb 100644 --- a/src/config.c +++ b/src/config.c @@ -95,7 +95,7 @@ bool fastd_config_protocol(fastd_context_t *ctx UNUSED, fastd_config_t *conf, co } bool fastd_config_method(fastd_context_t *ctx, fastd_config_t *conf, const char *name) { - if (!fastd_method_get_by_name(ctx, name)) + if (!fastd_method_get_by_name(name)) return false; fastd_string_stack_t **method; @@ -508,7 +508,7 @@ static void configure_method_parameters(fastd_context_t *ctx, fastd_config_t *co fastd_string_stack_t *method_name; for (method_name = conf->methods; method_name; method_name = method_name->next) { - const fastd_method_t *method = fastd_method_get_by_name(ctx, method_name->str); + const fastd_method_t *method = fastd_method_get_by_name(method_name->str); conf->max_packet_size = max_size_t(conf->max_packet_size, method->max_packet_size(ctx)); conf->min_encrypt_head_space = max_size_t(conf->min_encrypt_head_space, method->min_encrypt_head_space(ctx)); diff --git a/src/crypto/cipher/ciphers.c.in b/src/crypto/cipher/ciphers.c.in index 7ae2dea..6ff058f 100644 --- a/src/crypto/cipher/ciphers.c.in +++ b/src/crypto/cipher/ciphers.c.in @@ -91,6 +91,17 @@ void fastd_cipher_free(fastd_context_t *ctx) { free(ctx->cipher_contexts); } +bool fastd_cipher_available(const char *name) { + size_t i; + for (i = 0; i < array_size(ciphers); i++) { + if (!strcmp(ciphers[i].name, name)) { + return true; + } + } + + return false; +} + const fastd_cipher_t* fastd_cipher_get_by_name(fastd_context_t *ctx, const char *name, fastd_cipher_context_t **cctx) { size_t i; for (i = 0; i < array_size(ciphers); i++) { diff --git a/src/fastd.h b/src/fastd.h index 5ace535..6f0bd19 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -79,7 +79,7 @@ struct fastd_protocol { }; struct fastd_method { - bool (*provides)(fastd_context_t *ctx, const char *name); + bool (*provides)(const char *name); size_t (*max_packet_size)(fastd_context_t *ctx); size_t (*min_encrypt_head_space)(fastd_context_t *ctx); @@ -359,7 +359,8 @@ void fastd_logf(const fastd_context_t *ctx, fastd_loglevel_t level, const char * void fastd_add_peer_dir(fastd_context_t *ctx, fastd_config_t *conf, const char *dir); bool fastd_read_config(fastd_context_t *ctx, fastd_config_t *conf, const char *filename, bool peer_config, int depth); -const fastd_method_t* fastd_method_get_by_name(fastd_context_t *ctx, const char *name); +bool fastd_cipher_available(const char *name); +const fastd_method_t* fastd_method_get_by_name(const char *name); const fastd_cipher_t** fastd_cipher_config_alloc(void); void fastd_cipher_config_free(const fastd_cipher_t **cipher_conf); diff --git a/src/methods/CMakeLists.txt b/src/methods/CMakeLists.txt index c5a6273..d707884 100644 --- a/src/methods/CMakeLists.txt +++ b/src/methods/CMakeLists.txt @@ -6,10 +6,10 @@ if(WITH_METHOD_XSALSA20_POLY1305) set(METHOD_COMMON TRUE) endif(WITH_METHOD_XSALSA20_POLY1305) -if(WITH_METHOD_AES128_GCM) - list(APPEND METHODS aes128_gcm) +if(WITH_METHOD_GENERIC_GCM) + list(APPEND METHODS generic_gcm) set(METHOD_COMMON TRUE) -endif(WITH_METHOD_AES128_GCM) +endif(WITH_METHOD_GENERIC_GCM) set(METHOD_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/methods.c") diff --git a/src/methods/aes128_gcm/CMakeLists.txt b/src/methods/aes128_gcm/CMakeLists.txt deleted file mode 100644 index 6d07318..0000000 --- a/src/methods/aes128_gcm/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -include_directories(${FASTD_SOURCE_DIR}/src ${FASTD_BINARY_DIR} ${NACL_INCLUDE_DIRS}) - -add_library(method_aes128_gcm OBJECT - aes128_gcm.c -) -set_property(TARGET method_aes128_gcm PROPERTY COMPILE_FLAGS "${FASTD_CFLAGS}") diff --git a/src/methods/aes128_gcm/aes128_gcm.c b/src/methods/aes128_gcm/aes128_gcm.c deleted file mode 100644 index 8a6325e..0000000 --- a/src/methods/aes128_gcm/aes128_gcm.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - Copyright (c) 2012-2013, Matthias Schiffer - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - - -#include "../../fastd.h" -#include "../../crypto.h" -#include "../common.h" - - -struct fastd_method_session_state { - fastd_method_common_t common; - - const fastd_cipher_t *aes128_ctr; - fastd_cipher_context_t *aes128_ctr_ctx; - fastd_cipher_state_t *aes128_ctr_state; - - fastd_crypto_ghash_state_t *cstate_ghash; -}; - - -static bool method_provides(fastd_context_t *ctx UNUSED, const char *name) { - return !strcmp(name, "aes128-gcm"); -} - -static size_t method_max_packet_size(fastd_context_t *ctx) { - return (fastd_max_packet_size(ctx) + COMMON_NONCEBYTES + sizeof(fastd_block128_t)); -} - - -static size_t method_min_encrypt_head_space(fastd_context_t *ctx UNUSED) { - return sizeof(fastd_block128_t); -} - -static size_t method_min_decrypt_head_space(fastd_context_t *ctx UNUSED) { - return 0; -} - -static size_t method_min_encrypt_tail_space(fastd_context_t *ctx UNUSED) { - return (sizeof(fastd_block128_t)-1); -} - -static size_t method_min_decrypt_tail_space(fastd_context_t *ctx UNUSED) { - return (2*sizeof(fastd_block128_t)-1); -} - - -static size_t method_key_length(fastd_context_t *ctx UNUSED) { - return sizeof(fastd_block128_t); -} - -static fastd_method_session_state_t* method_session_init(fastd_context_t *ctx, const char *name UNUSED, const uint8_t *secret, bool initiator) { - fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); - - fastd_method_common_init(ctx, &session->common, initiator); - - session->aes128_ctr = fastd_cipher_get_by_name(ctx, "aes128-ctr", &session->aes128_ctr_ctx); - if (!session->aes128_ctr) - exit_bug(ctx, "aes128-gcm: can't instanciate aes128-ctr"); - - session->aes128_ctr_state = session->aes128_ctr->init_state(ctx, session->aes128_ctr_ctx, secret); - - static const fastd_block128_t zeroblock = {}; - fastd_block128_t H; - - session->aes128_ctr->crypt(ctx, session->aes128_ctr_state, &H, &zeroblock, sizeof(fastd_block128_t), &zeroblock); - - session->cstate_ghash = ctx->conf->crypto_ghash->set_h(ctx, ctx->crypto_ghash, &H); - - return session; -} - -static fastd_method_session_state_t* method_session_init_compat(fastd_context_t *ctx, const char *name, const uint8_t *secret, size_t length, bool initiator) { - if (length < sizeof(fastd_block128_t)) - exit_bug(ctx, "aes128-gcm: tried to init with short secret"); - - return method_session_init(ctx, name, secret, initiator); -} - -static bool method_session_is_valid(fastd_context_t *ctx, fastd_method_session_state_t *session) { - return (session && fastd_method_session_common_is_valid(ctx, &session->common)); -} - -static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - return fastd_method_session_common_is_initiator(&session->common); -} - -static bool method_session_want_refresh(fastd_context_t *ctx, fastd_method_session_state_t *session) { - return fastd_method_session_common_want_refresh(ctx, &session->common); -} - -static void method_session_superseded(fastd_context_t *ctx, fastd_method_session_state_t *session) { - fastd_method_session_common_superseded(ctx, &session->common); -} - -static void method_session_free(fastd_context_t *ctx, fastd_method_session_state_t *session) { - if (session) { - session->aes128_ctr->free_state(ctx, session->aes128_ctr_state); - ctx->conf->crypto_ghash->free_state(ctx, session->cstate_ghash); - - secure_memzero(session, sizeof(fastd_method_session_state_t)); - free(session); - } -} - -static inline void put_size(fastd_block128_t *out, size_t len) { - memset(out, 0, sizeof(fastd_block128_t)-5); - out->b[sizeof(fastd_block128_t)-5] = len >> 29; - out->b[sizeof(fastd_block128_t)-4] = len >> 21; - out->b[sizeof(fastd_block128_t)-3] = len >> 13; - out->b[sizeof(fastd_block128_t)-2] = len >> 5; - out->b[sizeof(fastd_block128_t)-1] = len << 3; -} - -static bool method_encrypt(fastd_context_t *ctx, fastd_peer_t *peer UNUSED, fastd_method_session_state_t *session, fastd_buffer_t *out, fastd_buffer_t in) { - fastd_buffer_pull_head(ctx, &in, sizeof(fastd_block128_t)); - memset(in.data, 0, sizeof(fastd_block128_t)); - - size_t tail_len = alignto(in.len, sizeof(fastd_block128_t))-in.len; - *out = fastd_buffer_alloc(ctx, in.len, alignto(COMMON_NONCEBYTES, 16), sizeof(fastd_block128_t)+tail_len); - - if (tail_len) - memset(in.data+in.len, 0, tail_len); - - fastd_block128_t nonce; - memcpy(nonce.b, session->common.send_nonce, COMMON_NONCEBYTES); - memset(nonce.b+COMMON_NONCEBYTES, 0, sizeof(fastd_block128_t)-COMMON_NONCEBYTES-1); - nonce.b[sizeof(fastd_block128_t)-1] = 1; - - int n_blocks = (in.len+sizeof(fastd_block128_t)-1)/sizeof(fastd_block128_t); - - fastd_block128_t *inblocks = in.data; - fastd_block128_t *outblocks = out->data; - fastd_block128_t sig; - - bool ok = session->aes128_ctr->crypt(ctx, session->aes128_ctr_state, outblocks, inblocks, n_blocks*sizeof(fastd_block128_t), &nonce); - - if (ok) { - if (tail_len) - memset(out->data+out->len, 0, tail_len); - - put_size(&outblocks[n_blocks], in.len-sizeof(fastd_block128_t)); - - ok = ctx->conf->crypto_ghash->hash(ctx, session->cstate_ghash, &sig, outblocks+1, n_blocks); - } - - if (!ok) { - /* restore original buffer */ - fastd_buffer_push_head(ctx, &in, sizeof(fastd_block128_t)); - fastd_buffer_free(*out); - return false; - } - - xor_a(&outblocks[0], &sig); - - fastd_buffer_free(in); - - fastd_buffer_pull_head(ctx, out, COMMON_NONCEBYTES); - memcpy(out->data, session->common.send_nonce, COMMON_NONCEBYTES); - fastd_method_increment_nonce(&session->common); - - return true; -} - -static bool method_decrypt(fastd_context_t *ctx, fastd_peer_t *peer, fastd_method_session_state_t *session, fastd_buffer_t *out, fastd_buffer_t in) { - if (in.len < COMMON_NONCEBYTES+sizeof(fastd_block128_t)) - return false; - - if (!method_session_is_valid(ctx, session)) - return false; - - fastd_block128_t nonce; - memcpy(nonce.b, in.data, COMMON_NONCEBYTES); - memset(nonce.b+COMMON_NONCEBYTES, 0, sizeof(fastd_block128_t)-COMMON_NONCEBYTES-1); - nonce.b[sizeof(fastd_block128_t)-1] = 1; - - int64_t age; - if (!fastd_method_is_nonce_valid(ctx, &session->common, nonce.b, &age)) - return false; - - fastd_buffer_push_head(ctx, &in, COMMON_NONCEBYTES); - - size_t tail_len = alignto(in.len, sizeof(fastd_block128_t))-in.len; - *out = fastd_buffer_alloc(ctx, in.len, 0, tail_len); - - int n_blocks = (in.len+sizeof(fastd_block128_t)-1)/sizeof(fastd_block128_t); - - fastd_block128_t *inblocks = in.data; - fastd_block128_t *outblocks = out->data; - fastd_block128_t sig; - - bool ok = session->aes128_ctr->crypt(ctx, session->aes128_ctr_state, outblocks, inblocks, n_blocks*sizeof(fastd_block128_t), &nonce); - - if (ok) { - if (tail_len) - memset(in.data+in.len, 0, tail_len); - - put_size(&inblocks[n_blocks], in.len-sizeof(fastd_block128_t)); - - ok = ctx->conf->crypto_ghash->hash(ctx, session->cstate_ghash, &sig, inblocks+1, n_blocks); - } - - if (!ok || memcmp(&sig, &outblocks[0], sizeof(fastd_block128_t)) != 0) { - fastd_buffer_free(*out); - return false; - } - - fastd_buffer_free(in); - - fastd_buffer_push_head(ctx, out, sizeof(fastd_block128_t)); - - if (!fastd_method_reorder_check(ctx, peer, &session->common, nonce.b, age)) { - fastd_buffer_free(*out); - *out = fastd_buffer_alloc(ctx, 0, 0, 0); - } - - return true; -} - -const fastd_method_t fastd_method_aes128_gcm = { - .provides = method_provides, - - .max_packet_size = method_max_packet_size, - .min_encrypt_head_space = method_min_encrypt_head_space, - .min_decrypt_head_space = method_min_decrypt_head_space, - .min_encrypt_tail_space = method_min_encrypt_tail_space, - .min_decrypt_tail_space = method_min_decrypt_tail_space, - - .key_length = method_key_length, - .session_init = method_session_init, - .session_init_compat = method_session_init_compat, - .session_is_valid = method_session_is_valid, - .session_is_initiator = method_session_is_initiator, - .session_want_refresh = method_session_want_refresh, - .session_superseded = method_session_superseded, - .session_free = method_session_free, - - .encrypt = method_encrypt, - .decrypt = method_decrypt, -}; diff --git a/src/methods/generic_gcm/CMakeLists.txt b/src/methods/generic_gcm/CMakeLists.txt new file mode 100644 index 0000000..1771ee0 --- /dev/null +++ b/src/methods/generic_gcm/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(${FASTD_SOURCE_DIR}/src ${FASTD_BINARY_DIR}) + +add_library(method_generic_gcm OBJECT + generic_gcm.c +) +set_property(TARGET method_generic_gcm PROPERTY COMPILE_FLAGS "${FASTD_CFLAGS}") diff --git a/src/methods/generic_gcm/generic_gcm.c b/src/methods/generic_gcm/generic_gcm.c new file mode 100644 index 0000000..479ea83 --- /dev/null +++ b/src/methods/generic_gcm/generic_gcm.c @@ -0,0 +1,283 @@ +/* + Copyright (c) 2012-2013, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "../../fastd.h" +#include "../../crypto.h" +#include "../common.h" + + +struct fastd_method_session_state { + fastd_method_common_t common; + + const fastd_cipher_t *cipher; + fastd_cipher_context_t *cipher_ctx; + fastd_cipher_state_t *cipher_state; + + fastd_crypto_ghash_state_t *cstate_ghash; +}; + + +static bool cipher_get(fastd_context_t *ctx, const char *name, const fastd_cipher_t **cipher, fastd_cipher_context_t **cctx) { + size_t len = strlen(name); + + if (len < 4) + return NULL; + + if (strcmp(name+len-4, "-gcm")) + return NULL; + + char name_ctr[len+1]; + memcpy(name_ctr, name, len-3); + strncpy(name_ctr+len-3, "ctr", 4); + + if (ctx) { + *cipher = fastd_cipher_get_by_name(ctx, name_ctr, cctx); + return *cipher; + } + else { + return fastd_cipher_available(name_ctr); + } +} + + +static bool method_provides(const char *name) { + return cipher_get(NULL, name, NULL, NULL); +} + +static size_t method_max_packet_size(fastd_context_t *ctx) { + return (fastd_max_packet_size(ctx) + COMMON_NONCEBYTES + sizeof(fastd_block128_t)); +} + + +static size_t method_min_encrypt_head_space(fastd_context_t *ctx UNUSED) { + return sizeof(fastd_block128_t); +} + +static size_t method_min_decrypt_head_space(fastd_context_t *ctx UNUSED) { + return 0; +} + +static size_t method_min_encrypt_tail_space(fastd_context_t *ctx UNUSED) { + return (sizeof(fastd_block128_t)-1); +} + +static size_t method_min_decrypt_tail_space(fastd_context_t *ctx UNUSED) { + return (2*sizeof(fastd_block128_t)-1); +} + + +static size_t method_key_length(fastd_context_t *ctx UNUSED) { + return sizeof(fastd_block128_t); +} + +static fastd_method_session_state_t* method_session_init(fastd_context_t *ctx, const char *name, const uint8_t *secret, bool initiator) { + fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); + + fastd_method_common_init(ctx, &session->common, initiator); + + if (!cipher_get(ctx, name, &session->cipher, &session->cipher_ctx)) + exit_bug(ctx, "generic-gcm: can't instanciate cipher"); + + session->cipher_state = session->cipher->init_state(ctx, session->cipher_ctx, secret); + + static const fastd_block128_t zeroblock = {}; + fastd_block128_t H; + + session->cipher->crypt(ctx, session->cipher_state, &H, &zeroblock, sizeof(fastd_block128_t), &zeroblock); + + session->cstate_ghash = ctx->conf->crypto_ghash->set_h(ctx, ctx->crypto_ghash, &H); + + return session; +} + +static fastd_method_session_state_t* method_session_init_compat(fastd_context_t *ctx, const char *name, const uint8_t *secret, size_t length, bool initiator) { + if (length < sizeof(fastd_block128_t)) + exit_bug(ctx, "generic-gcm: tried to init with short secret"); + + return method_session_init(ctx, name, secret, initiator); +} + +static bool method_session_is_valid(fastd_context_t *ctx, fastd_method_session_state_t *session) { + return (session && fastd_method_session_common_is_valid(ctx, &session->common)); +} + +static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + return fastd_method_session_common_is_initiator(&session->common); +} + +static bool method_session_want_refresh(fastd_context_t *ctx, fastd_method_session_state_t *session) { + return fastd_method_session_common_want_refresh(ctx, &session->common); +} + +static void method_session_superseded(fastd_context_t *ctx, fastd_method_session_state_t *session) { + fastd_method_session_common_superseded(ctx, &session->common); +} + +static void method_session_free(fastd_context_t *ctx, fastd_method_session_state_t *session) { + if (session) { + session->cipher->free_state(ctx, session->cipher_state); + ctx->conf->crypto_ghash->free_state(ctx, session->cstate_ghash); + + secure_memzero(session, sizeof(fastd_method_session_state_t)); + free(session); + } +} + +static inline void put_size(fastd_block128_t *out, size_t len) { + memset(out, 0, sizeof(fastd_block128_t)-5); + out->b[sizeof(fastd_block128_t)-5] = len >> 29; + out->b[sizeof(fastd_block128_t)-4] = len >> 21; + out->b[sizeof(fastd_block128_t)-3] = len >> 13; + out->b[sizeof(fastd_block128_t)-2] = len >> 5; + out->b[sizeof(fastd_block128_t)-1] = len << 3; +} + +static bool method_encrypt(fastd_context_t *ctx, fastd_peer_t *peer UNUSED, fastd_method_session_state_t *session, fastd_buffer_t *out, fastd_buffer_t in) { + fastd_buffer_pull_head(ctx, &in, sizeof(fastd_block128_t)); + memset(in.data, 0, sizeof(fastd_block128_t)); + + size_t tail_len = alignto(in.len, sizeof(fastd_block128_t))-in.len; + *out = fastd_buffer_alloc(ctx, in.len, alignto(COMMON_NONCEBYTES, 16), sizeof(fastd_block128_t)+tail_len); + + if (tail_len) + memset(in.data+in.len, 0, tail_len); + + fastd_block128_t nonce; + memcpy(nonce.b, session->common.send_nonce, COMMON_NONCEBYTES); + memset(nonce.b+COMMON_NONCEBYTES, 0, sizeof(fastd_block128_t)-COMMON_NONCEBYTES-1); + nonce.b[sizeof(fastd_block128_t)-1] = 1; + + int n_blocks = (in.len+sizeof(fastd_block128_t)-1)/sizeof(fastd_block128_t); + + fastd_block128_t *inblocks = in.data; + fastd_block128_t *outblocks = out->data; + fastd_block128_t sig; + + bool ok = session->cipher->crypt(ctx, session->cipher_state, outblocks, inblocks, n_blocks*sizeof(fastd_block128_t), &nonce); + + if (ok) { + if (tail_len) + memset(out->data+out->len, 0, tail_len); + + put_size(&outblocks[n_blocks], in.len-sizeof(fastd_block128_t)); + + ok = ctx->conf->crypto_ghash->hash(ctx, session->cstate_ghash, &sig, outblocks+1, n_blocks); + } + + if (!ok) { + /* restore original buffer */ + fastd_buffer_push_head(ctx, &in, sizeof(fastd_block128_t)); + fastd_buffer_free(*out); + return false; + } + + xor_a(&outblocks[0], &sig); + + fastd_buffer_free(in); + + fastd_buffer_pull_head(ctx, out, COMMON_NONCEBYTES); + memcpy(out->data, session->common.send_nonce, COMMON_NONCEBYTES); + fastd_method_increment_nonce(&session->common); + + return true; +} + +static bool method_decrypt(fastd_context_t *ctx, fastd_peer_t *peer, fastd_method_session_state_t *session, fastd_buffer_t *out, fastd_buffer_t in) { + if (in.len < COMMON_NONCEBYTES+sizeof(fastd_block128_t)) + return false; + + if (!method_session_is_valid(ctx, session)) + return false; + + fastd_block128_t nonce; + memcpy(nonce.b, in.data, COMMON_NONCEBYTES); + memset(nonce.b+COMMON_NONCEBYTES, 0, sizeof(fastd_block128_t)-COMMON_NONCEBYTES-1); + nonce.b[sizeof(fastd_block128_t)-1] = 1; + + int64_t age; + if (!fastd_method_is_nonce_valid(ctx, &session->common, nonce.b, &age)) + return false; + + fastd_buffer_push_head(ctx, &in, COMMON_NONCEBYTES); + + size_t tail_len = alignto(in.len, sizeof(fastd_block128_t))-in.len; + *out = fastd_buffer_alloc(ctx, in.len, 0, tail_len); + + int n_blocks = (in.len+sizeof(fastd_block128_t)-1)/sizeof(fastd_block128_t); + + fastd_block128_t *inblocks = in.data; + fastd_block128_t *outblocks = out->data; + fastd_block128_t sig; + + bool ok = session->cipher->crypt(ctx, session->cipher_state, outblocks, inblocks, n_blocks*sizeof(fastd_block128_t), &nonce); + + if (ok) { + if (tail_len) + memset(in.data+in.len, 0, tail_len); + + put_size(&inblocks[n_blocks], in.len-sizeof(fastd_block128_t)); + + ok = ctx->conf->crypto_ghash->hash(ctx, session->cstate_ghash, &sig, inblocks+1, n_blocks); + } + + if (!ok || memcmp(&sig, &outblocks[0], sizeof(fastd_block128_t)) != 0) { + fastd_buffer_free(*out); + return false; + } + + fastd_buffer_free(in); + + fastd_buffer_push_head(ctx, out, sizeof(fastd_block128_t)); + + if (!fastd_method_reorder_check(ctx, peer, &session->common, nonce.b, age)) { + fastd_buffer_free(*out); + *out = fastd_buffer_alloc(ctx, 0, 0, 0); + } + + return true; +} + +const fastd_method_t fastd_method_generic_gcm = { + .provides = method_provides, + + .max_packet_size = method_max_packet_size, + .min_encrypt_head_space = method_min_encrypt_head_space, + .min_decrypt_head_space = method_min_decrypt_head_space, + .min_encrypt_tail_space = method_min_encrypt_tail_space, + .min_decrypt_tail_space = method_min_decrypt_tail_space, + + .key_length = method_key_length, + .session_init = method_session_init, + .session_init_compat = method_session_init_compat, + .session_is_valid = method_session_is_valid, + .session_is_initiator = method_session_is_initiator, + .session_want_refresh = method_session_want_refresh, + .session_superseded = method_session_superseded, + .session_free = method_session_free, + + .encrypt = method_encrypt, + .decrypt = method_decrypt, +}; diff --git a/src/methods/methods.c.in b/src/methods/methods.c.in index f39fbc1..8a8958c 100644 --- a/src/methods/methods.c.in +++ b/src/methods/methods.c.in @@ -33,10 +33,10 @@ static const fastd_method_t *const methods[] = { @METHOD_LIST@ }; -const fastd_method_t* fastd_method_get_by_name(fastd_context_t *ctx, const char *name) { +const fastd_method_t* fastd_method_get_by_name(const char *name) { size_t i; for (i = 0; i < array_size(methods); i++) { - if (methods[i]->provides(ctx, name)) + if (methods[i]->provides(name)) return methods[i]; } diff --git a/src/methods/null/null.c b/src/methods/null/null.c index cba7931..e93f0cf 100644 --- a/src/methods/null/null.c +++ b/src/methods/null/null.c @@ -33,7 +33,7 @@ struct fastd_method_session_state { }; -static bool method_provides(fastd_context_t *ctx UNUSED, const char *name) { +static bool method_provides(const char *name) { return !strcmp(name, "null"); } diff --git a/src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c b/src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c index 220e913..45bdc14 100644 --- a/src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c +++ b/src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c @@ -37,7 +37,7 @@ struct fastd_method_session_state { }; -static bool method_provides(fastd_context_t *ctx UNUSED, const char *name) { +static bool method_provides(const char *name) { return !strcmp(name, "xsalsa20-poly1305"); } diff --git a/src/protocols/ec25519_fhmqvc/handshake.c b/src/protocols/ec25519_fhmqvc/handshake.c index cb89462..909d011 100644 --- a/src/protocols/ec25519_fhmqvc/handshake.c +++ b/src/protocols/ec25519_fhmqvc/handshake.c @@ -126,7 +126,7 @@ static bool establish(fastd_context_t *ctx, fastd_peer_t *peer, const char *meth return false; } - const fastd_method_t *method = fastd_method_get_by_name(ctx, method_name); + const fastd_method_t *method = fastd_method_get_by_name(method_name); if (!salt && !method->session_init_compat) { pr_warn(ctx, "can't establish session with %P[%I] (method without compat support)"); return false; -- cgit v1.2.3