From 095ca93d81cfae0fcb137919231810577c556404 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 28 Oct 2013 16:59:42 +0100 Subject: Organize method and protocol source files into distinct source directories --- CMakeLists.txt | 11 + src/CMakeLists.txt | 34 +- src/fastd.c | 1 + src/handshake.c | 1 + src/method_aes128_gcm.c | 317 ------- src/method_null.c | 96 -- src/method_xsalsa20_poly1305.c | 247 ----- src/methods/CMakeLists.txt | 19 + src/methods/aes128_gcm/CMakeLists.txt | 6 + src/methods/aes128_gcm/aes128_gcm.c | 317 +++++++ src/methods/null/CMakeLists.txt | 6 + src/methods/null/null.c | 96 ++ src/methods/xsalsa20_poly1305/CMakeLists.txt | 6 + src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c | 248 +++++ src/options.c | 1 + src/protocol_ec25519_fhmqvc.c | 1015 --------------------- src/protocols/CMakeLists.txt | 10 + src/protocols/ec25519_fhmqvc/CMakeLists.txt | 6 + src/protocols/ec25519_fhmqvc/ec25519_fhmqvc.c | 1014 ++++++++++++++++++++ src/types.h | 1 - 20 files changed, 1752 insertions(+), 1700 deletions(-) delete mode 100644 src/method_aes128_gcm.c delete mode 100644 src/method_null.c delete mode 100644 src/method_xsalsa20_poly1305.c create mode 100644 src/methods/CMakeLists.txt create mode 100644 src/methods/aes128_gcm/CMakeLists.txt create mode 100644 src/methods/aes128_gcm/aes128_gcm.c create mode 100644 src/methods/null/CMakeLists.txt create mode 100644 src/methods/null/null.c create mode 100644 src/methods/xsalsa20_poly1305/CMakeLists.txt create mode 100644 src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c delete mode 100644 src/protocol_ec25519_fhmqvc.c create mode 100644 src/protocols/CMakeLists.txt create mode 100644 src/protocols/ec25519_fhmqvc/CMakeLists.txt create mode 100644 src/protocols/ec25519_fhmqvc/ec25519_fhmqvc.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4490bc7..910ba33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,4 +145,15 @@ string(COMPARE NOTEQUAL "${SIZEOF_ETHHDR}" "" HAVE_ETHHDR) configure_file(${FASTD_SOURCE_DIR}/config.h.in ${FASTD_BINARY_DIR}/config.h) +add_custom_target( + version + COMMAND echo "#ifndef _FASTD_VERSION_H_" > ${FASTD_BINARY_DIR}/version.h.new + COMMAND echo "#define _FASTD_VERSION_H_" >> ${FASTD_BINARY_DIR}/version.h.new + COMMAND sh -c "echo \"#define FASTD_VERSION \\\"$(git --git-dir=./.git describe --dirty 2>/dev/null || echo ${FASTD_VERSION})\\\"\"" >> ${FASTD_BINARY_DIR}/version.h.new + COMMAND echo "#endif /* _FASTD_VERSION_H_ */" >> ${FASTD_BINARY_DIR}/version.h.new + COMMAND cmp -s ${FASTD_BINARY_DIR}/version.h.new ${FASTD_BINARY_DIR}/version.h && rm ${FASTD_BINARY_DIR}/version.h.new || mv ${FASTD_BINARY_DIR}/version.h.new ${FASTD_BINARY_DIR}/version.h + WORKING_DIRECTORY "${FASTD_SOURCE_DIR}" + VERBATIM +) + add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3e962f7..99ff006 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,15 +1,13 @@ -set(METHODS method_null.c) +set_directory_properties(PROPERTIES COMPILE_DEFINITIONS _GNU_SOURCE) +set(FASTD_CFLAGS "-Wall -pthread ${UECC_CFLAGS_OTHER}") -if(WITH_METHOD_XSALSA20_POLY1305) - list(APPEND METHODS method_xsalsa20_poly1305.c) -endif(WITH_METHOD_XSALSA20_POLY1305) +include_directories(BEFORE ${FASTD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CAP_INCLUDE_DIR} ${NACL_INCLUDE_DIR}) +link_directories(${UECC_LIBRARY_DIRS}) -if(WITH_METHOD_AES128_GCM) - list(APPEND METHODS method_aes128_gcm.c) -endif(WITH_METHOD_AES128_GCM) -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${FASTD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CAP_INCLUDE_DIR} ${UECC_INCLUDE_DIRS} ${NACL_INCLUDE_DIR}) -link_directories(${UECC_LIBRARY_DIRS}) +add_subdirectory(protocols) +add_subdirectory(methods) + BISON_TARGET(fastd_config_parse config.y ${CMAKE_CURRENT_BINARY_DIR}/config.yy.c) @@ -32,26 +30,14 @@ add_executable(fastd shell.c socket.c tuntap.c - protocol_ec25519_fhmqvc.c ${BISON_fastd_config_parse_OUTPUTS} - ${METHODS} + ${PROTOCOL_OBJECTS} + ${METHOD_OBJECTS} ) -set_property(TARGET fastd PROPERTY COMPILE_FLAGS "-Wall -pthread ${UECC_CFLAGS_OTHER}") -set_property(TARGET fastd PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE) +set_property(TARGET fastd PROPERTY COMPILE_FLAGS "${FASTD_CFLAGS}") set_property(TARGET fastd PROPERTY LINK_FLAGS "-pthread ${UECC_LDFLAGS_OTHER}") target_link_libraries(fastd ${RT_LIBRARY} ${CAP_LIBRARY} ${UECC_LIBRARIES} ${NACL_LIBRARY}) -add_custom_target( - version - COMMAND echo "#ifndef _FASTD_VERSION_H_" > ${CMAKE_CURRENT_BINARY_DIR}/version.h.new - COMMAND echo "#define _FASTD_VERSION_H_" >> ${CMAKE_CURRENT_BINARY_DIR}/version.h.new - COMMAND sh -c "echo \"#define FASTD_VERSION \\\"$(git --git-dir=./.git describe --dirty 2>/dev/null || echo ${FASTD_VERSION})\\\"\"" >> ${CMAKE_CURRENT_BINARY_DIR}/version.h.new - COMMAND echo "#endif /* _FASTD_VERSION_H_ */" >> ${CMAKE_CURRENT_BINARY_DIR}/version.h.new - COMMAND cmp -s ${CMAKE_CURRENT_BINARY_DIR}/version.h.new ${CMAKE_CURRENT_BINARY_DIR}/version.h && rm ${CMAKE_CURRENT_BINARY_DIR}/version.h.new || mv ${CMAKE_CURRENT_BINARY_DIR}/version.h.new ${CMAKE_CURRENT_BINARY_DIR}/version.h - WORKING_DIRECTORY "${FASTD_SOURCE_DIR}" - VERBATIM -) - add_dependencies(fastd version) install(TARGETS fastd RUNTIME DESTINATION bin) diff --git a/src/fastd.c b/src/fastd.c index f281744..c434216 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -28,6 +28,7 @@ #include "crypto.h" #include "handshake.h" #include "peer.h" +#include #include #include diff --git a/src/handshake.c b/src/handshake.c index 070c959..2986b90 100644 --- a/src/handshake.c +++ b/src/handshake.c @@ -26,6 +26,7 @@ #include "handshake.h" #include "peer.h" +#include static const char *const RECORD_TYPES[RECORD_MAX] = { diff --git a/src/method_aes128_gcm.c b/src/method_aes128_gcm.c deleted file mode 100644 index 1b91925..0000000 --- a/src/method_aes128_gcm.c +++ /dev/null @@ -1,317 +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" - - -#define KEYBYTES 16 -#define NONCEBYTES 7 - -struct fastd_method_session_state { - struct timespec valid_till; - struct timespec refresh_after; - - uint8_t send_nonce[NONCEBYTES]; - uint8_t receive_nonce[NONCEBYTES]; - - struct timespec receive_last; - uint64_t receive_reorder_seen; - - fastd_crypto_aes128ctr_state_t *cstate_aes128ctr; - fastd_crypto_ghash_state_t *cstate_ghash; -}; - - -static inline void increment_nonce(uint8_t nonce[NONCEBYTES]) { - nonce[0] += 2; - - if (nonce[0] == 0 || nonce[0] == 1) { - int i; - for (i = 1; i < NONCEBYTES; i++) { - nonce[i]++; - if (nonce[i] != 0) - break; - } - } -} - -static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES], int64_t *age) { - if ((nonce[0] & 1) != (old_nonce[0] & 1)) - return false; - - int i; - *age = 0; - - for (i = NONCEBYTES-1; i >= 0; i--) { - *age *= 256; - *age += old_nonce[i]-nonce[i]; - } - - *age /= 2; - return true; -} - -static size_t method_max_packet_size(fastd_context_t *ctx) { - return (fastd_max_packet_size(ctx) + 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 fastd_method_session_state_t* method_session_init(fastd_context_t *ctx, uint8_t *secret, size_t length, bool initiator) { - int i; - - if (length < KEYBYTES) - exit_bug(ctx, "aes128-gcm: tried to init with short secret"); - - fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); - - session->valid_till = ctx->now; - session->valid_till.tv_sec += ctx->conf->key_valid; - - session->refresh_after = ctx->now; - session->refresh_after.tv_sec += ctx->conf->key_refresh - fastd_rand(ctx, 0, ctx->conf->key_refresh_splay); - - fastd_block128_t key; - memcpy(key.b, secret, sizeof(fastd_block128_t)); - session->cstate_aes128ctr = ctx->conf->crypto_aes128ctr->set_key(ctx, ctx->crypto_aes128ctr, &key); - - static const fastd_block128_t zeroblock = {}; - fastd_block128_t H; - - ctx->conf->crypto_aes128ctr->crypt(ctx, session->cstate_aes128ctr, &H, &zeroblock, sizeof(fastd_block128_t), &zeroblock); - - session->cstate_ghash = ctx->conf->crypto_ghash->set_h(ctx, ctx->crypto_ghash, &H); - - session->send_nonce[0] = initiator ? 3 : 2; - session->receive_nonce[0] = initiator ? 0 : 1; - - for (i = 1; i < NONCEBYTES; i++) { - session->send_nonce[i] = 0; - session->receive_nonce[i] = 0; - } - - return session; -} - -static bool method_session_is_valid(fastd_context_t *ctx, fastd_method_session_state_t *session) { - return (session && timespec_after(&session->valid_till, &ctx->now)); -} - -static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - return (session->send_nonce[0] & 1); -} - -static bool method_session_want_refresh(fastd_context_t *ctx, fastd_method_session_state_t *session) { - return timespec_after(&ctx->now, &session->refresh_after); -} - -static void method_session_superseded(fastd_context_t *ctx, fastd_method_session_state_t *session) { - struct timespec valid_max = ctx->now; - valid_max.tv_sec += ctx->conf->key_valid_old; - - if (timespec_after(&session->valid_till, &valid_max)) - session->valid_till = valid_max; -} - -static void method_session_free(fastd_context_t *ctx, fastd_method_session_state_t *session) { - if(session) { - ctx->conf->crypto_aes128ctr->free_state(ctx, session->cstate_aes128ctr); - 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(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->send_nonce, NONCEBYTES); - memset(nonce.b+NONCEBYTES, 0, sizeof(fastd_block128_t)-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 = ctx->conf->crypto_aes128ctr->crypt(ctx, session->cstate_aes128ctr, 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, NONCEBYTES); - memcpy(out->data, session->send_nonce, NONCEBYTES); - increment_nonce(session->send_nonce); - - 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 < 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, NONCEBYTES); - memset(nonce.b+NONCEBYTES, 0, sizeof(fastd_block128_t)-NONCEBYTES-1); - nonce.b[sizeof(fastd_block128_t)-1] = 1; - - int64_t age; - if (!is_nonce_valid(nonce.b, session->receive_nonce, &age)) - return false; - - if (age >= 0) { - if (timespec_diff(&ctx->now, &session->receive_last) > (int)ctx->conf->reorder_time*1000) - return false; - - if (age > ctx->conf->reorder_count) - return false; - } - - fastd_buffer_push_head(ctx, &in, 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 = ctx->conf->crypto_aes128ctr->crypt(ctx, session->cstate_aes128ctr, 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 (age < 0) { - session->receive_reorder_seen >>= age; - session->receive_reorder_seen |= (1 >> (age+1)); - memcpy(session->receive_nonce, nonce.b, NONCEBYTES); - session->receive_last = ctx->now; - } - else if (age == 0 || session->receive_reorder_seen & (1 << (age-1))) { - pr_debug(ctx, "dropping duplicate packet from %P (age %u)", peer, (unsigned)age); - fastd_buffer_free(*out); - *out = fastd_buffer_alloc(ctx, 0, 0, 0); - } - else { - pr_debug2(ctx, "accepting reordered packet from %P (age %u)", peer, (unsigned)age); - session->receive_reorder_seen |= (1 << (age-1)); - } - - return true; -} - -const fastd_method_t fastd_method_aes128_gcm = { - .name = "aes128-gcm", - - .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, - - .session_init = method_session_init, - .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/method_null.c b/src/method_null.c deleted file mode 100644 index a978cb1..0000000 --- a/src/method_null.c +++ /dev/null @@ -1,96 +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" - - -struct fastd_method_session_state { - bool valid; - bool initiator; -}; - - -static size_t method_max_packet_size(fastd_context_t *ctx) { - return fastd_max_packet_size(ctx); -} - -static size_t method_min_head_tail_space(fastd_context_t *ctx UNUSED) { - return 0; -} - -static fastd_method_session_state_t* method_session_init(fastd_context_t *ctx UNUSED, uint8_t *secret UNUSED, size_t length UNUSED, bool initiator) { - fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); - - session->valid = true; - session->initiator = initiator; - - return session; -} - -static bool method_session_is_valid(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - return (session && session->valid); -} - -static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - return (session->initiator); -} - -static bool method_session_want_refresh(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session UNUSED) { - return false; -} - -static void method_session_superseded(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - session->valid = false; -} - -static void method_session_free(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - free(session); -} - -static bool method_passthrough(fastd_context_t *ctx UNUSED, fastd_peer_t *peer UNUSED, fastd_method_session_state_t *session UNUSED, fastd_buffer_t *out, fastd_buffer_t in) { - *out = in; - return true; -} - -const fastd_method_t fastd_method_null = { - .name = "null", - - .max_packet_size = method_max_packet_size, - .min_encrypt_head_space = method_min_head_tail_space, - .min_decrypt_head_space = method_min_head_tail_space, - .min_encrypt_tail_space = method_min_head_tail_space, - .min_decrypt_tail_space = method_min_head_tail_space, - - .session_init = method_session_init, - .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_passthrough, - .decrypt = method_passthrough, -}; diff --git a/src/method_xsalsa20_poly1305.c b/src/method_xsalsa20_poly1305.c deleted file mode 100644 index 395f322..0000000 --- a/src/method_xsalsa20_poly1305.c +++ /dev/null @@ -1,247 +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 - - -#define NONCEBYTES 7 - - -struct fastd_method_session_state { - struct timespec valid_till; - struct timespec refresh_after; - - uint8_t key[crypto_secretbox_xsalsa20poly1305_KEYBYTES]; - - uint8_t send_nonce[NONCEBYTES]; - uint8_t receive_nonce[NONCEBYTES]; - - struct timespec receive_last; - uint64_t receive_reorder_seen; -}; - - -static inline void increment_nonce(uint8_t nonce[NONCEBYTES]) { - nonce[0] += 2; - - if (nonce[0] == 0 || nonce[0] == 1) { - int i; - for (i = 1; i < NONCEBYTES; i++) { - nonce[i]++; - if (nonce[i] != 0) - break; - } - } -} - -static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES], int64_t *age) { - if ((nonce[0] & 1) != (old_nonce[0] & 1)) - return false; - - int i; - *age = 0; - - for (i = NONCEBYTES-1; i >= 0; i--) { - *age *= 256; - *age += old_nonce[i]-nonce[i]; - } - - *age /= 2; - return true; -} - -static size_t method_max_packet_size(fastd_context_t *ctx) { - return (fastd_max_packet_size(ctx) + NONCEBYTES + crypto_secretbox_xsalsa20poly1305_ZEROBYTES - crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); -} - -static size_t method_min_encrypt_head_space(fastd_context_t *ctx UNUSED) { - return crypto_secretbox_xsalsa20poly1305_ZEROBYTES; -} - -static size_t method_min_decrypt_head_space(fastd_context_t *ctx UNUSED) { - return (crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES - NONCEBYTES); -} - -static size_t method_min_tail_space(fastd_context_t *ctx UNUSED) { - return 0; -} - -static fastd_method_session_state_t* method_session_init(fastd_context_t *ctx, uint8_t *secret, size_t length, bool initiator) { - int i; - - if (length < crypto_secretbox_xsalsa20poly1305_KEYBYTES) - exit_bug(ctx, "xsalsa20-poly1305: tried to init with short secret"); - - fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); - - session->valid_till = ctx->now; - session->valid_till.tv_sec += ctx->conf->key_valid; - - session->refresh_after = ctx->now; - session->refresh_after.tv_sec += ctx->conf->key_refresh - fastd_rand(ctx, 0, ctx->conf->key_refresh_splay); - - memcpy(session->key, secret, crypto_secretbox_xsalsa20poly1305_KEYBYTES); - - session->send_nonce[0] = initiator ? 3 : 2; - session->receive_nonce[0] = initiator ? 0 : 1; - - for (i = 1; i < NONCEBYTES; i++) { - session->send_nonce[i] = 0; - session->receive_nonce[i] = 0; - } - - return session; -} - -static bool method_session_is_valid(fastd_context_t *ctx, fastd_method_session_state_t *session) { - return (session && timespec_after(&session->valid_till, &ctx->now)); -} - -static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - return (session->send_nonce[0] & 1); -} - -static bool method_session_want_refresh(fastd_context_t *ctx, fastd_method_session_state_t *session) { - return timespec_after(&ctx->now, &session->refresh_after); -} - -static void method_session_superseded(fastd_context_t *ctx, fastd_method_session_state_t *session) { - struct timespec valid_max = ctx->now; - valid_max.tv_sec += ctx->conf->key_valid_old; - - if (timespec_after(&session->valid_till, &valid_max)) - session->valid_till = valid_max; -} - -static void method_session_free(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { - if(session) { - secure_memzero(session, sizeof(fastd_method_session_state_t)); - free(session); - } -} - -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, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); - memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); - - *out = fastd_buffer_alloc(ctx, in.len, 0, 0); - - uint8_t nonce[crypto_secretbox_xsalsa20poly1305_NONCEBYTES]; - memcpy(nonce, session->send_nonce, NONCEBYTES); - memset(nonce+NONCEBYTES, 0, crypto_secretbox_xsalsa20poly1305_NONCEBYTES-NONCEBYTES); - - crypto_secretbox_xsalsa20poly1305(out->data, in.data, in.len, nonce, session->key); - - fastd_buffer_free(in); - - fastd_buffer_push_head(ctx, out, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); - memcpy(out->data, session->send_nonce, NONCEBYTES); - - increment_nonce(session->send_nonce); - - 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 < NONCEBYTES) - return false; - - if (!method_session_is_valid(ctx, session)) - return false; - - uint8_t nonce[crypto_secretbox_xsalsa20poly1305_NONCEBYTES]; - memcpy(nonce, in.data, NONCEBYTES); - memset(nonce+NONCEBYTES, 0, crypto_secretbox_xsalsa20poly1305_NONCEBYTES-NONCEBYTES); - - int64_t age; - if (!is_nonce_valid(nonce, session->receive_nonce, &age)) - return false; - - if (age >= 0) { - if (timespec_diff(&ctx->now, &session->receive_last) > (int)ctx->conf->reorder_time*1000) - return false; - - if (age > ctx->conf->reorder_count) - return false; - } - - fastd_buffer_pull_head(ctx, &in, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); - memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); - - *out = fastd_buffer_alloc(ctx, in.len, 0, 0); - - if (crypto_secretbox_xsalsa20poly1305_open(out->data, in.data, in.len, nonce, session->key) != 0) { - fastd_buffer_free(*out); - - /* restore input buffer */ - fastd_buffer_push_head(ctx, &in, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); - memcpy(in.data, nonce, NONCEBYTES); - return false; - } - - fastd_buffer_free(in); - - if (age < 0) { - session->receive_reorder_seen >>= age; - session->receive_reorder_seen |= (1 >> (age+1)); - memcpy(session->receive_nonce, nonce, NONCEBYTES); - session->receive_last = ctx->now; - } - else if (age == 0 || session->receive_reorder_seen & (1 << (age-1))) { - pr_debug(ctx, "dropping duplicate packet from %P (age %u)", peer, (unsigned)age); - fastd_buffer_free(*out); - *out = fastd_buffer_alloc(ctx, crypto_secretbox_xsalsa20poly1305_ZEROBYTES, 0, 0); - } - else { - pr_debug2(ctx, "accepting reordered packet from %P (age %u)", peer, (unsigned)age); - session->receive_reorder_seen |= (1 << (age-1)); - } - - fastd_buffer_push_head(ctx, out, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); - - return true; -} - -const fastd_method_t fastd_method_xsalsa20_poly1305 = { - .name = "xsalsa20-poly1305", - - .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_tail_space, - .min_decrypt_tail_space = method_min_tail_space, - - .session_init = method_session_init, - .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/CMakeLists.txt b/src/methods/CMakeLists.txt new file mode 100644 index 0000000..55f41d2 --- /dev/null +++ b/src/methods/CMakeLists.txt @@ -0,0 +1,19 @@ +set(METHODS null) + +if(WITH_METHOD_XSALSA20_POLY1305) + list(APPEND METHODS xsalsa20_poly1305) +endif(WITH_METHOD_XSALSA20_POLY1305) + +if(WITH_METHOD_AES128_GCM) + list(APPEND METHODS aes128_gcm) +endif(WITH_METHOD_AES128_GCM) + + +set(METHOD_OBJECTS "") + +foreach(method ${METHODS}) + add_subdirectory(${method}) + list(APPEND METHOD_OBJECTS $) +endforeach(method) + +set(METHOD_OBJECTS "${METHOD_OBJECTS}" PARENT_SCOPE) diff --git a/src/methods/aes128_gcm/CMakeLists.txt b/src/methods/aes128_gcm/CMakeLists.txt new file mode 100644 index 0000000..1c5aa3f --- /dev/null +++ b/src/methods/aes128_gcm/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(BEFORE ${FASTD_SOURCE_DIR}/src ${FASTD_BINARY_DIR} ${NACL_INCLUDE_DIR}) + +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 new file mode 100644 index 0000000..46dba5c --- /dev/null +++ b/src/methods/aes128_gcm/aes128_gcm.c @@ -0,0 +1,317 @@ +/* + 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 +#include + + +#define KEYBYTES 16 +#define NONCEBYTES 7 + +struct fastd_method_session_state { + struct timespec valid_till; + struct timespec refresh_after; + + uint8_t send_nonce[NONCEBYTES]; + uint8_t receive_nonce[NONCEBYTES]; + + struct timespec receive_last; + uint64_t receive_reorder_seen; + + fastd_crypto_aes128ctr_state_t *cstate_aes128ctr; + fastd_crypto_ghash_state_t *cstate_ghash; +}; + + +static inline void increment_nonce(uint8_t nonce[NONCEBYTES]) { + nonce[0] += 2; + + if (nonce[0] == 0 || nonce[0] == 1) { + int i; + for (i = 1; i < NONCEBYTES; i++) { + nonce[i]++; + if (nonce[i] != 0) + break; + } + } +} + +static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES], int64_t *age) { + if ((nonce[0] & 1) != (old_nonce[0] & 1)) + return false; + + int i; + *age = 0; + + for (i = NONCEBYTES-1; i >= 0; i--) { + *age *= 256; + *age += old_nonce[i]-nonce[i]; + } + + *age /= 2; + return true; +} + +static size_t method_max_packet_size(fastd_context_t *ctx) { + return (fastd_max_packet_size(ctx) + 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 fastd_method_session_state_t* method_session_init(fastd_context_t *ctx, uint8_t *secret, size_t length, bool initiator) { + int i; + + if (length < KEYBYTES) + exit_bug(ctx, "aes128-gcm: tried to init with short secret"); + + fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); + + session->valid_till = ctx->now; + session->valid_till.tv_sec += ctx->conf->key_valid; + + session->refresh_after = ctx->now; + session->refresh_after.tv_sec += ctx->conf->key_refresh - fastd_rand(ctx, 0, ctx->conf->key_refresh_splay); + + fastd_block128_t key; + memcpy(key.b, secret, sizeof(fastd_block128_t)); + session->cstate_aes128ctr = ctx->conf->crypto_aes128ctr->set_key(ctx, ctx->crypto_aes128ctr, &key); + + static const fastd_block128_t zeroblock = {}; + fastd_block128_t H; + + ctx->conf->crypto_aes128ctr->crypt(ctx, session->cstate_aes128ctr, &H, &zeroblock, sizeof(fastd_block128_t), &zeroblock); + + session->cstate_ghash = ctx->conf->crypto_ghash->set_h(ctx, ctx->crypto_ghash, &H); + + session->send_nonce[0] = initiator ? 3 : 2; + session->receive_nonce[0] = initiator ? 0 : 1; + + for (i = 1; i < NONCEBYTES; i++) { + session->send_nonce[i] = 0; + session->receive_nonce[i] = 0; + } + + return session; +} + +static bool method_session_is_valid(fastd_context_t *ctx, fastd_method_session_state_t *session) { + return (session && timespec_after(&session->valid_till, &ctx->now)); +} + +static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + return (session->send_nonce[0] & 1); +} + +static bool method_session_want_refresh(fastd_context_t *ctx, fastd_method_session_state_t *session) { + return timespec_after(&ctx->now, &session->refresh_after); +} + +static void method_session_superseded(fastd_context_t *ctx, fastd_method_session_state_t *session) { + struct timespec valid_max = ctx->now; + valid_max.tv_sec += ctx->conf->key_valid_old; + + if (timespec_after(&session->valid_till, &valid_max)) + session->valid_till = valid_max; +} + +static void method_session_free(fastd_context_t *ctx, fastd_method_session_state_t *session) { + if(session) { + ctx->conf->crypto_aes128ctr->free_state(ctx, session->cstate_aes128ctr); + 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(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->send_nonce, NONCEBYTES); + memset(nonce.b+NONCEBYTES, 0, sizeof(fastd_block128_t)-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 = ctx->conf->crypto_aes128ctr->crypt(ctx, session->cstate_aes128ctr, 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, NONCEBYTES); + memcpy(out->data, session->send_nonce, NONCEBYTES); + increment_nonce(session->send_nonce); + + 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 < 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, NONCEBYTES); + memset(nonce.b+NONCEBYTES, 0, sizeof(fastd_block128_t)-NONCEBYTES-1); + nonce.b[sizeof(fastd_block128_t)-1] = 1; + + int64_t age; + if (!is_nonce_valid(nonce.b, session->receive_nonce, &age)) + return false; + + if (age >= 0) { + if (timespec_diff(&ctx->now, &session->receive_last) > (int)ctx->conf->reorder_time*1000) + return false; + + if (age > ctx->conf->reorder_count) + return false; + } + + fastd_buffer_push_head(ctx, &in, 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 = ctx->conf->crypto_aes128ctr->crypt(ctx, session->cstate_aes128ctr, 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 (age < 0) { + session->receive_reorder_seen >>= age; + session->receive_reorder_seen |= (1 >> (age+1)); + memcpy(session->receive_nonce, nonce.b, NONCEBYTES); + session->receive_last = ctx->now; + } + else if (age == 0 || session->receive_reorder_seen & (1 << (age-1))) { + pr_debug(ctx, "dropping duplicate packet from %P (age %u)", peer, (unsigned)age); + fastd_buffer_free(*out); + *out = fastd_buffer_alloc(ctx, 0, 0, 0); + } + else { + pr_debug2(ctx, "accepting reordered packet from %P (age %u)", peer, (unsigned)age); + session->receive_reorder_seen |= (1 << (age-1)); + } + + return true; +} + +const fastd_method_t fastd_method_aes128_gcm = { + .name = "aes128-gcm", + + .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, + + .session_init = method_session_init, + .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/null/CMakeLists.txt b/src/methods/null/CMakeLists.txt new file mode 100644 index 0000000..91c1832 --- /dev/null +++ b/src/methods/null/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(BEFORE ${FASTD_SOURCE_DIR}/src ${FASTD_BINARY_DIR}) + +add_library(method_null OBJECT + null.c +) +set_property(TARGET method_null PROPERTY COMPILE_FLAGS "${FASTD_CFLAGS}") diff --git a/src/methods/null/null.c b/src/methods/null/null.c new file mode 100644 index 0000000..643e5e1 --- /dev/null +++ b/src/methods/null/null.c @@ -0,0 +1,96 @@ +/* + 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 + + +struct fastd_method_session_state { + bool valid; + bool initiator; +}; + + +static size_t method_max_packet_size(fastd_context_t *ctx) { + return fastd_max_packet_size(ctx); +} + +static size_t method_min_head_tail_space(fastd_context_t *ctx UNUSED) { + return 0; +} + +static fastd_method_session_state_t* method_session_init(fastd_context_t *ctx UNUSED, uint8_t *secret UNUSED, size_t length UNUSED, bool initiator) { + fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); + + session->valid = true; + session->initiator = initiator; + + return session; +} + +static bool method_session_is_valid(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + return (session && session->valid); +} + +static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + return (session->initiator); +} + +static bool method_session_want_refresh(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session UNUSED) { + return false; +} + +static void method_session_superseded(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + session->valid = false; +} + +static void method_session_free(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + free(session); +} + +static bool method_passthrough(fastd_context_t *ctx UNUSED, fastd_peer_t *peer UNUSED, fastd_method_session_state_t *session UNUSED, fastd_buffer_t *out, fastd_buffer_t in) { + *out = in; + return true; +} + +const fastd_method_t fastd_method_null = { + .name = "null", + + .max_packet_size = method_max_packet_size, + .min_encrypt_head_space = method_min_head_tail_space, + .min_decrypt_head_space = method_min_head_tail_space, + .min_encrypt_tail_space = method_min_head_tail_space, + .min_decrypt_tail_space = method_min_head_tail_space, + + .session_init = method_session_init, + .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_passthrough, + .decrypt = method_passthrough, +}; diff --git a/src/methods/xsalsa20_poly1305/CMakeLists.txt b/src/methods/xsalsa20_poly1305/CMakeLists.txt new file mode 100644 index 0000000..09b029c --- /dev/null +++ b/src/methods/xsalsa20_poly1305/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(BEFORE ${FASTD_SOURCE_DIR}/src ${FASTD_BINARY_DIR} ${NACL_INCLUDE_DIR}) + +add_library(method_xsalsa20_poly1305 OBJECT + xsalsa20_poly1305.c +) +set_property(TARGET method_xsalsa20_poly1305 PROPERTY COMPILE_FLAGS "${FASTD_CFLAGS}") diff --git a/src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c b/src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c new file mode 100644 index 0000000..d891aa5 --- /dev/null +++ b/src/methods/xsalsa20_poly1305/xsalsa20_poly1305.c @@ -0,0 +1,248 @@ +/* + 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 + +#include + + +#define NONCEBYTES 7 + + +struct fastd_method_session_state { + struct timespec valid_till; + struct timespec refresh_after; + + uint8_t key[crypto_secretbox_xsalsa20poly1305_KEYBYTES]; + + uint8_t send_nonce[NONCEBYTES]; + uint8_t receive_nonce[NONCEBYTES]; + + struct timespec receive_last; + uint64_t receive_reorder_seen; +}; + + +static inline void increment_nonce(uint8_t nonce[NONCEBYTES]) { + nonce[0] += 2; + + if (nonce[0] == 0 || nonce[0] == 1) { + int i; + for (i = 1; i < NONCEBYTES; i++) { + nonce[i]++; + if (nonce[i] != 0) + break; + } + } +} + +static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES], int64_t *age) { + if ((nonce[0] & 1) != (old_nonce[0] & 1)) + return false; + + int i; + *age = 0; + + for (i = NONCEBYTES-1; i >= 0; i--) { + *age *= 256; + *age += old_nonce[i]-nonce[i]; + } + + *age /= 2; + return true; +} + +static size_t method_max_packet_size(fastd_context_t *ctx) { + return (fastd_max_packet_size(ctx) + NONCEBYTES + crypto_secretbox_xsalsa20poly1305_ZEROBYTES - crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); +} + +static size_t method_min_encrypt_head_space(fastd_context_t *ctx UNUSED) { + return crypto_secretbox_xsalsa20poly1305_ZEROBYTES; +} + +static size_t method_min_decrypt_head_space(fastd_context_t *ctx UNUSED) { + return (crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES - NONCEBYTES); +} + +static size_t method_min_tail_space(fastd_context_t *ctx UNUSED) { + return 0; +} + +static fastd_method_session_state_t* method_session_init(fastd_context_t *ctx, uint8_t *secret, size_t length, bool initiator) { + int i; + + if (length < crypto_secretbox_xsalsa20poly1305_KEYBYTES) + exit_bug(ctx, "xsalsa20-poly1305: tried to init with short secret"); + + fastd_method_session_state_t *session = malloc(sizeof(fastd_method_session_state_t)); + + session->valid_till = ctx->now; + session->valid_till.tv_sec += ctx->conf->key_valid; + + session->refresh_after = ctx->now; + session->refresh_after.tv_sec += ctx->conf->key_refresh - fastd_rand(ctx, 0, ctx->conf->key_refresh_splay); + + memcpy(session->key, secret, crypto_secretbox_xsalsa20poly1305_KEYBYTES); + + session->send_nonce[0] = initiator ? 3 : 2; + session->receive_nonce[0] = initiator ? 0 : 1; + + for (i = 1; i < NONCEBYTES; i++) { + session->send_nonce[i] = 0; + session->receive_nonce[i] = 0; + } + + return session; +} + +static bool method_session_is_valid(fastd_context_t *ctx, fastd_method_session_state_t *session) { + return (session && timespec_after(&session->valid_till, &ctx->now)); +} + +static bool method_session_is_initiator(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + return (session->send_nonce[0] & 1); +} + +static bool method_session_want_refresh(fastd_context_t *ctx, fastd_method_session_state_t *session) { + return timespec_after(&ctx->now, &session->refresh_after); +} + +static void method_session_superseded(fastd_context_t *ctx, fastd_method_session_state_t *session) { + struct timespec valid_max = ctx->now; + valid_max.tv_sec += ctx->conf->key_valid_old; + + if (timespec_after(&session->valid_till, &valid_max)) + session->valid_till = valid_max; +} + +static void method_session_free(fastd_context_t *ctx UNUSED, fastd_method_session_state_t *session) { + if(session) { + secure_memzero(session, sizeof(fastd_method_session_state_t)); + free(session); + } +} + +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, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); + memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); + + *out = fastd_buffer_alloc(ctx, in.len, 0, 0); + + uint8_t nonce[crypto_secretbox_xsalsa20poly1305_NONCEBYTES]; + memcpy(nonce, session->send_nonce, NONCEBYTES); + memset(nonce+NONCEBYTES, 0, crypto_secretbox_xsalsa20poly1305_NONCEBYTES-NONCEBYTES); + + crypto_secretbox_xsalsa20poly1305(out->data, in.data, in.len, nonce, session->key); + + fastd_buffer_free(in); + + fastd_buffer_push_head(ctx, out, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); + memcpy(out->data, session->send_nonce, NONCEBYTES); + + increment_nonce(session->send_nonce); + + 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 < NONCEBYTES) + return false; + + if (!method_session_is_valid(ctx, session)) + return false; + + uint8_t nonce[crypto_secretbox_xsalsa20poly1305_NONCEBYTES]; + memcpy(nonce, in.data, NONCEBYTES); + memset(nonce+NONCEBYTES, 0, crypto_secretbox_xsalsa20poly1305_NONCEBYTES-NONCEBYTES); + + int64_t age; + if (!is_nonce_valid(nonce, session->receive_nonce, &age)) + return false; + + if (age >= 0) { + if (timespec_diff(&ctx->now, &session->receive_last) > (int)ctx->conf->reorder_time*1000) + return false; + + if (age > ctx->conf->reorder_count) + return false; + } + + fastd_buffer_pull_head(ctx, &in, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); + memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); + + *out = fastd_buffer_alloc(ctx, in.len, 0, 0); + + if (crypto_secretbox_xsalsa20poly1305_open(out->data, in.data, in.len, nonce, session->key) != 0) { + fastd_buffer_free(*out); + + /* restore input buffer */ + fastd_buffer_push_head(ctx, &in, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); + memcpy(in.data, nonce, NONCEBYTES); + return false; + } + + fastd_buffer_free(in); + + if (age < 0) { + session->receive_reorder_seen >>= age; + session->receive_reorder_seen |= (1 >> (age+1)); + memcpy(session->receive_nonce, nonce, NONCEBYTES); + session->receive_last = ctx->now; + } + else if (age == 0 || session->receive_reorder_seen & (1 << (age-1))) { + pr_debug(ctx, "dropping duplicate packet from %P (age %u)", peer, (unsigned)age); + fastd_buffer_free(*out); + *out = fastd_buffer_alloc(ctx, crypto_secretbox_xsalsa20poly1305_ZEROBYTES, 0, 0); + } + else { + pr_debug2(ctx, "accepting reordered packet from %P (age %u)", peer, (unsigned)age); + session->receive_reorder_seen |= (1 << (age-1)); + } + + fastd_buffer_push_head(ctx, out, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); + + return true; +} + +const fastd_method_t fastd_method_xsalsa20_poly1305 = { + .name = "xsalsa20-poly1305", + + .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_tail_space, + .min_decrypt_tail_space = method_min_tail_space, + + .session_init = method_session_init, + .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/options.c b/src/options.c index 1462b9f..8d93bf9 100644 --- a/src/options.c +++ b/src/options.c @@ -26,6 +26,7 @@ #include "fastd.h" #include "peer.h" +#include #include diff --git a/src/protocol_ec25519_fhmqvc.c b/src/protocol_ec25519_fhmqvc.c deleted file mode 100644 index b7b5162..0000000 --- a/src/protocol_ec25519_fhmqvc.c +++ /dev/null @@ -1,1015 +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 "handshake.h" -#include "peer.h" -#include "sha256.h" - - -#include - - -#define PUBLICKEYBYTES 32 -#define SECRETKEYBYTES 32 -#define HASHBYTES FASTD_SHA256_HASH_BYTES - - -#if HASHBYTES != FASTD_HMACSHA256_KEY_BYTES -#error bug: HASHBYTES != FASTD_HMACSHA256_KEY_BYTES -#endif - -#if HASHBYTES != SECRETKEYBYTES -#error bug: HASHBYTES != SECRETKEYBYTES -#endif - - -typedef ecc_int256_t __attribute__((aligned(4))) aligned_int256_t; - -typedef struct keypair { - ecc_int256_t secret; - aligned_int256_t public; -} keypair_t; - -struct fastd_protocol_config { - keypair_t key; -}; - -typedef struct handshake_key { - uint64_t serial; - struct timespec preferred_till; - struct timespec valid_till; - - /* keypair used as initiator */ - keypair_t key1; - - /* keypair used as responder */ - keypair_t key2; -} handshake_key_t; - -struct fastd_protocol_state { - handshake_key_t prev_handshake_key; - handshake_key_t handshake_key; -}; - -struct fastd_protocol_peer_config { - aligned_int256_t public_key; -}; - -typedef struct protocol_session { - struct timespec established; - - bool handshakes_cleaned; - bool refreshing; - - const fastd_method_t *method; - fastd_method_session_state_t *method_state; -} protocol_session_t; - -struct fastd_protocol_peer_state { - protocol_session_t old_session; - protocol_session_t session; - - uint64_t last_serial; - - /* handshake cache */ - uint64_t last_handshake_serial; - aligned_int256_t peer_handshake_key; - aligned_int256_t sigma; - fastd_sha256_t shared_handshake_key; -}; - - -#define RECORD_SENDER_KEY RECORD_PROTOCOL1 -#define RECORD_RECEIPIENT_KEY RECORD_PROTOCOL2 -#define RECORD_SENDER_HANDSHAKE_KEY RECORD_PROTOCOL3 -#define RECORD_RECEIPIENT_HANDSHAKE_KEY RECORD_PROTOCOL4 -#define RECORD_T RECORD_PROTOCOL5 - - -static void send_empty(fastd_context_t *ctx, fastd_peer_t *peer, protocol_session_t *session); - - -static inline bool read_key(uint8_t key[32], const char *hexkey) { - if ((strlen(hexkey) != 64) || (strspn(hexkey, "0123456789abcdefABCDEF") != 64)) - return false; - - int i; - for (i = 0; i < 32; i++) - sscanf(&hexkey[2*i], "%02hhx", &key[i]); - - return true; -} - -static inline bool is_handshake_key_valid(fastd_context_t *ctx, const handshake_key_t *handshake_key) { - return timespec_after(&handshake_key->valid_till, &ctx->now); -} - -static inline bool is_handshake_key_preferred(fastd_context_t *ctx, const handshake_key_t *handshake_key) { - return timespec_after(&handshake_key->preferred_till, &ctx->now); -} - -static inline bool is_session_valid(fastd_context_t *ctx, const protocol_session_t *session) { - return (session->method && session->method->session_is_valid(ctx, session->method_state)); -} - -static bool backoff(fastd_context_t *ctx, const fastd_peer_t *peer) { - return (peer->protocol_state && is_session_valid(ctx, &peer->protocol_state->session) - && timespec_diff(&ctx->now, &peer->protocol_state->session.established) < 15000); -} - -static inline void check_session_refresh(fastd_context_t *ctx, fastd_peer_t *peer) { - protocol_session_t *session = &peer->protocol_state->session; - - if (!session->refreshing && session->method->session_is_initiator(ctx, session->method_state) && session->method->session_want_refresh(ctx, session->method_state)) { - pr_verbose(ctx, "refreshing session with %P", peer); - session->handshakes_cleaned = true; - session->refreshing = true; - fastd_peer_schedule_handshake(ctx, peer, 0); - } -} - -static fastd_protocol_config_t* protocol_init(fastd_context_t *ctx) { - fastd_protocol_config_t *protocol_config = malloc(sizeof(fastd_protocol_config_t)); - - if (!ctx->conf->secret) - exit_error(ctx, "no secret key configured"); - - if (!read_key(protocol_config->key.secret.p, ctx->conf->secret)) - exit_error(ctx, "invalid secret key"); - - ecc_25519_work_t work; - ecc_25519_scalarmult_base(&work, &protocol_config->key.secret); - ecc_25519_store_packed(&protocol_config->key.public, &work); - - return protocol_config; -} - -static inline void hexdump(char out[65], const unsigned char d[32]) { - int i; - for (i = 0; i < 32; i++) - snprintf(out+2*i, 3, "%02x", d[i]); -} - -static size_t key_count(fastd_context_t *ctx, const unsigned char key[32]) { - size_t ret = 0; - - fastd_peer_config_t *p; - for (p = ctx->conf->peers; p; p = p->next) { - if (!p->protocol_config) - continue; - - if (memcmp(p->protocol_config->public_key.p, key, 32) == 0) - ret++; - } - - return ret; -} - -static void protocol_peer_configure(fastd_context_t *ctx, fastd_peer_config_t *peer_conf) { - if (peer_conf->protocol_config) - return; - - if (!peer_conf->key) { - pr_warn(ctx, "no key configured for `%s', disabling peer", peer_conf->name); - return; - } - - aligned_int256_t key; - if (!read_key(key.p, peer_conf->key)) { - pr_warn(ctx, "invalid key configured for `%s', disabling peer", peer_conf->name); - return; - } - - peer_conf->protocol_config = malloc(sizeof(fastd_protocol_peer_config_t)); - peer_conf->protocol_config->public_key = key; - - if (memcmp(peer_conf->protocol_config->public_key.p, ctx->conf->protocol_config->key.public.p, 32) == 0) - pr_debug(ctx, "found own key as `%s', ignoring peer", peer_conf->name); -} - -static bool protocol_peer_check(fastd_context_t *ctx, fastd_peer_config_t *peer_conf) { - if (!peer_conf->protocol_config) - return false; - - if (memcmp(peer_conf->protocol_config->public_key.p, ctx->conf->protocol_config->key.public.p, 32) == 0) - return false; - - if (key_count(ctx, peer_conf->protocol_config->public_key.p) > 1) { - char buf[65]; - hexdump(buf, peer_conf->protocol_config->public_key.p); - pr_warn(ctx, "more than one peer is configured with key %s, disabling %s", buf, peer_conf->name); - return false; - } - - return true; -} - -static bool protocol_peer_check_temporary(fastd_context_t *ctx, fastd_peer_t *peer) { - if (key_count(ctx, peer->protocol_config->public_key.p)) { - char buf[65]; - hexdump(buf, peer->protocol_config->public_key.p); - pr_info(ctx, "key %s is configured now, deleting temporary peer.", buf); - return false; - } - - return true; -} - -static void init_protocol_state(fastd_context_t *ctx) { - if (!ctx->protocol_state) { - ctx->protocol_state = calloc(1, sizeof(fastd_protocol_state_t)); - - ctx->protocol_state->prev_handshake_key.preferred_till = ctx->conf->long_ago; - ctx->protocol_state->handshake_key.preferred_till = ctx->conf->long_ago; - } -} - -static void new_handshake_key(fastd_context_t *ctx, keypair_t *key) { - fastd_random_bytes(ctx, key->secret.p, 32, false); - ecc_25519_gf_sanitize_secret(&key->secret, &key->secret); - - ecc_25519_work_t work; - ecc_25519_scalarmult_base(&work, &key->secret); - ecc_25519_store_packed(&key->public, &work); -} - -static void maintenance(fastd_context_t *ctx) { - init_protocol_state(ctx); - - if (!is_handshake_key_preferred(ctx, &ctx->protocol_state->handshake_key)) { - pr_debug(ctx, "generating new handshake key"); - - ctx->protocol_state->prev_handshake_key = ctx->protocol_state->handshake_key; - - ctx->protocol_state->handshake_key.serial++; - - new_handshake_key(ctx, &ctx->protocol_state->handshake_key.key1); - new_handshake_key(ctx, &ctx->protocol_state->handshake_key.key2); - - ctx->protocol_state->handshake_key.preferred_till = ctx->now; - ctx->protocol_state->handshake_key.preferred_till.tv_sec += 15; - - ctx->protocol_state->handshake_key.valid_till = ctx->now; - ctx->protocol_state->handshake_key.valid_till.tv_sec += 30; - } -} - -static void protocol_handshake_init(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer) { - maintenance(ctx); - - fastd_buffer_t buffer = fastd_handshake_new_init(ctx, 3*(4+PUBLICKEYBYTES) /* sender key, receipient key, handshake key */); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->key.public.p); - - if (peer) - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_config->public_key.p); - else - pr_debug(ctx, "sending handshake to unknown peer %I", remote_addr); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, ctx->protocol_state->handshake_key.key1.public.p); - - fastd_send_handshake(ctx, sock, local_addr, remote_addr, peer, buffer); -} - - -static bool update_shared_handshake_key(fastd_context_t *ctx, const fastd_peer_t *peer, const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key) { - if (peer->protocol_state->last_handshake_serial == handshake_key->serial) { - if (memcmp(&peer->protocol_state->peer_handshake_key, peer_handshake_key, PUBLICKEYBYTES) == 0) - return true; - } - - fastd_sha256_t hashbuf; - fastd_sha256_blocks(&hashbuf, - handshake_key->key2.public.p, - peer_handshake_key->p, - ctx->conf->protocol_config->key.public.p, - peer->protocol_config->public_key.p, - NULL); - - ecc_int256_t d = {{0}}, e = {{0}}, eb, s; - - memcpy(d.p, hashbuf.b, HASHBYTES/2); - memcpy(e.p, hashbuf.b+HASHBYTES/2, HASHBYTES/2); - - d.p[15] |= 0x80; - e.p[15] |= 0x80; - - ecc_25519_gf_mult(&eb, &e, &ctx->conf->protocol_config->key.secret); - ecc_25519_gf_add(&s, &eb, &handshake_key->key2.secret); - - ecc_25519_work_t work, workX; - if (!ecc_25519_load_packed(&workX, peer_handshake_key)) - return false; - - ecc_25519_scalarmult(&work, &ecc_25519_gf_order, &workX); - if (!ecc_25519_is_identity(&work)) - return false; - - if (!ecc_25519_load_packed(&work, &peer->protocol_config->public_key)) - return false; - - ecc_25519_scalarmult(&work, &d, &work); - ecc_25519_add(&work, &workX, &work); - ecc_25519_scalarmult(&work, &s, &work); - - if (ecc_25519_is_identity(&work)) - return false; - - ecc_25519_store_packed(&peer->protocol_state->sigma, &work); - - fastd_sha256_blocks(&peer->protocol_state->shared_handshake_key, - handshake_key->key2.public.p, - peer_handshake_key->p, - ctx->conf->protocol_config->key.public.p, - peer->protocol_config->public_key.p, - peer->protocol_state->sigma.p, - NULL); - - peer->protocol_state->last_handshake_serial = handshake_key->serial; - peer->protocol_state->peer_handshake_key = *peer_handshake_key; - - return true; -} - -static void clear_shared_handshake_key(fastd_context_t *ctx UNUSED, const fastd_peer_t *peer) { - memset(&peer->protocol_state->sigma, 0, sizeof(peer->protocol_state->sigma)); - memset(&peer->protocol_state->shared_handshake_key, 0, sizeof(peer->protocol_state->shared_handshake_key)); - - peer->protocol_state->last_handshake_serial = 0; - memset(&peer->protocol_state->peer_handshake_key, 0, sizeof(peer->protocol_state->peer_handshake_key)); -} - -static void respond_handshake(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, - const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key, const fastd_handshake_t *handshake, const fastd_method_t *method) { - pr_debug(ctx, "responding handshake with %P[%I]...", peer, remote_addr); - - if (!update_shared_handshake_key(ctx, peer, handshake_key, peer_handshake_key)) - return; - - fastd_buffer_t buffer = fastd_handshake_new_reply(ctx, handshake, method, true, 4*(4+PUBLICKEYBYTES) + 2*(4+HASHBYTES)); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->key.public.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_config->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake_key->key2.public.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p); - - fastd_sha256_t hmacbuf; - - if (!ctx->conf->secure_handshakes) { - fastd_hmacsha256_blocks(&hmacbuf, peer->protocol_state->shared_handshake_key.w, ctx->conf->protocol_config->key.public.p, handshake_key->key2.public.p, NULL); - fastd_handshake_add(ctx, &buffer, RECORD_T, HASHBYTES, hmacbuf.b); - } - - memset(&hmacbuf, 0, sizeof(hmacbuf)); - fastd_handshake_add(ctx, &buffer, RECORD_TLV_MAC, HASHBYTES, hmacbuf.b); - fastd_hmacsha256(&hmacbuf, peer->protocol_state->shared_handshake_key.w, fastd_handshake_tlv_data(&buffer), fastd_handshake_tlv_len(&buffer)); - memcpy(buffer.data+buffer.len-HASHBYTES, hmacbuf.b, HASHBYTES); - - fastd_send_handshake(ctx, sock, local_addr, remote_addr, peer, buffer); -} - -static bool establish(fastd_context_t *ctx, fastd_peer_t *peer, const fastd_method_t *method, fastd_socket_t *sock, - const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, bool initiator, - const aligned_int256_t *A, const aligned_int256_t *B, const aligned_int256_t *X, - const aligned_int256_t *Y, const aligned_int256_t *sigma, uint64_t serial) { - if (serial <= peer->protocol_state->last_serial) { - pr_debug(ctx, "ignoring handshake from %P[%I] because of handshake key reuse", peer, remote_addr); - return false; - } - - pr_verbose(ctx, "%I authorized as %P", remote_addr, peer); - - if (!fastd_peer_claim_address(ctx, peer, sock, local_addr, remote_addr)) { - pr_warn(ctx, "can't set address %I which is used by a fixed peer", remote_addr); - fastd_peer_reset(ctx, peer); - return false; - } - - if (is_session_valid(ctx, &peer->protocol_state->session) && !is_session_valid(ctx, &peer->protocol_state->old_session)) { - if (peer->protocol_state->old_session.method) - peer->protocol_state->old_session.method->session_free(ctx, peer->protocol_state->old_session.method_state); - peer->protocol_state->old_session = peer->protocol_state->session; - } - else { - if (peer->protocol_state->session.method) - peer->protocol_state->session.method->session_free(ctx, peer->protocol_state->session.method_state); - } - - if (peer->protocol_state->old_session.method) { - if (peer->protocol_state->old_session.method != method) { - pr_debug(ctx, "method of %P[%I] has changed, terminating old session", peer, remote_addr); - peer->protocol_state->old_session.method->session_free(ctx, peer->protocol_state->old_session.method_state); - peer->protocol_state->old_session = (protocol_session_t){}; - } - else { - peer->protocol_state->old_session.method->session_superseded(ctx, peer->protocol_state->old_session.method_state); - } - } - - fastd_sha256_t hash; - fastd_sha256_blocks(&hash, X->p, Y->p, A->p, B->p, sigma->p, NULL); - - peer->protocol_state->session.established = ctx->now; - peer->protocol_state->session.handshakes_cleaned = false; - peer->protocol_state->session.refreshing = false; - peer->protocol_state->session.method = method; - peer->protocol_state->session.method_state = method->session_init(ctx, hash.b, HASHBYTES, initiator); - peer->protocol_state->last_serial = serial; - - fastd_peer_seen(ctx, peer); - - fastd_peer_set_established(ctx, peer); - - pr_verbose(ctx, "new session with %P established using method `%s'.", peer, method->name); - - if (initiator) - fastd_peer_schedule_handshake_default(ctx, peer); - else - send_empty(ctx, peer, &peer->protocol_state->session); - - return true; -} - -static inline bool has_field(const fastd_handshake_t *handshake, uint8_t type, size_t length) { - return (handshake->records[type].length == length); -} - -static void finish_handshake(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key, - const fastd_handshake_t *handshake, const fastd_method_t *method) { - pr_debug(ctx, "finishing handshake with %P[%I]...", peer, remote_addr); - - fastd_sha256_t hashbuf; - fastd_sha256_blocks(&hashbuf, - peer_handshake_key->p, - handshake_key->key1.public.p, - peer->protocol_config->public_key.p, - ctx->conf->protocol_config->key.public.p, - NULL); - - ecc_int256_t d = {{0}}, e = {{0}}, da, s; - - memcpy(d.p, hashbuf.b, HASHBYTES/2); - memcpy(e.p, hashbuf.b+HASHBYTES/2, HASHBYTES/2); - - d.p[15] |= 0x80; - e.p[15] |= 0x80; - - ecc_25519_gf_mult(&da, &d, &ctx->conf->protocol_config->key.secret); - ecc_25519_gf_add(&s, &da, &handshake_key->key1.secret); - - ecc_25519_work_t work, workY; - if (!ecc_25519_load_packed(&workY, peer_handshake_key)) - return; - - ecc_25519_scalarmult(&work, &ecc_25519_gf_order, &workY); - if (!ecc_25519_is_identity(&work)) - return; - - if (!ecc_25519_load_packed(&work, &peer->protocol_config->public_key)) - return; - - ecc_25519_scalarmult(&work, &e, &work); - ecc_25519_add(&work, &workY, &work); - ecc_25519_scalarmult(&work, &s, &work); - - if (ecc_25519_is_identity(&work)) - return; - - aligned_int256_t sigma; - ecc_25519_store_packed(&sigma, &work); - - fastd_sha256_t shared_handshake_key; - fastd_sha256_blocks(&shared_handshake_key, - peer_handshake_key->p, - handshake_key->key1.public.p, - peer->protocol_config->public_key.p, - ctx->conf->protocol_config->key.public.p, - sigma.p, - NULL); - - bool valid; - if (has_field(handshake, RECORD_TLV_MAC, HASHBYTES)) { - uint8_t mac[HASHBYTES]; - memcpy(mac, handshake->records[RECORD_TLV_MAC].data, HASHBYTES); - memset(handshake->records[RECORD_TLV_MAC].data, 0, HASHBYTES); - - valid = fastd_hmacsha256_verify(mac, shared_handshake_key.w, handshake->tlv_data, handshake->tlv_len); - } - else { - valid = fastd_hmacsha256_blocks_verify(handshake->records[RECORD_T].data, shared_handshake_key.w, peer->protocol_config->public_key.p, peer_handshake_key->p, NULL); - } - - if (!valid) { - pr_warn(ctx, "received invalid protocol handshake response from %P[%I]", peer, remote_addr); - return; - } - - if (!establish(ctx, peer, method, sock, local_addr, remote_addr, true, &handshake_key->key1.public, peer_handshake_key, &ctx->conf->protocol_config->key.public, - &peer->protocol_config->public_key, &sigma, handshake_key->serial)) - return; - - fastd_buffer_t buffer = fastd_handshake_new_reply(ctx, handshake, method, false, 4*(4+PUBLICKEYBYTES) + 2*(4+HASHBYTES)); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->key.public.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_config->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake_key->key1.public.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p); - - fastd_sha256_t hmacbuf; - - if (!ctx->conf->secure_handshakes) { - fastd_hmacsha256_blocks(&hmacbuf, shared_handshake_key.w, ctx->conf->protocol_config->key.public.p, handshake_key->key1.public.p, NULL); - fastd_handshake_add(ctx, &buffer, RECORD_T, HASHBYTES, hmacbuf.b); - } - - memset(&hmacbuf, 0, sizeof(hmacbuf)); - fastd_handshake_add(ctx, &buffer, RECORD_TLV_MAC, HASHBYTES, hmacbuf.b); - fastd_hmacsha256(&hmacbuf, shared_handshake_key.w, fastd_handshake_tlv_data(&buffer), fastd_handshake_tlv_len(&buffer)); - memcpy(buffer.data+buffer.len-HASHBYTES, hmacbuf.b, HASHBYTES); - - fastd_send_handshake(ctx, sock, local_addr, remote_addr, peer, buffer); -} - -static void handle_finish_handshake(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, - fastd_peer_t *peer, const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key, - const fastd_handshake_t *handshake, const fastd_method_t *method) { - pr_debug(ctx, "handling handshake finish with %P[%I]...", peer, remote_addr); - - if (!update_shared_handshake_key(ctx, peer, handshake_key, peer_handshake_key)) - return; - - bool valid; - if (has_field(handshake, RECORD_TLV_MAC, HASHBYTES)) { - uint8_t mac[HASHBYTES]; - memcpy(mac, handshake->records[RECORD_TLV_MAC].data, HASHBYTES); - memset(handshake->records[RECORD_TLV_MAC].data, 0, HASHBYTES); - - valid = fastd_hmacsha256_verify(mac, peer->protocol_state->shared_handshake_key.w, handshake->tlv_data, handshake->tlv_len); - } - else { - valid = fastd_hmacsha256_blocks_verify(handshake->records[RECORD_T].data, peer->protocol_state->shared_handshake_key.w, peer->protocol_config->public_key.p, peer_handshake_key->p, NULL); - } - - if (!valid) { - pr_warn(ctx, "received invalid protocol handshake finish from %P[%I]", peer, remote_addr); - return; - } - - establish(ctx, peer, method, sock, local_addr, remote_addr, false, peer_handshake_key, &handshake_key->key2.public, &peer->protocol_config->public_key, - &ctx->conf->protocol_config->key.public, &peer->protocol_state->sigma, handshake_key->serial); - - clear_shared_handshake_key(ctx, peer); -} - -static fastd_peer_t* find_sender_key(fastd_context_t *ctx, const fastd_peer_address_t *address, const unsigned char key[32], fastd_peer_t *peers) { - errno = 0; - - fastd_peer_t *ret = NULL, *peer; - - for (peer = peers; peer; peer = peer->next) { - if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) { - if (!fastd_peer_matches_address(ctx, peer, address)) { - errno = EPERM; - return NULL; - } - - ret = peer; - continue; - } - - if (fastd_peer_owns_address(ctx, peer, address)) { - errno = EPERM; - return NULL; - } - } - - if (!ret) - errno = ENOENT; - - return ret; -} - -static fastd_peer_t* match_sender_key(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *address, fastd_peer_t *peer, const unsigned char key[32]) { - errno = 0; - - if (sock->peer && peer != sock->peer) - exit_bug(ctx, "packet without correct peer set on dynamic socket"); - - if (peer) { - if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) - return peer; - - if (fastd_peer_owns_address(ctx, peer, address)) { - errno = EPERM; - return NULL; - } - } - - peer = find_sender_key(ctx, address, key, ctx->peers); - - if (!peer && errno == ENOENT) - peer = find_sender_key(ctx, address, key, ctx->peers_temp); - - return peer; -} - -static inline fastd_peer_t* add_temporary(fastd_context_t *ctx, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, const unsigned char key[32]) { - if (!fastd_peer_allow_unknown(ctx)) { - pr_debug(ctx, "ignoring handshake from %I (unknown key)", remote_addr); - return NULL; - } - - if (key_count(ctx, key)) { - pr_debug(ctx, "ignoring handshake from %I (disabled key)", remote_addr); - return NULL; - } - - fastd_peer_t *peer = fastd_peer_add_temporary(ctx); - - peer->protocol_config = malloc(sizeof(fastd_protocol_peer_config_t)); - memcpy(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES); - - /* Ugly hack */ - peer->protocol_state->last_serial--; - - if (!fastd_peer_verify_temporary(ctx, peer, local_addr, remote_addr)) { - pr_debug(ctx, "ignoring handshake from %P[%I] (verification failed)", peer, remote_addr); - fastd_peer_delete(ctx, peer); - return NULL; - } - - return peer; -} - -static inline keypair_t* get_handshake_keypair(handshake_key_t *handshake_key, uint8_t type) { - return (type % 2) ? &handshake_key->key2 : &handshake_key->key1; -} - -static void protocol_handshake_handle(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, const fastd_handshake_t *handshake, const fastd_method_t *method) { - bool temporary_added = false; - - maintenance(ctx); - - if (!has_field(handshake, RECORD_SENDER_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake without sender key from %I", remote_addr); - return; - } - - peer = match_sender_key(ctx, sock, remote_addr, peer, handshake->records[RECORD_SENDER_KEY].data); - if (!peer) { - switch (errno) { - case EPERM: - pr_debug(ctx, "ignoring handshake from %I (incorrect source address)", remote_addr); - return; - - case ENOENT: - peer = add_temporary(ctx, local_addr, remote_addr, handshake->records[RECORD_SENDER_KEY].data); - if (peer) { - temporary_added = true; - break; - } - - return; - - default: - exit_bug(ctx, "match_sender_key: unknown error"); - } - } - - if (fastd_peer_is_temporary(peer) && !temporary_added) { - if (!fastd_peer_verify_temporary(ctx, peer, local_addr, remote_addr)) { - pr_debug(ctx, "ignoring handshake from %P[%I] (verification failed)", peer, remote_addr); - return; - } - } - - if (!fastd_peer_may_connect(ctx, peer)) { - pr_debug(ctx, "ignoring handshake from %P[%I] because of local constraints", peer, remote_addr); - return; - } - - if (backoff(ctx, peer)) { - pr_debug(ctx, "received repeated handshakes from %P[%I], ignoring", peer, remote_addr); - return; - } - - if (has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { - if (memcmp(ctx->conf->protocol_config->key.public.p, handshake->records[RECORD_RECEIPIENT_KEY].data, PUBLICKEYBYTES) != 0) { - pr_debug(ctx, "received protocol handshake with wrong receipient key from %P[%I]", peer, remote_addr); - return; - } - } - - if (!has_field(handshake, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake without sender handshake key from %P[%I]", peer, remote_addr); - return; - } - - aligned_int256_t peer_handshake_key; - memcpy(peer_handshake_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES); - - if (handshake->type == 1) { - if (timespec_diff(&ctx->now, &peer->last_handshake_response) < (int)ctx->conf->min_handshake_interval*1000 - && fastd_peer_address_equal(remote_addr, &peer->last_handshake_response_address)) { - pr_debug(ctx, "not responding repeated handshake from %P[%I]", peer, remote_addr); - return; - } - - pr_verbose(ctx, "received handshake from %P[%I]%s%s", peer, remote_addr, handshake->peer_version ? " using fastd " : "", handshake->peer_version ?: ""); - - peer->last_handshake_response = ctx->now; - peer->last_handshake_response_address = *remote_addr; - respond_handshake(ctx, sock, local_addr, remote_addr, peer, &ctx->protocol_state->handshake_key, &peer_handshake_key, handshake, method); - return; - } - - if (!has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake reply without receipient key from %P[%I]", peer, remote_addr); - return; - } - - if (!has_field(handshake, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake reply without receipient handshake key from %P[%I]", peer, remote_addr); - return; - } - - if (!has_field(handshake, RECORD_TLV_MAC, HASHBYTES)) { - if (ctx->conf->secure_handshakes || !has_field(handshake, RECORD_T, HASHBYTES)) { - pr_debug(ctx, "received handshake reply without HMAC from %P[%I]", peer, remote_addr); - return; - } - } - - handshake_key_t *handshake_key; - if (is_handshake_key_valid(ctx, &ctx->protocol_state->handshake_key) && - memcmp(get_handshake_keypair(&ctx->protocol_state->handshake_key, handshake->type)->public.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) { - handshake_key = &ctx->protocol_state->handshake_key; - } - else if (is_handshake_key_valid(ctx, &ctx->protocol_state->prev_handshake_key) && - memcmp(get_handshake_keypair(&ctx->protocol_state->prev_handshake_key, handshake->type)->public.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) { - handshake_key = &ctx->protocol_state->prev_handshake_key; - } - else { - pr_debug(ctx, "received handshake reply with unexpected receipient handshake key from %P[%I]", peer, remote_addr); - return; - } - - switch (handshake->type) { - case 2: - pr_verbose(ctx, "received handshake response from %P[%I]%s%s", peer, remote_addr, handshake->peer_version ? " using fastd " : "", handshake->peer_version ?: ""); - - finish_handshake(ctx, sock, local_addr, remote_addr, peer, handshake_key, &peer_handshake_key, handshake, method); - break; - - case 3: - pr_debug(ctx, "received handshake finish from %P[%I]%s%s", peer, remote_addr, handshake->peer_version ? " using fastd " : "", handshake->peer_version ?: ""); - - handle_finish_handshake(ctx, sock, local_addr, remote_addr, peer, handshake_key, &peer_handshake_key, handshake, method); - break; - - default: - pr_debug(ctx, "received handshake reply with unknown type %u from %P[%I]", handshake->type, peer, remote_addr); - } -} - -static inline bool check_session(fastd_context_t *ctx, fastd_peer_t *peer) { - if (is_session_valid(ctx, &peer->protocol_state->session)) - return true; - - pr_verbose(ctx, "active session with %P timed out", peer); - fastd_peer_reset(ctx, peer); - return false; -} - -static void protocol_handle_recv(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer) { - if (!peer->protocol_state || !check_session(ctx, peer)) - goto fail; - - fastd_buffer_t recv_buffer; - bool ok = false; - - if (is_session_valid(ctx, &peer->protocol_state->old_session)) { - if (peer->protocol_state->old_session.method->decrypt(ctx, peer, peer->protocol_state->old_session.method_state, &recv_buffer, buffer)) - ok = true; - } - - if (!ok) { - if (peer->protocol_state->session.method->decrypt(ctx, peer, peer->protocol_state->session.method_state, &recv_buffer, buffer)) { - ok = true; - - if (peer->protocol_state->old_session.method) { - pr_debug(ctx, "invalidating old session with %P", peer); - peer->protocol_state->old_session.method->session_free(ctx, peer->protocol_state->old_session.method_state); - peer->protocol_state->old_session = (protocol_session_t){}; - } - - if (!peer->protocol_state->session.handshakes_cleaned) { - pr_debug(ctx, "cleaning left handshakes with %P", peer); - fastd_peer_unschedule_handshake(ctx, peer); - peer->protocol_state->session.handshakes_cleaned = true; - - if (peer->protocol_state->session.method->session_is_initiator(ctx, peer->protocol_state->session.method_state)) - send_empty(ctx, peer, &peer->protocol_state->session); - } - - check_session_refresh(ctx, peer); - } - } - - if (!ok) { - pr_verbose(ctx, "verification failed for packet received from %P", peer); - goto fail; - } - - fastd_peer_seen(ctx, peer); - - if (recv_buffer.len) - fastd_handle_receive(ctx, peer, recv_buffer); - else - fastd_buffer_free(recv_buffer); - - return; - - fail: - fastd_buffer_free(buffer); -} - -static void session_send(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer, protocol_session_t *session) { - size_t stat_size = buffer.len; - - fastd_buffer_t send_buffer; - if (!session->method->encrypt(ctx, peer, session->method_state, &send_buffer, buffer)) { - fastd_buffer_free(buffer); - return; - } - - fastd_send(ctx, peer->sock, &peer->local_address, &peer->address, peer, send_buffer, stat_size); - peer->last_send = ctx->now; -} - -static void protocol_send(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer) { - if (!peer->protocol_state || !fastd_peer_is_established(peer) || !check_session(ctx, peer)) { - fastd_buffer_free(buffer); - return; - } - - check_session_refresh(ctx, peer); - - if (peer->protocol_state->session.method->session_is_initiator(ctx, peer->protocol_state->session.method_state) && is_session_valid(ctx, &peer->protocol_state->old_session)) { - pr_debug2(ctx, "sending packet for old session to %P", peer); - session_send(ctx, peer, buffer, &peer->protocol_state->old_session); - } - else { - session_send(ctx, peer, buffer, &peer->protocol_state->session); - } -} - -static void send_empty(fastd_context_t *ctx, fastd_peer_t *peer, protocol_session_t *session) { - session_send(ctx, peer, fastd_buffer_alloc(ctx, 0, alignto(session->method->min_encrypt_head_space(ctx), 8), session->method->min_encrypt_tail_space(ctx)), session); -} - -static void protocol_init_peer_state(fastd_context_t *ctx, fastd_peer_t *peer) { - init_protocol_state(ctx); - - if (peer->protocol_state) - exit_bug(ctx, "tried to reinit peer state"); - - peer->protocol_state = calloc(1, sizeof(fastd_protocol_peer_state_t)); - peer->protocol_state->last_serial = ctx->protocol_state->handshake_key.serial; -} - -static void reset_session(fastd_context_t *ctx, protocol_session_t *session) { - if (session->method) - session->method->session_free(ctx, session->method_state); - secure_memzero(session, sizeof(protocol_session_t)); -} - -static void protocol_reset_peer_state(fastd_context_t *ctx, fastd_peer_t *peer) { - if (!peer->protocol_state) - return; - - reset_session(ctx, &peer->protocol_state->old_session); - reset_session(ctx, &peer->protocol_state->session); -} - -static void protocol_free_peer_state(fastd_context_t *ctx, fastd_peer_t *peer) { - if (peer->protocol_state) { - reset_session(ctx, &peer->protocol_state->old_session); - reset_session(ctx, &peer->protocol_state->session); - - free(peer->protocol_state); - } -} - -static inline void print_hexdump(const char *desc, unsigned char d[32]) { - char buf[65]; - hexdump(buf, d); - - printf("%s%s\n", desc, buf); -} - -static void protocol_generate_key(fastd_context_t *ctx) { - ecc_int256_t secret_key; - ecc_int256_t public_key; - - if (!ctx->conf->machine_readable) - pr_info(ctx, "Reading 32 bytes from /dev/random..."); - - fastd_random_bytes(ctx, secret_key.p, 32, true); - ecc_25519_gf_sanitize_secret(&secret_key, &secret_key); - - ecc_25519_work_t work; - ecc_25519_scalarmult_base(&work, &secret_key); - ecc_25519_store_packed(&public_key, &work); - - if (ctx->conf->machine_readable) { - print_hexdump("", secret_key.p); - } - else { - print_hexdump("Secret: ", secret_key.p); - print_hexdump("Public: ", public_key.p); - } -} - -static void protocol_show_key(fastd_context_t *ctx) { - if (ctx->conf->machine_readable) - print_hexdump("", ctx->conf->protocol_config->key.public.p); - else - print_hexdump("Public: ", ctx->conf->protocol_config->key.public.p); -} - -static void protocol_set_shell_env(fastd_context_t *ctx, const fastd_peer_t *peer) { - char buf[65]; - - hexdump(buf, ctx->conf->protocol_config->key.public.p); - setenv("LOCAL_KEY", buf, 1); - - if (peer && peer->protocol_config) { - hexdump(buf, peer->protocol_config->public_key.p); - setenv("PEER_KEY", buf, 1); - } - else { - unsetenv("PEER_KEY"); - } -} - -static bool protocol_describe_peer(const fastd_context_t *ctx UNUSED, const fastd_peer_t *peer, char *buf, size_t len) { - if (peer && peer->protocol_config) { - char dumpbuf[65]; - - hexdump(dumpbuf, peer->protocol_config->public_key.p); - snprintf(buf, len, "%.16s", dumpbuf); - return true; - } - else { - return false; - } -} - -const fastd_protocol_t fastd_protocol_ec25519_fhmqvc = { - .name = "ec25519-fhmqvc", - - .init = protocol_init, - .peer_configure = protocol_peer_configure, - .peer_check = protocol_peer_check, - .peer_check_temporary = protocol_peer_check_temporary, - - .handshake_init = protocol_handshake_init, - .handshake_handle = protocol_handshake_handle, - - .handle_recv = protocol_handle_recv, - .send = protocol_send, - - .init_peer_state = protocol_init_peer_state, - .reset_peer_state = protocol_reset_peer_state, - .free_peer_state = protocol_free_peer_state, - - .generate_key = protocol_generate_key, - .show_key = protocol_show_key, - .set_shell_env = protocol_set_shell_env, - .describe_peer = protocol_describe_peer, -}; diff --git a/src/protocols/CMakeLists.txt b/src/protocols/CMakeLists.txt new file mode 100644 index 0000000..f033d32 --- /dev/null +++ b/src/protocols/CMakeLists.txt @@ -0,0 +1,10 @@ +set(PROTOCOLS ec25519_fhmqvc) + +set(PROTOCOL_OBJECTS "") + +foreach(protocol ${PROTOCOLS}) + add_subdirectory(${protocol}) + list(APPEND PROTOCOL_OBJECTS $) +endforeach(protocol) + +set(PROTOCOL_OBJECTS "${PROTOCOL_OBJECTS}" PARENT_SCOPE) diff --git a/src/protocols/ec25519_fhmqvc/CMakeLists.txt b/src/protocols/ec25519_fhmqvc/CMakeLists.txt new file mode 100644 index 0000000..ff1e246 --- /dev/null +++ b/src/protocols/ec25519_fhmqvc/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(BEFORE ${FASTD_SOURCE_DIR}/src ${FASTD_BINARY_DIR} ${UECC_INCLUDE_DIRS}) + +add_library(protocol_ec25519_fhmqvc OBJECT + ec25519_fhmqvc.c +) +set_property(TARGET protocol_ec25519_fhmqvc PROPERTY COMPILE_FLAGS "${FASTD_CFLAGS}") diff --git a/src/protocols/ec25519_fhmqvc/ec25519_fhmqvc.c b/src/protocols/ec25519_fhmqvc/ec25519_fhmqvc.c new file mode 100644 index 0000000..9fe1414 --- /dev/null +++ b/src/protocols/ec25519_fhmqvc/ec25519_fhmqvc.c @@ -0,0 +1,1014 @@ +/* + 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 +#include +#include +#include + +#include + + +#define PUBLICKEYBYTES 32 +#define SECRETKEYBYTES 32 +#define HASHBYTES FASTD_SHA256_HASH_BYTES + + +#if HASHBYTES != FASTD_HMACSHA256_KEY_BYTES +#error bug: HASHBYTES != FASTD_HMACSHA256_KEY_BYTES +#endif + +#if HASHBYTES != SECRETKEYBYTES +#error bug: HASHBYTES != SECRETKEYBYTES +#endif + + +typedef ecc_int256_t __attribute__((aligned(4))) aligned_int256_t; + +typedef struct keypair { + ecc_int256_t secret; + aligned_int256_t public; +} keypair_t; + +struct fastd_protocol_config { + keypair_t key; +}; + +typedef struct handshake_key { + uint64_t serial; + struct timespec preferred_till; + struct timespec valid_till; + + /* keypair used as initiator */ + keypair_t key1; + + /* keypair used as responder */ + keypair_t key2; +} handshake_key_t; + +struct fastd_protocol_state { + handshake_key_t prev_handshake_key; + handshake_key_t handshake_key; +}; + +struct fastd_protocol_peer_config { + aligned_int256_t public_key; +}; + +typedef struct protocol_session { + struct timespec established; + + bool handshakes_cleaned; + bool refreshing; + + const fastd_method_t *method; + fastd_method_session_state_t *method_state; +} protocol_session_t; + +struct fastd_protocol_peer_state { + protocol_session_t old_session; + protocol_session_t session; + + uint64_t last_serial; + + /* handshake cache */ + uint64_t last_handshake_serial; + aligned_int256_t peer_handshake_key; + aligned_int256_t sigma; + fastd_sha256_t shared_handshake_key; +}; + + +#define RECORD_SENDER_KEY RECORD_PROTOCOL1 +#define RECORD_RECEIPIENT_KEY RECORD_PROTOCOL2 +#define RECORD_SENDER_HANDSHAKE_KEY RECORD_PROTOCOL3 +#define RECORD_RECEIPIENT_HANDSHAKE_KEY RECORD_PROTOCOL4 +#define RECORD_T RECORD_PROTOCOL5 + + +static void send_empty(fastd_context_t *ctx, fastd_peer_t *peer, protocol_session_t *session); + + +static inline bool read_key(uint8_t key[32], const char *hexkey) { + if ((strlen(hexkey) != 64) || (strspn(hexkey, "0123456789abcdefABCDEF") != 64)) + return false; + + int i; + for (i = 0; i < 32; i++) + sscanf(&hexkey[2*i], "%02hhx", &key[i]); + + return true; +} + +static inline bool is_handshake_key_valid(fastd_context_t *ctx, const handshake_key_t *handshake_key) { + return timespec_after(&handshake_key->valid_till, &ctx->now); +} + +static inline bool is_handshake_key_preferred(fastd_context_t *ctx, const handshake_key_t *handshake_key) { + return timespec_after(&handshake_key->preferred_till, &ctx->now); +} + +static inline bool is_session_valid(fastd_context_t *ctx, const protocol_session_t *session) { + return (session->method && session->method->session_is_valid(ctx, session->method_state)); +} + +static bool backoff(fastd_context_t *ctx, const fastd_peer_t *peer) { + return (peer->protocol_state && is_session_valid(ctx, &peer->protocol_state->session) + && timespec_diff(&ctx->now, &peer->protocol_state->session.established) < 15000); +} + +static inline void check_session_refresh(fastd_context_t *ctx, fastd_peer_t *peer) { + protocol_session_t *session = &peer->protocol_state->session; + + if (!session->refreshing && session->method->session_is_initiator(ctx, session->method_state) && session->method->session_want_refresh(ctx, session->method_state)) { + pr_verbose(ctx, "refreshing session with %P", peer); + session->handshakes_cleaned = true; + session->refreshing = true; + fastd_peer_schedule_handshake(ctx, peer, 0); + } +} + +static fastd_protocol_config_t* protocol_init(fastd_context_t *ctx) { + fastd_protocol_config_t *protocol_config = malloc(sizeof(fastd_protocol_config_t)); + + if (!ctx->conf->secret) + exit_error(ctx, "no secret key configured"); + + if (!read_key(protocol_config->key.secret.p, ctx->conf->secret)) + exit_error(ctx, "invalid secret key"); + + ecc_25519_work_t work; + ecc_25519_scalarmult_base(&work, &protocol_config->key.secret); + ecc_25519_store_packed(&protocol_config->key.public, &work); + + return protocol_config; +} + +static inline void hexdump(char out[65], const unsigned char d[32]) { + int i; + for (i = 0; i < 32; i++) + snprintf(out+2*i, 3, "%02x", d[i]); +} + +static size_t key_count(fastd_context_t *ctx, const unsigned char key[32]) { + size_t ret = 0; + + fastd_peer_config_t *p; + for (p = ctx->conf->peers; p; p = p->next) { + if (!p->protocol_config) + continue; + + if (memcmp(p->protocol_config->public_key.p, key, 32) == 0) + ret++; + } + + return ret; +} + +static void protocol_peer_configure(fastd_context_t *ctx, fastd_peer_config_t *peer_conf) { + if (peer_conf->protocol_config) + return; + + if (!peer_conf->key) { + pr_warn(ctx, "no key configured for `%s', disabling peer", peer_conf->name); + return; + } + + aligned_int256_t key; + if (!read_key(key.p, peer_conf->key)) { + pr_warn(ctx, "invalid key configured for `%s', disabling peer", peer_conf->name); + return; + } + + peer_conf->protocol_config = malloc(sizeof(fastd_protocol_peer_config_t)); + peer_conf->protocol_config->public_key = key; + + if (memcmp(peer_conf->protocol_config->public_key.p, ctx->conf->protocol_config->key.public.p, 32) == 0) + pr_debug(ctx, "found own key as `%s', ignoring peer", peer_conf->name); +} + +static bool protocol_peer_check(fastd_context_t *ctx, fastd_peer_config_t *peer_conf) { + if (!peer_conf->protocol_config) + return false; + + if (memcmp(peer_conf->protocol_config->public_key.p, ctx->conf->protocol_config->key.public.p, 32) == 0) + return false; + + if (key_count(ctx, peer_conf->protocol_config->public_key.p) > 1) { + char buf[65]; + hexdump(buf, peer_conf->protocol_config->public_key.p); + pr_warn(ctx, "more than one peer is configured with key %s, disabling %s", buf, peer_conf->name); + return false; + } + + return true; +} + +static bool protocol_peer_check_temporary(fastd_context_t *ctx, fastd_peer_t *peer) { + if (key_count(ctx, peer->protocol_config->public_key.p)) { + char buf[65]; + hexdump(buf, peer->protocol_config->public_key.p); + pr_info(ctx, "key %s is configured now, deleting temporary peer.", buf); + return false; + } + + return true; +} + +static void init_protocol_state(fastd_context_t *ctx) { + if (!ctx->protocol_state) { + ctx->protocol_state = calloc(1, sizeof(fastd_protocol_state_t)); + + ctx->protocol_state->prev_handshake_key.preferred_till = ctx->conf->long_ago; + ctx->protocol_state->handshake_key.preferred_till = ctx->conf->long_ago; + } +} + +static void new_handshake_key(fastd_context_t *ctx, keypair_t *key) { + fastd_random_bytes(ctx, key->secret.p, 32, false); + ecc_25519_gf_sanitize_secret(&key->secret, &key->secret); + + ecc_25519_work_t work; + ecc_25519_scalarmult_base(&work, &key->secret); + ecc_25519_store_packed(&key->public, &work); +} + +static void maintenance(fastd_context_t *ctx) { + init_protocol_state(ctx); + + if (!is_handshake_key_preferred(ctx, &ctx->protocol_state->handshake_key)) { + pr_debug(ctx, "generating new handshake key"); + + ctx->protocol_state->prev_handshake_key = ctx->protocol_state->handshake_key; + + ctx->protocol_state->handshake_key.serial++; + + new_handshake_key(ctx, &ctx->protocol_state->handshake_key.key1); + new_handshake_key(ctx, &ctx->protocol_state->handshake_key.key2); + + ctx->protocol_state->handshake_key.preferred_till = ctx->now; + ctx->protocol_state->handshake_key.preferred_till.tv_sec += 15; + + ctx->protocol_state->handshake_key.valid_till = ctx->now; + ctx->protocol_state->handshake_key.valid_till.tv_sec += 30; + } +} + +static void protocol_handshake_init(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer) { + maintenance(ctx); + + fastd_buffer_t buffer = fastd_handshake_new_init(ctx, 3*(4+PUBLICKEYBYTES) /* sender key, receipient key, handshake key */); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->key.public.p); + + if (peer) + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_config->public_key.p); + else + pr_debug(ctx, "sending handshake to unknown peer %I", remote_addr); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, ctx->protocol_state->handshake_key.key1.public.p); + + fastd_send_handshake(ctx, sock, local_addr, remote_addr, peer, buffer); +} + + +static bool update_shared_handshake_key(fastd_context_t *ctx, const fastd_peer_t *peer, const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key) { + if (peer->protocol_state->last_handshake_serial == handshake_key->serial) { + if (memcmp(&peer->protocol_state->peer_handshake_key, peer_handshake_key, PUBLICKEYBYTES) == 0) + return true; + } + + fastd_sha256_t hashbuf; + fastd_sha256_blocks(&hashbuf, + handshake_key->key2.public.p, + peer_handshake_key->p, + ctx->conf->protocol_config->key.public.p, + peer->protocol_config->public_key.p, + NULL); + + ecc_int256_t d = {{0}}, e = {{0}}, eb, s; + + memcpy(d.p, hashbuf.b, HASHBYTES/2); + memcpy(e.p, hashbuf.b+HASHBYTES/2, HASHBYTES/2); + + d.p[15] |= 0x80; + e.p[15] |= 0x80; + + ecc_25519_gf_mult(&eb, &e, &ctx->conf->protocol_config->key.secret); + ecc_25519_gf_add(&s, &eb, &handshake_key->key2.secret); + + ecc_25519_work_t work, workX; + if (!ecc_25519_load_packed(&workX, peer_handshake_key)) + return false; + + ecc_25519_scalarmult(&work, &ecc_25519_gf_order, &workX); + if (!ecc_25519_is_identity(&work)) + return false; + + if (!ecc_25519_load_packed(&work, &peer->protocol_config->public_key)) + return false; + + ecc_25519_scalarmult(&work, &d, &work); + ecc_25519_add(&work, &workX, &work); + ecc_25519_scalarmult(&work, &s, &work); + + if (ecc_25519_is_identity(&work)) + return false; + + ecc_25519_store_packed(&peer->protocol_state->sigma, &work); + + fastd_sha256_blocks(&peer->protocol_state->shared_handshake_key, + handshake_key->key2.public.p, + peer_handshake_key->p, + ctx->conf->protocol_config->key.public.p, + peer->protocol_config->public_key.p, + peer->protocol_state->sigma.p, + NULL); + + peer->protocol_state->last_handshake_serial = handshake_key->serial; + peer->protocol_state->peer_handshake_key = *peer_handshake_key; + + return true; +} + +static void clear_shared_handshake_key(fastd_context_t *ctx UNUSED, const fastd_peer_t *peer) { + memset(&peer->protocol_state->sigma, 0, sizeof(peer->protocol_state->sigma)); + memset(&peer->protocol_state->shared_handshake_key, 0, sizeof(peer->protocol_state->shared_handshake_key)); + + peer->protocol_state->last_handshake_serial = 0; + memset(&peer->protocol_state->peer_handshake_key, 0, sizeof(peer->protocol_state->peer_handshake_key)); +} + +static void respond_handshake(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, + const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key, const fastd_handshake_t *handshake, const fastd_method_t *method) { + pr_debug(ctx, "responding handshake with %P[%I]...", peer, remote_addr); + + if (!update_shared_handshake_key(ctx, peer, handshake_key, peer_handshake_key)) + return; + + fastd_buffer_t buffer = fastd_handshake_new_reply(ctx, handshake, method, true, 4*(4+PUBLICKEYBYTES) + 2*(4+HASHBYTES)); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->key.public.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_config->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake_key->key2.public.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p); + + fastd_sha256_t hmacbuf; + + if (!ctx->conf->secure_handshakes) { + fastd_hmacsha256_blocks(&hmacbuf, peer->protocol_state->shared_handshake_key.w, ctx->conf->protocol_config->key.public.p, handshake_key->key2.public.p, NULL); + fastd_handshake_add(ctx, &buffer, RECORD_T, HASHBYTES, hmacbuf.b); + } + + memset(&hmacbuf, 0, sizeof(hmacbuf)); + fastd_handshake_add(ctx, &buffer, RECORD_TLV_MAC, HASHBYTES, hmacbuf.b); + fastd_hmacsha256(&hmacbuf, peer->protocol_state->shared_handshake_key.w, fastd_handshake_tlv_data(&buffer), fastd_handshake_tlv_len(&buffer)); + memcpy(buffer.data+buffer.len-HASHBYTES, hmacbuf.b, HASHBYTES); + + fastd_send_handshake(ctx, sock, local_addr, remote_addr, peer, buffer); +} + +static bool establish(fastd_context_t *ctx, fastd_peer_t *peer, const fastd_method_t *method, fastd_socket_t *sock, + const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, bool initiator, + const aligned_int256_t *A, const aligned_int256_t *B, const aligned_int256_t *X, + const aligned_int256_t *Y, const aligned_int256_t *sigma, uint64_t serial) { + if (serial <= peer->protocol_state->last_serial) { + pr_debug(ctx, "ignoring handshake from %P[%I] because of handshake key reuse", peer, remote_addr); + return false; + } + + pr_verbose(ctx, "%I authorized as %P", remote_addr, peer); + + if (!fastd_peer_claim_address(ctx, peer, sock, local_addr, remote_addr)) { + pr_warn(ctx, "can't set address %I which is used by a fixed peer", remote_addr); + fastd_peer_reset(ctx, peer); + return false; + } + + if (is_session_valid(ctx, &peer->protocol_state->session) && !is_session_valid(ctx, &peer->protocol_state->old_session)) { + if (peer->protocol_state->old_session.method) + peer->protocol_state->old_session.method->session_free(ctx, peer->protocol_state->old_session.method_state); + peer->protocol_state->old_session = peer->protocol_state->session; + } + else { + if (peer->protocol_state->session.method) + peer->protocol_state->session.method->session_free(ctx, peer->protocol_state->session.method_state); + } + + if (peer->protocol_state->old_session.method) { + if (peer->protocol_state->old_session.method != method) { + pr_debug(ctx, "method of %P[%I] has changed, terminating old session", peer, remote_addr); + peer->protocol_state->old_session.method->session_free(ctx, peer->protocol_state->old_session.method_state); + peer->protocol_state->old_session = (protocol_session_t){}; + } + else { + peer->protocol_state->old_session.method->session_superseded(ctx, peer->protocol_state->old_session.method_state); + } + } + + fastd_sha256_t hash; + fastd_sha256_blocks(&hash, X->p, Y->p, A->p, B->p, sigma->p, NULL); + + peer->protocol_state->session.established = ctx->now; + peer->protocol_state->session.handshakes_cleaned = false; + peer->protocol_state->session.refreshing = false; + peer->protocol_state->session.method = method; + peer->protocol_state->session.method_state = method->session_init(ctx, hash.b, HASHBYTES, initiator); + peer->protocol_state->last_serial = serial; + + fastd_peer_seen(ctx, peer); + + fastd_peer_set_established(ctx, peer); + + pr_verbose(ctx, "new session with %P established using method `%s'.", peer, method->name); + + if (initiator) + fastd_peer_schedule_handshake_default(ctx, peer); + else + send_empty(ctx, peer, &peer->protocol_state->session); + + return true; +} + +static inline bool has_field(const fastd_handshake_t *handshake, uint8_t type, size_t length) { + return (handshake->records[type].length == length); +} + +static void finish_handshake(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key, + const fastd_handshake_t *handshake, const fastd_method_t *method) { + pr_debug(ctx, "finishing handshake with %P[%I]...", peer, remote_addr); + + fastd_sha256_t hashbuf; + fastd_sha256_blocks(&hashbuf, + peer_handshake_key->p, + handshake_key->key1.public.p, + peer->protocol_config->public_key.p, + ctx->conf->protocol_config->key.public.p, + NULL); + + ecc_int256_t d = {{0}}, e = {{0}}, da, s; + + memcpy(d.p, hashbuf.b, HASHBYTES/2); + memcpy(e.p, hashbuf.b+HASHBYTES/2, HASHBYTES/2); + + d.p[15] |= 0x80; + e.p[15] |= 0x80; + + ecc_25519_gf_mult(&da, &d, &ctx->conf->protocol_config->key.secret); + ecc_25519_gf_add(&s, &da, &handshake_key->key1.secret); + + ecc_25519_work_t work, workY; + if (!ecc_25519_load_packed(&workY, peer_handshake_key)) + return; + + ecc_25519_scalarmult(&work, &ecc_25519_gf_order, &workY); + if (!ecc_25519_is_identity(&work)) + return; + + if (!ecc_25519_load_packed(&work, &peer->protocol_config->public_key)) + return; + + ecc_25519_scalarmult(&work, &e, &work); + ecc_25519_add(&work, &workY, &work); + ecc_25519_scalarmult(&work, &s, &work); + + if (ecc_25519_is_identity(&work)) + return; + + aligned_int256_t sigma; + ecc_25519_store_packed(&sigma, &work); + + fastd_sha256_t shared_handshake_key; + fastd_sha256_blocks(&shared_handshake_key, + peer_handshake_key->p, + handshake_key->key1.public.p, + peer->protocol_config->public_key.p, + ctx->conf->protocol_config->key.public.p, + sigma.p, + NULL); + + bool valid; + if (has_field(handshake, RECORD_TLV_MAC, HASHBYTES)) { + uint8_t mac[HASHBYTES]; + memcpy(mac, handshake->records[RECORD_TLV_MAC].data, HASHBYTES); + memset(handshake->records[RECORD_TLV_MAC].data, 0, HASHBYTES); + + valid = fastd_hmacsha256_verify(mac, shared_handshake_key.w, handshake->tlv_data, handshake->tlv_len); + } + else { + valid = fastd_hmacsha256_blocks_verify(handshake->records[RECORD_T].data, shared_handshake_key.w, peer->protocol_config->public_key.p, peer_handshake_key->p, NULL); + } + + if (!valid) { + pr_warn(ctx, "received invalid protocol handshake response from %P[%I]", peer, remote_addr); + return; + } + + if (!establish(ctx, peer, method, sock, local_addr, remote_addr, true, &handshake_key->key1.public, peer_handshake_key, &ctx->conf->protocol_config->key.public, + &peer->protocol_config->public_key, &sigma, handshake_key->serial)) + return; + + fastd_buffer_t buffer = fastd_handshake_new_reply(ctx, handshake, method, false, 4*(4+PUBLICKEYBYTES) + 2*(4+HASHBYTES)); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->key.public.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_config->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake_key->key1.public.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p); + + fastd_sha256_t hmacbuf; + + if (!ctx->conf->secure_handshakes) { + fastd_hmacsha256_blocks(&hmacbuf, shared_handshake_key.w, ctx->conf->protocol_config->key.public.p, handshake_key->key1.public.p, NULL); + fastd_handshake_add(ctx, &buffer, RECORD_T, HASHBYTES, hmacbuf.b); + } + + memset(&hmacbuf, 0, sizeof(hmacbuf)); + fastd_handshake_add(ctx, &buffer, RECORD_TLV_MAC, HASHBYTES, hmacbuf.b); + fastd_hmacsha256(&hmacbuf, shared_handshake_key.w, fastd_handshake_tlv_data(&buffer), fastd_handshake_tlv_len(&buffer)); + memcpy(buffer.data+buffer.len-HASHBYTES, hmacbuf.b, HASHBYTES); + + fastd_send_handshake(ctx, sock, local_addr, remote_addr, peer, buffer); +} + +static void handle_finish_handshake(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, + fastd_peer_t *peer, const handshake_key_t *handshake_key, const aligned_int256_t *peer_handshake_key, + const fastd_handshake_t *handshake, const fastd_method_t *method) { + pr_debug(ctx, "handling handshake finish with %P[%I]...", peer, remote_addr); + + if (!update_shared_handshake_key(ctx, peer, handshake_key, peer_handshake_key)) + return; + + bool valid; + if (has_field(handshake, RECORD_TLV_MAC, HASHBYTES)) { + uint8_t mac[HASHBYTES]; + memcpy(mac, handshake->records[RECORD_TLV_MAC].data, HASHBYTES); + memset(handshake->records[RECORD_TLV_MAC].data, 0, HASHBYTES); + + valid = fastd_hmacsha256_verify(mac, peer->protocol_state->shared_handshake_key.w, handshake->tlv_data, handshake->tlv_len); + } + else { + valid = fastd_hmacsha256_blocks_verify(handshake->records[RECORD_T].data, peer->protocol_state->shared_handshake_key.w, peer->protocol_config->public_key.p, peer_handshake_key->p, NULL); + } + + if (!valid) { + pr_warn(ctx, "received invalid protocol handshake finish from %P[%I]", peer, remote_addr); + return; + } + + establish(ctx, peer, method, sock, local_addr, remote_addr, false, peer_handshake_key, &handshake_key->key2.public, &peer->protocol_config->public_key, + &ctx->conf->protocol_config->key.public, &peer->protocol_state->sigma, handshake_key->serial); + + clear_shared_handshake_key(ctx, peer); +} + +static fastd_peer_t* find_sender_key(fastd_context_t *ctx, const fastd_peer_address_t *address, const unsigned char key[32], fastd_peer_t *peers) { + errno = 0; + + fastd_peer_t *ret = NULL, *peer; + + for (peer = peers; peer; peer = peer->next) { + if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) { + if (!fastd_peer_matches_address(ctx, peer, address)) { + errno = EPERM; + return NULL; + } + + ret = peer; + continue; + } + + if (fastd_peer_owns_address(ctx, peer, address)) { + errno = EPERM; + return NULL; + } + } + + if (!ret) + errno = ENOENT; + + return ret; +} + +static fastd_peer_t* match_sender_key(fastd_context_t *ctx, const fastd_socket_t *sock, const fastd_peer_address_t *address, fastd_peer_t *peer, const unsigned char key[32]) { + errno = 0; + + if (sock->peer && peer != sock->peer) + exit_bug(ctx, "packet without correct peer set on dynamic socket"); + + if (peer) { + if (memcmp(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) + return peer; + + if (fastd_peer_owns_address(ctx, peer, address)) { + errno = EPERM; + return NULL; + } + } + + peer = find_sender_key(ctx, address, key, ctx->peers); + + if (!peer && errno == ENOENT) + peer = find_sender_key(ctx, address, key, ctx->peers_temp); + + return peer; +} + +static inline fastd_peer_t* add_temporary(fastd_context_t *ctx, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, const unsigned char key[32]) { + if (!fastd_peer_allow_unknown(ctx)) { + pr_debug(ctx, "ignoring handshake from %I (unknown key)", remote_addr); + return NULL; + } + + if (key_count(ctx, key)) { + pr_debug(ctx, "ignoring handshake from %I (disabled key)", remote_addr); + return NULL; + } + + fastd_peer_t *peer = fastd_peer_add_temporary(ctx); + + peer->protocol_config = malloc(sizeof(fastd_protocol_peer_config_t)); + memcpy(peer->protocol_config->public_key.p, key, PUBLICKEYBYTES); + + /* Ugly hack */ + peer->protocol_state->last_serial--; + + if (!fastd_peer_verify_temporary(ctx, peer, local_addr, remote_addr)) { + pr_debug(ctx, "ignoring handshake from %P[%I] (verification failed)", peer, remote_addr); + fastd_peer_delete(ctx, peer); + return NULL; + } + + return peer; +} + +static inline keypair_t* get_handshake_keypair(handshake_key_t *handshake_key, uint8_t type) { + return (type % 2) ? &handshake_key->key2 : &handshake_key->key1; +} + +static void protocol_handshake_handle(fastd_context_t *ctx, fastd_socket_t *sock, const fastd_peer_address_t *local_addr, const fastd_peer_address_t *remote_addr, fastd_peer_t *peer, const fastd_handshake_t *handshake, const fastd_method_t *method) { + bool temporary_added = false; + + maintenance(ctx); + + if (!has_field(handshake, RECORD_SENDER_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake without sender key from %I", remote_addr); + return; + } + + peer = match_sender_key(ctx, sock, remote_addr, peer, handshake->records[RECORD_SENDER_KEY].data); + if (!peer) { + switch (errno) { + case EPERM: + pr_debug(ctx, "ignoring handshake from %I (incorrect source address)", remote_addr); + return; + + case ENOENT: + peer = add_temporary(ctx, local_addr, remote_addr, handshake->records[RECORD_SENDER_KEY].data); + if (peer) { + temporary_added = true; + break; + } + + return; + + default: + exit_bug(ctx, "match_sender_key: unknown error"); + } + } + + if (fastd_peer_is_temporary(peer) && !temporary_added) { + if (!fastd_peer_verify_temporary(ctx, peer, local_addr, remote_addr)) { + pr_debug(ctx, "ignoring handshake from %P[%I] (verification failed)", peer, remote_addr); + return; + } + } + + if (!fastd_peer_may_connect(ctx, peer)) { + pr_debug(ctx, "ignoring handshake from %P[%I] because of local constraints", peer, remote_addr); + return; + } + + if (backoff(ctx, peer)) { + pr_debug(ctx, "received repeated handshakes from %P[%I], ignoring", peer, remote_addr); + return; + } + + if (has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { + if (memcmp(ctx->conf->protocol_config->key.public.p, handshake->records[RECORD_RECEIPIENT_KEY].data, PUBLICKEYBYTES) != 0) { + pr_debug(ctx, "received protocol handshake with wrong receipient key from %P[%I]", peer, remote_addr); + return; + } + } + + if (!has_field(handshake, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake without sender handshake key from %P[%I]", peer, remote_addr); + return; + } + + aligned_int256_t peer_handshake_key; + memcpy(peer_handshake_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES); + + if (handshake->type == 1) { + if (timespec_diff(&ctx->now, &peer->last_handshake_response) < (int)ctx->conf->min_handshake_interval*1000 + && fastd_peer_address_equal(remote_addr, &peer->last_handshake_response_address)) { + pr_debug(ctx, "not responding repeated handshake from %P[%I]", peer, remote_addr); + return; + } + + pr_verbose(ctx, "received handshake from %P[%I]%s%s", peer, remote_addr, handshake->peer_version ? " using fastd " : "", handshake->peer_version ?: ""); + + peer->last_handshake_response = ctx->now; + peer->last_handshake_response_address = *remote_addr; + respond_handshake(ctx, sock, local_addr, remote_addr, peer, &ctx->protocol_state->handshake_key, &peer_handshake_key, handshake, method); + return; + } + + if (!has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake reply without receipient key from %P[%I]", peer, remote_addr); + return; + } + + if (!has_field(handshake, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake reply without receipient handshake key from %P[%I]", peer, remote_addr); + return; + } + + if (!has_field(handshake, RECORD_TLV_MAC, HASHBYTES)) { + if (ctx->conf->secure_handshakes || !has_field(handshake, RECORD_T, HASHBYTES)) { + pr_debug(ctx, "received handshake reply without HMAC from %P[%I]", peer, remote_addr); + return; + } + } + + handshake_key_t *handshake_key; + if (is_handshake_key_valid(ctx, &ctx->protocol_state->handshake_key) && + memcmp(get_handshake_keypair(&ctx->protocol_state->handshake_key, handshake->type)->public.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) { + handshake_key = &ctx->protocol_state->handshake_key; + } + else if (is_handshake_key_valid(ctx, &ctx->protocol_state->prev_handshake_key) && + memcmp(get_handshake_keypair(&ctx->protocol_state->prev_handshake_key, handshake->type)->public.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) { + handshake_key = &ctx->protocol_state->prev_handshake_key; + } + else { + pr_debug(ctx, "received handshake reply with unexpected receipient handshake key from %P[%I]", peer, remote_addr); + return; + } + + switch (handshake->type) { + case 2: + pr_verbose(ctx, "received handshake response from %P[%I]%s%s", peer, remote_addr, handshake->peer_version ? " using fastd " : "", handshake->peer_version ?: ""); + + finish_handshake(ctx, sock, local_addr, remote_addr, peer, handshake_key, &peer_handshake_key, handshake, method); + break; + + case 3: + pr_debug(ctx, "received handshake finish from %P[%I]%s%s", peer, remote_addr, handshake->peer_version ? " using fastd " : "", handshake->peer_version ?: ""); + + handle_finish_handshake(ctx, sock, local_addr, remote_addr, peer, handshake_key, &peer_handshake_key, handshake, method); + break; + + default: + pr_debug(ctx, "received handshake reply with unknown type %u from %P[%I]", handshake->type, peer, remote_addr); + } +} + +static inline bool check_session(fastd_context_t *ctx, fastd_peer_t *peer) { + if (is_session_valid(ctx, &peer->protocol_state->session)) + return true; + + pr_verbose(ctx, "active session with %P timed out", peer); + fastd_peer_reset(ctx, peer); + return false; +} + +static void protocol_handle_recv(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer) { + if (!peer->protocol_state || !check_session(ctx, peer)) + goto fail; + + fastd_buffer_t recv_buffer; + bool ok = false; + + if (is_session_valid(ctx, &peer->protocol_state->old_session)) { + if (peer->protocol_state->old_session.method->decrypt(ctx, peer, peer->protocol_state->old_session.method_state, &recv_buffer, buffer)) + ok = true; + } + + if (!ok) { + if (peer->protocol_state->session.method->decrypt(ctx, peer, peer->protocol_state->session.method_state, &recv_buffer, buffer)) { + ok = true; + + if (peer->protocol_state->old_session.method) { + pr_debug(ctx, "invalidating old session with %P", peer); + peer->protocol_state->old_session.method->session_free(ctx, peer->protocol_state->old_session.method_state); + peer->protocol_state->old_session = (protocol_session_t){}; + } + + if (!peer->protocol_state->session.handshakes_cleaned) { + pr_debug(ctx, "cleaning left handshakes with %P", peer); + fastd_peer_unschedule_handshake(ctx, peer); + peer->protocol_state->session.handshakes_cleaned = true; + + if (peer->protocol_state->session.method->session_is_initiator(ctx, peer->protocol_state->session.method_state)) + send_empty(ctx, peer, &peer->protocol_state->session); + } + + check_session_refresh(ctx, peer); + } + } + + if (!ok) { + pr_verbose(ctx, "verification failed for packet received from %P", peer); + goto fail; + } + + fastd_peer_seen(ctx, peer); + + if (recv_buffer.len) + fastd_handle_receive(ctx, peer, recv_buffer); + else + fastd_buffer_free(recv_buffer); + + return; + + fail: + fastd_buffer_free(buffer); +} + +static void session_send(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer, protocol_session_t *session) { + size_t stat_size = buffer.len; + + fastd_buffer_t send_buffer; + if (!session->method->encrypt(ctx, peer, session->method_state, &send_buffer, buffer)) { + fastd_buffer_free(buffer); + return; + } + + fastd_send(ctx, peer->sock, &peer->local_address, &peer->address, peer, send_buffer, stat_size); + peer->last_send = ctx->now; +} + +static void protocol_send(fastd_context_t *ctx, fastd_peer_t *peer, fastd_buffer_t buffer) { + if (!peer->protocol_state || !fastd_peer_is_established(peer) || !check_session(ctx, peer)) { + fastd_buffer_free(buffer); + return; + } + + check_session_refresh(ctx, peer); + + if (peer->protocol_state->session.method->session_is_initiator(ctx, peer->protocol_state->session.method_state) && is_session_valid(ctx, &peer->protocol_state->old_session)) { + pr_debug2(ctx, "sending packet for old session to %P", peer); + session_send(ctx, peer, buffer, &peer->protocol_state->old_session); + } + else { + session_send(ctx, peer, buffer, &peer->protocol_state->session); + } +} + +static void send_empty(fastd_context_t *ctx, fastd_peer_t *peer, protocol_session_t *session) { + session_send(ctx, peer, fastd_buffer_alloc(ctx, 0, alignto(session->method->min_encrypt_head_space(ctx), 8), session->method->min_encrypt_tail_space(ctx)), session); +} + +static void protocol_init_peer_state(fastd_context_t *ctx, fastd_peer_t *peer) { + init_protocol_state(ctx); + + if (peer->protocol_state) + exit_bug(ctx, "tried to reinit peer state"); + + peer->protocol_state = calloc(1, sizeof(fastd_protocol_peer_state_t)); + peer->protocol_state->last_serial = ctx->protocol_state->handshake_key.serial; +} + +static void reset_session(fastd_context_t *ctx, protocol_session_t *session) { + if (session->method) + session->method->session_free(ctx, session->method_state); + secure_memzero(session, sizeof(protocol_session_t)); +} + +static void protocol_reset_peer_state(fastd_context_t *ctx, fastd_peer_t *peer) { + if (!peer->protocol_state) + return; + + reset_session(ctx, &peer->protocol_state->old_session); + reset_session(ctx, &peer->protocol_state->session); +} + +static void protocol_free_peer_state(fastd_context_t *ctx, fastd_peer_t *peer) { + if (peer->protocol_state) { + reset_session(ctx, &peer->protocol_state->old_session); + reset_session(ctx, &peer->protocol_state->session); + + free(peer->protocol_state); + } +} + +static inline void print_hexdump(const char *desc, unsigned char d[32]) { + char buf[65]; + hexdump(buf, d); + + printf("%s%s\n", desc, buf); +} + +static void protocol_generate_key(fastd_context_t *ctx) { + ecc_int256_t secret_key; + ecc_int256_t public_key; + + if (!ctx->conf->machine_readable) + pr_info(ctx, "Reading 32 bytes from /dev/random..."); + + fastd_random_bytes(ctx, secret_key.p, 32, true); + ecc_25519_gf_sanitize_secret(&secret_key, &secret_key); + + ecc_25519_work_t work; + ecc_25519_scalarmult_base(&work, &secret_key); + ecc_25519_store_packed(&public_key, &work); + + if (ctx->conf->machine_readable) { + print_hexdump("", secret_key.p); + } + else { + print_hexdump("Secret: ", secret_key.p); + print_hexdump("Public: ", public_key.p); + } +} + +static void protocol_show_key(fastd_context_t *ctx) { + if (ctx->conf->machine_readable) + print_hexdump("", ctx->conf->protocol_config->key.public.p); + else + print_hexdump("Public: ", ctx->conf->protocol_config->key.public.p); +} + +static void protocol_set_shell_env(fastd_context_t *ctx, const fastd_peer_t *peer) { + char buf[65]; + + hexdump(buf, ctx->conf->protocol_config->key.public.p); + setenv("LOCAL_KEY", buf, 1); + + if (peer && peer->protocol_config) { + hexdump(buf, peer->protocol_config->public_key.p); + setenv("PEER_KEY", buf, 1); + } + else { + unsetenv("PEER_KEY"); + } +} + +static bool protocol_describe_peer(const fastd_context_t *ctx UNUSED, const fastd_peer_t *peer, char *buf, size_t len) { + if (peer && peer->protocol_config) { + char dumpbuf[65]; + + hexdump(dumpbuf, peer->protocol_config->public_key.p); + snprintf(buf, len, "%.16s", dumpbuf); + return true; + } + else { + return false; + } +} + +const fastd_protocol_t fastd_protocol_ec25519_fhmqvc = { + .name = "ec25519-fhmqvc", + + .init = protocol_init, + .peer_configure = protocol_peer_configure, + .peer_check = protocol_peer_check, + .peer_check_temporary = protocol_peer_check_temporary, + + .handshake_init = protocol_handshake_init, + .handshake_handle = protocol_handshake_handle, + + .handle_recv = protocol_handle_recv, + .send = protocol_send, + + .init_peer_state = protocol_init_peer_state, + .reset_peer_state = protocol_reset_peer_state, + .free_peer_state = protocol_free_peer_state, + + .generate_key = protocol_generate_key, + .show_key = protocol_show_key, + .set_shell_env = protocol_set_shell_env, + .describe_peer = protocol_describe_peer, +}; diff --git a/src/types.h b/src/types.h index 16b1a5a..8e70326 100644 --- a/src/types.h +++ b/src/types.h @@ -34,7 +34,6 @@ #define _FASTD_TYPES_H_ #include -#include #include -- cgit v1.2.3