From 701fcc7c7e353def78d89d9ee0ca52d32fb894b9 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 14 Apr 2012 13:06:11 +0200 Subject: Separate handshake from encryption method --- .gitignore | 1 + CMakeLists.txt | 13 +- config.h.in | 2 +- src/CMakeLists.txt | 19 +- src/config.c | 47 +- src/config.l | 1 + src/config.y | 28 +- src/fastd.c | 12 +- src/fastd.h | 25 +- src/handshake.c | 17 + src/handshake.h | 1 + src/method_null.c | 80 +++ src/method_xsalsa20_poly1305.c | 205 ++++++ src/protocol_ec25519_fhmqvc.c | 749 ++++++++++++++++++++ src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c | 865 ------------------------ src/protocol_null.c | 158 ----- src/types.h | 4 +- 17 files changed, 1144 insertions(+), 1083 deletions(-) create mode 100644 src/method_null.c create mode 100644 src/method_xsalsa20_poly1305.c create mode 100644 src/protocol_ec25519_fhmqvc.c delete mode 100644 src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c delete mode 100644 src/protocol_null.c diff --git a/.gitignore b/.gitignore index bdc5af0..4f1b7ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *~ build +profile diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e50c36..aa84c0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,24 +6,17 @@ set(CMAKE_MODULE_PATH ${FASTD_SOURCE_DIR}) find_package(BISON 2.5 REQUIRED) find_package(FLEX REQUIRED) -find_package(UECC) -find_package(NaCl) +find_package(UECC REQUIRED) +find_package(NaCl REQUIRED) -if(UECC_FOUND AND NACL_FOUND) - set(CRYPTO_FOUND TRUE) -endif(UECC_FOUND AND NACL_FOUND) -set(WITH_PROTOCOL_ECFXP ${CRYPTO_FOUND} CACHE BOOL "Include ec25519-fhmqvc-xsalsa20-poly1305 protocol") +set(WITH_METHOD_XSALSA20_POLY1305 TRUE CACHE BOOL "Include xsalsa20-poly1305 method") set(MAX_CONFIG_DEPTH 10 CACHE STRING "Maximum config include depth") # Ensure the value is numeric math(EXPR MAX_CONFIG_DEPTH_NUM ${MAX_CONFIG_DEPTH}) -if(WITH_PROTOCOL_ECFXP AND NOT CRYPTO_FOUND) - MESSAGE(FATAL_ERROR "libuecc and NaCl are required for the ec25519-fhmqvc-xsalsa20-poly1305 protocol") -endif(WITH_PROTOCOL_ECFXP AND NOT CRYPTO_FOUND) - configure_file(${FASTD_SOURCE_DIR}/config.h.in ${FASTD_BINARY_DIR}/config.h) add_subdirectory(src) diff --git a/config.h.in b/config.h.in index 2a58ff1..c9db59c 100644 --- a/config.h.in +++ b/config.h.in @@ -28,7 +28,7 @@ #ifndef _FASTD_CONFIG_H_ #define _FASTD_CONFIG_H_ -#cmakedefine WITH_PROTOCOL_ECFXP +#cmakedefine WITH_METHOD_XSALSA20_POLY1305 #define MAX_CONFIG_DEPTH @MAX_CONFIG_DEPTH_NUM@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 986568c..a700c4c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,10 @@ -set(PROTOCOLS protocol_null.c) -set(FASTD_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${FASTD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -set(FASTD_LIBS "") +set(METHODS method_null.c) -if(WITH_PROTOCOL_ECFXP) - list(APPEND PROTOCOLS protocol_ec25519_fhmqvc_xsalsa20_poly1305.c) - list(APPEND FASTD_INCLUDES ${UECC_INCLUDE_DIR} ${NACL_INCLUDE_DIR}) - list(APPEND FASTD_LIBS ${UECC_LIBRARY} ${NACL_LIBRARY}) -endif(WITH_PROTOCOL_ECFXP) +if(WITH_METHOD_XSALSA20_POLY1305) + list(APPEND METHODS method_xsalsa20_poly1305.c) +endif(WITH_METHOD_XSALSA20_POLY1305) -include_directories(${FASTD_INCLUDES}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${FASTD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${UECC_INCLUDE_DIR} ${NACL_INCLUDE_DIR}) FLEX_TARGET(fastd_config_lex config.l ${CMAKE_CURRENT_BINARY_DIR}/config.ll.c) BISON_TARGET(fastd_config_parse config.y ${CMAKE_CURRENT_BINARY_DIR}/config.yy.c) @@ -22,10 +18,11 @@ add_executable(fastd queue.c random.c task.c + protocol_ec25519_fhmqvc.c ${FLEX_fastd_config_lex_OUTPUTS} ${BISON_fastd_config_parse_OUTPUTS} - ${PROTOCOLS} + ${METHODS} ) -target_link_libraries(fastd rt ${FASTD_LIBS}) +target_link_libraries(fastd rt ${UECC_LIBRARY} ${NACL_LIBRARY}) install(TARGETS fastd RUNTIME DESTINATION sbin) diff --git a/src/config.c b/src/config.c index 8c63b4e..e7c8640 100644 --- a/src/config.c +++ b/src/config.c @@ -41,10 +41,12 @@ #include -extern const fastd_protocol fastd_protocol_null; +extern const fastd_protocol fastd_protocol_ec25519_fhmqvc; -#ifdef WITH_PROTOCOL_ECFXP -extern const fastd_protocol fastd_protocol_ec25519_fhmqvc_xsalsa20_poly1305; +extern const fastd_method fastd_method_null; + +#ifdef WITH_METHOD_XSALSA20_POLY1305 +extern const fastd_method fastd_method_xsalsa20_poly1305; #endif @@ -66,7 +68,8 @@ static void default_config(fastd_config *conf) { conf->peer_to_peer = false; - conf->protocol = &fastd_protocol_null; + conf->protocol = &fastd_protocol_ec25519_fhmqvc; + conf->method = &fastd_method_null; conf->secret = NULL; conf->key_valid = 3600; /* 60 minutes */ conf->key_refresh = 3300; /* 55 minutes */ @@ -107,6 +110,28 @@ static bool config_match(const char *opt, ...) { return match; } +bool fastd_config_protocol(fastd_context *ctx, fastd_config *conf, const char *name) { + if (!strcmp(name, "ec25519-fhmqvc")) + conf->protocol = &fastd_protocol_ec25519_fhmqvc; + else + return false; + + return true; +} + +bool fastd_config_method(fastd_context *ctx, fastd_config *conf, const char *name) { + if (!strcmp(name, "null")) + conf->method = &fastd_method_null; +#ifdef WITH_METHOD_XSALSA20_POLY1305 + else if (!strcmp(name, "xsalsa20-poly1305")) + conf->method = &fastd_method_xsalsa20_poly1305; +#endif + else + return false; + + return true; +} + static void read_peer_dir(fastd_context *ctx, fastd_config *conf, const char *dir) { DIR *dirh = opendir("."); @@ -436,17 +461,17 @@ void fastd_configure(fastd_context *ctx, fastd_config *conf, int argc, char *con IF_OPTION_ARG("-P", "--protocol") { - if (!strcmp(arg, "null")) - conf->protocol = &fastd_protocol_null; -#ifdef WITH_PROTOCOL_ECFXP - else if (!strcmp(arg, "ecfxp")) - conf->protocol = &fastd_protocol_ec25519_fhmqvc_xsalsa20_poly1305; -#endif - else + if (!fastd_config_protocol(ctx, conf, arg)) exit_error(ctx, "invalid protocol `%s'", arg); continue; } + IF_OPTION_ARG("--method") { + if (!fastd_config_method(ctx, conf, arg)) + exit_error(ctx, "invalid method `%s'", arg); + continue; + } + IF_OPTION_ARG("-p", "--peer") { peer = fastd_peer_config_new(ctx, conf); diff --git a/src/config.l b/src/config.l index c2f771a..f20116c 100644 --- a/src/config.l +++ b/src/config.l @@ -59,6 +59,7 @@ bind { UPDATE_LOCATION; return TOK_BIND; } mtu { UPDATE_LOCATION; return TOK_MTU; } mode { UPDATE_LOCATION; return TOK_MODE; } protocol { UPDATE_LOCATION; return TOK_PROTOCOL; } +method { UPDATE_LOCATION; return TOK_METHOD; } peer { UPDATE_LOCATION; return TOK_PEER; } address { UPDATE_LOCATION; return TOK_ADDRESS; } secret { UPDATE_LOCATION; return TOK_SECRET; } diff --git a/src/config.y b/src/config.y index ac5f808..b1d68e0 100644 --- a/src/config.y +++ b/src/config.y @@ -61,6 +61,7 @@ %token TOK_MTU %token TOK_MODE %token TOK_PROTOCOL +%token TOK_METHOD %token TOK_PEER %token TOK_ADDRESS %token TOK_SECRET @@ -99,12 +100,6 @@ #include void fastd_config_error(YYLTYPE *loc, fastd_context *ctx, fastd_config *conf, const char *filename, int depth, char *s); - - extern const fastd_protocol fastd_protocol_null; - - #ifdef WITH_PROTOCOL_ECFXP - extern const fastd_protocol fastd_protocol_ec25519_fhmqvc_xsalsa20_poly1305; - #endif } @@ -130,6 +125,7 @@ statement: TOK_LOG log ';' | TOK_MTU mtu ';' | TOK_MODE mode ';' | TOK_PROTOCOL protocol ';' + | TOK_METHOD method ';' | TOK_SECRET secret ';' | TOK_ON TOK_UP on_up ';' | TOK_ON TOK_DOWN on_down ';' @@ -180,19 +176,19 @@ mode: TOK_TAP { conf->mode = MODE_TAP; } ; protocol: TOK_STRING { - if (!strcmp($1->str, "null")) { - conf->protocol = &fastd_protocol_null; - } -#ifdef WITH_PROTOCOL_ECFXP - else if (!strcmp($1->str, "ecfxp")) { - conf->protocol = &fastd_protocol_ec25519_fhmqvc_xsalsa20_poly1305; - } -#endif - else { + if (!fastd_config_protocol(ctx, conf, $1->str)) { fastd_config_error(&@$, ctx, conf, filename, depth, "invalid protocol"); YYERROR; } -} + } + ; + +method: TOK_STRING { + if (!fastd_config_method(ctx, conf, $1->str)) { + fastd_config_error(&@$, ctx, conf, filename, depth, "invalid method"); + YYERROR; + } + } ; secret: TOK_STRING { free(conf->secret); conf->secret = strdup($1->str); } diff --git a/src/fastd.c b/src/fastd.c index ba8c6e8..711d903 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -340,7 +340,7 @@ static void handle_tasks(fastd_context *ctx) { fastd_peer *dest_peer; for (dest_peer = ctx->peers; dest_peer; dest_peer = dest_peer->next) { if (dest_peer != task->peer && dest_peer->state == STATE_ESTABLISHED) { - fastd_buffer send_buffer = fastd_buffer_alloc(task->handle_recv.buffer.len, ctx->conf->protocol->min_encrypt_head_space(ctx), 0); + fastd_buffer send_buffer = fastd_buffer_alloc(task->handle_recv.buffer.len, ctx->conf->method->min_encrypt_head_space(ctx), 0); memcpy(send_buffer.data, task->handle_recv.buffer.data, task->handle_recv.buffer.len); ctx->conf->protocol->send(ctx, dest_peer, send_buffer); } @@ -366,7 +366,7 @@ static void handle_tasks(fastd_context *ctx) { case TASK_KEEPALIVE: pr_debug(ctx, "sending keepalive to %P", task->peer); - ctx->conf->protocol->send(ctx, task->peer, fastd_buffer_alloc(0, ctx->conf->protocol->min_encrypt_head_space(ctx), 0)); + ctx->conf->protocol->send(ctx, task->peer, fastd_buffer_alloc(0, ctx->conf->method->min_encrypt_head_space(ctx), 0)); break; default: @@ -379,7 +379,7 @@ static void handle_tasks(fastd_context *ctx) { static void handle_tun(fastd_context *ctx) { size_t max_len = fastd_max_packet_size(ctx); - fastd_buffer buffer = fastd_buffer_alloc(max_len, ctx->conf->protocol->min_encrypt_head_space(ctx), 0); + fastd_buffer buffer = fastd_buffer_alloc(max_len, ctx->conf->method->min_encrypt_head_space(ctx), 0); ssize_t len = read(ctx->tunfd, buffer.data, max_len); if (len < 0) { @@ -416,7 +416,7 @@ static void handle_tun(fastd_context *ctx) { if (peer == NULL) { for (peer = ctx->peers; peer; peer = peer->next) { if (peer->state == STATE_ESTABLISHED) { - fastd_buffer send_buffer = fastd_buffer_alloc(len, ctx->conf->protocol->min_encrypt_head_space(ctx), 0); + fastd_buffer send_buffer = fastd_buffer_alloc(len, ctx->conf->method->min_encrypt_head_space(ctx), 0); memcpy(send_buffer.data, buffer.data, len); ctx->conf->protocol->send(ctx, peer, send_buffer); } @@ -427,8 +427,8 @@ static void handle_tun(fastd_context *ctx) { } static void handle_socket(fastd_context *ctx, int sockfd) { - size_t max_len = ctx->conf->protocol->max_packet_size(ctx); - fastd_buffer buffer = fastd_buffer_alloc(max_len, ctx->conf->protocol->min_decrypt_head_space(ctx), 0); + size_t max_len = ctx->conf->method->max_packet_size(ctx); + fastd_buffer buffer = fastd_buffer_alloc(max_len, ctx->conf->method->min_decrypt_head_space(ctx), 0); uint8_t packet_type; struct iovec iov[2] = { diff --git a/src/fastd.h b/src/fastd.h index e3d3b02..2113ccd 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -63,10 +63,6 @@ struct _fastd_protocol { void (*peer_configure)(fastd_context *ctx, fastd_peer_config *peer_conf); void (*peer_config_purged)(fastd_context *ctx, fastd_peer_config *peer_conf); - size_t (*max_packet_size)(fastd_context *ctx); - size_t (*min_encrypt_head_space)(fastd_context *ctx); - size_t (*min_decrypt_head_space)(fastd_context *ctx); - void (*handshake_init)(fastd_context *ctx, fastd_peer *peer); void (*handshake_handle)(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake); @@ -78,6 +74,23 @@ struct _fastd_protocol { void (*generate_key)(fastd_context *ctx); }; +struct _fastd_method { + const char *name; + + size_t (*max_packet_size)(fastd_context *ctx); + size_t (*min_encrypt_head_space)(fastd_context *ctx); + size_t (*min_decrypt_head_space)(fastd_context *ctx); + + fastd_method_session_state* (*session_init)(fastd_context *ctx, uint8_t *secret, size_t length, bool initiator); + bool (*session_is_valid)(fastd_context *ctx, fastd_method_session_state *session); + bool (*session_is_initiator)(fastd_context *ctx, fastd_method_session_state *session); + bool (*session_want_refresh)(fastd_context *ctx, fastd_method_session_state *session); + void (*session_free)(fastd_context *ctx, fastd_method_session_state *session); + + bool (*encrypt)(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in); + bool (*decrypt)(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in); +}; + struct _fastd_config { fastd_loglevel loglevel; @@ -97,6 +110,7 @@ struct _fastd_config { bool peer_to_peer; const fastd_protocol *protocol; + const fastd_method *method; char *secret; unsigned key_valid; unsigned key_refresh; @@ -153,6 +167,9 @@ void fastd_printf(const fastd_context *ctx, const char *format, ...); void fastd_read_peer_dir(fastd_context *ctx, fastd_config *conf, const char *dir); bool fastd_read_config(fastd_context *ctx, fastd_config *conf, const char *filename, bool peer_config, int depth); + +bool fastd_config_protocol(fastd_context *ctx, fastd_config *conf, const char *name); +bool fastd_config_method(fastd_context *ctx, fastd_config *conf, const char *name); void fastd_configure(fastd_context *ctx, fastd_config *conf, int argc, char *const argv[]); void fastd_reconfigure(fastd_context *ctx, fastd_config *conf); void fastd_config_release(fastd_context *ctx, fastd_config *conf); diff --git a/src/handshake.c b/src/handshake.c index 06adef8..2015208 100644 --- a/src/handshake.c +++ b/src/handshake.c @@ -45,6 +45,7 @@ static const char const *RECORD_TYPES[RECORD_MAX] = { "(protocol specific 4)", "(protocol specific 5)", "MTU", + "method name", }; static const char const *REPLY_TYPES[REPLY_MAX] = { @@ -59,10 +60,12 @@ static const char const *REPLY_TYPES[REPLY_MAX] = { fastd_buffer fastd_handshake_new_init(fastd_context *ctx, fastd_peer *peer, size_t tail_space) { size_t protocol_len = strlen(ctx->conf->protocol->name); + size_t method_len = strlen(ctx->conf->method->name); fastd_buffer buffer = fastd_buffer_alloc(sizeof(fastd_packet), 0, 2*5 + /* handshake type, mode */ 6 + /* MTU */ 4+protocol_len + /* protocol name */ + 4+method_len + /* method name */ tail_space ); fastd_packet *request = buffer.data; @@ -75,6 +78,7 @@ fastd_buffer fastd_handshake_new_init(fastd_context *ctx, fastd_peer *peer, size fastd_handshake_add_uint16(ctx, &buffer, RECORD_MTU, ctx->conf->mtu); fastd_handshake_add(ctx, &buffer, RECORD_PROTOCOL_NAME, protocol_len, ctx->conf->protocol->name); + fastd_handshake_add(ctx, &buffer, RECORD_METHOD_NAME, method_len, ctx->conf->method->name); return buffer; } @@ -181,6 +185,19 @@ void fastd_handshake_handle(fastd_context *ctx, fastd_peer *peer, fastd_buffer b goto send_reply; } + if (!handshake.records[RECORD_METHOD_NAME].data) { + reply_code = REPLY_MANDATORY_MISSING; + error_detail = RECORD_METHOD_NAME; + goto send_reply; + } + + if (handshake.records[RECORD_METHOD_NAME].length != strlen(ctx->conf->method->name) + || strncmp((char*)handshake.records[RECORD_METHOD_NAME].data, ctx->conf->method->name, handshake.records[RECORD_METHOD_NAME].length)) { + reply_code = REPLY_UNACCEPTABLE_VALUE; + error_detail = RECORD_METHOD_NAME; + goto send_reply; + } + send_reply: if (reply_code) { fastd_buffer reply_buffer = fastd_buffer_alloc(sizeof(fastd_packet), 0, 3*5 /* enough space for handshake type, reply code and error detail */); diff --git a/src/handshake.h b/src/handshake.h index a5b861e..9bb1a56 100644 --- a/src/handshake.h +++ b/src/handshake.h @@ -43,6 +43,7 @@ typedef enum _fastd_handshake_record_type { RECORD_PROTOCOL4, RECORD_PROTOCOL5, RECORD_MTU, + RECORD_METHOD_NAME, RECORD_MAX, } fastd_handshake_record_type; diff --git a/src/method_null.c b/src/method_null.c new file mode 100644 index 0000000..fbdf1f5 --- /dev/null +++ b/src/method_null.c @@ -0,0 +1,80 @@ +/* + Copyright (c) 2012, 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" + + +static size_t method_max_packet_size(fastd_context *ctx) { + return fastd_max_packet_size(ctx); +} + +static size_t method_min_head_space(fastd_context *ctx) { + return 0; +} + +static fastd_method_session_state* method_session_init(fastd_context *ctx, uint8_t *secret, size_t length, bool initiator) { + if (initiator) + return (fastd_method_session_state*)1; + else + return (fastd_method_session_state*)2; +} + +static bool method_session_is_valid(fastd_context *ctx, fastd_method_session_state *session) { + return session; +} + +static bool method_session_is_initiator(fastd_context *ctx, fastd_method_session_state *session) { + return (session == (fastd_method_session_state*)1); +} + +static bool method_session_want_refresh(fastd_context *ctx, fastd_method_session_state *session) { + return false; +} + +static void method_session_free(fastd_context *ctx, fastd_method_session_state *session) { +} + +static bool method_passthrough(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { + *out = in; + return true; +} + +const fastd_method fastd_method_null = { + .name = "null", + + .max_packet_size = method_max_packet_size, + .min_encrypt_head_space = method_min_head_space, + .min_decrypt_head_space = method_min_head_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_free = method_session_free, + + .encrypt = method_passthrough, + .decrypt = method_passthrough, +}; diff --git a/src/method_xsalsa20_poly1305.c b/src/method_xsalsa20_poly1305.c new file mode 100644 index 0000000..5194a4d --- /dev/null +++ b/src/method_xsalsa20_poly1305.c @@ -0,0 +1,205 @@ +/* + Copyright (c) 2012, 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]; +}; + + +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]) { + if ((nonce[0] & 1) != (old_nonce[0] & 1)) + return false; + + int i; + for (i = NONCEBYTES-1; i >= 0; i--) { + if (nonce[i] > old_nonce[i]) + return true; + if (nonce[i] < old_nonce[i]) + return false; + } + + return false; +} + +static size_t method_max_packet_size(fastd_context *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 *ctx) { + return crypto_secretbox_xsalsa20poly1305_ZEROBYTES; +} + +static size_t method_min_decrypt_head_space(fastd_context *ctx) { + return (crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES - NONCEBYTES); +} + +static fastd_method_session_state* method_session_init(fastd_context *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 *session = malloc(sizeof(fastd_method_session_state)); + + 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; + + 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 *ctx, fastd_method_session_state *session) { + return (session && timespec_after(&session->valid_till, &ctx->now)); +} + +static bool method_session_is_initiator(fastd_context *ctx, fastd_method_session_state *session) { + return (session->send_nonce[0] & 1); +} + +static bool method_session_want_refresh(fastd_context *ctx, fastd_method_session_state *session) { + return (method_session_is_initiator(ctx, session) && timespec_after(&ctx->now, &session->refresh_after)); +} + +static void method_session_free(fastd_context *ctx, fastd_method_session_state *session) { + if(session) { + memset(session, 0, sizeof(fastd_method_session_state)); + free(session); + } +} + +static bool method_encrypt(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { + fastd_buffer_pull_head(&in, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); + memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); + + *out = fastd_buffer_alloc(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(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 *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer 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); + + if (!is_nonce_valid(nonce, session->receive_nonce)) + return false; + + fastd_buffer_pull_head(&in, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); + memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); + + *out = fastd_buffer_alloc(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(&in, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); + memcpy(in.data, nonce, NONCEBYTES); + return false; + } + + fastd_buffer_free(in); + + fastd_buffer_push_head(out, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); + + memcpy(session->receive_nonce, nonce, NONCEBYTES); + + return true; +} + +const fastd_method 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, + + .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_free = method_session_free, + + .encrypt = method_encrypt, + .decrypt = method_decrypt, +}; diff --git a/src/protocol_ec25519_fhmqvc.c b/src/protocol_ec25519_fhmqvc.c new file mode 100644 index 0000000..3168d03 --- /dev/null +++ b/src/protocol_ec25519_fhmqvc.c @@ -0,0 +1,749 @@ +/* + Copyright (c) 2012, 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. +*/ + + +#define _GNU_SOURCE + +#include "fastd.h" +#include "handshake.h" +#include "peer.h" +#include "task.h" + +#include + +#include +#include +#include + + +#define PUBLICKEYBYTES 32 +#define SECRETKEYBYTES 32 +#define HMACBYTES crypto_auth_hmacsha256_BYTES +#define HASHBYTES crypto_hash_sha256_BYTES + + +#if HASHBYTES != crypto_auth_hmacsha256_KEYBYTES +#error bug: HASHBYTES != crypto_auth_hmacsha256_KEYBYTES +#endif + +#if HASHBYTES != SECRETKEYBYTES +#error bug: HASHBYTES != SECRETKEYBYTES +#endif + + +struct _fastd_protocol_config { + ecc_secret_key_256 secret_key; + ecc_public_key_256 public_key; +}; + +typedef enum _handshake_state { + HANDSHAKE_STATE_INIT, + HANDSHAKE_STATE_RESPONSE, + HANDSHAKE_STATE_ESTABLISHED +} handshake_state; + +struct _fastd_protocol_peer_config { + ecc_public_key_256 public_key; +}; + +typedef struct _protocol_handshake { + const fastd_peer_config *peer_config; + + handshake_state state; + ecc_secret_key_256 secret_key; + ecc_public_key_256 public_key; + ecc_public_key_256 peer_key; + ecc_public_key_256 sigma; + uint8_t shared_handshake_key[HASHBYTES]; +} protocol_handshake; + +typedef struct _protocol_session { + bool handshakes_cleaned; + bool refreshing; + + fastd_method_session_state *method_state; +} protocol_session; + +struct _fastd_protocol_peer_state { + protocol_session old_session; + protocol_session session; + + protocol_handshake *initiating_handshake; + protocol_handshake *accepting_handshake; +}; + + +#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 protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer); + + +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_session_valid(fastd_context *ctx, const protocol_session *session) { + return ctx->conf->method->session_is_valid(ctx, session->method_state); +} + +static inline void check_session_refresh(fastd_context *ctx, fastd_peer *peer) { + protocol_session *session = &peer->protocol_state->session; + + if (!session->refreshing && ctx->conf->method->session_want_refresh(ctx, session->method_state)) { + pr_debug(ctx, "refreshing session with %P", peer); + session->refreshing = true; + fastd_task_schedule_handshake(ctx, peer, 0); + } +} + +static fastd_protocol_config* protocol_init(fastd_context *ctx) { + fastd_protocol_config *protocol_config = malloc(sizeof(fastd_protocol_config)); + + if (!ctx->conf->secret) + exit_error(ctx, "no secret key configured"); + + if (!read_key(protocol_config->secret_key.s, ctx->conf->secret)) + exit_error(ctx, "invalid secret key"); + + ecc_25519_work work; + ecc_25519_scalarmult_base(&work, &protocol_config->secret_key); + ecc_25519_store(&protocol_config->public_key, &work); + + return protocol_config; +} + +static void protocol_peer_configure(fastd_context *ctx, fastd_peer_config *peer_conf) { + ecc_public_key_256 key; + + if (!peer_conf->key) { + pr_warn(ctx, "no key configured for `%s', disabling peer", peer_conf->name); + peer_conf->enabled = false; + return; + } + + if (!read_key(key.p, peer_conf->key)) { + pr_warn(ctx, "invalid key configured for `%s', disabling peer", peer_conf->name); + peer_conf->enabled = false; + return; + } + + peer_conf->protocol_config = malloc(sizeof(fastd_protocol_peer_config)); + peer_conf->protocol_config->public_key = key; +} + +static void init_peer_state(fastd_context *ctx, fastd_peer *peer) { + if (peer->protocol_state) + return; + + peer->protocol_state = malloc(sizeof(fastd_protocol_peer_state)); + memset(peer->protocol_state, 0, sizeof(fastd_protocol_peer_state)); +} + +static inline void free_handshake(protocol_handshake *handshake) { + if (handshake) { + memset(handshake, 0, sizeof(protocol_handshake)); + free(handshake); + } +} + +static void protocol_peer_config_purged(fastd_context *ctx, fastd_peer_config *peer_conf) { + fastd_peer *peer; + for (peer = ctx->peers; peer; peer = peer->next) { + if (!peer->protocol_state) + continue; + + if (peer->protocol_state->initiating_handshake && + peer->protocol_state->initiating_handshake->peer_config == peer_conf) { + free_handshake(peer->protocol_state->initiating_handshake); + peer->protocol_state->initiating_handshake = NULL; + } + + if (peer->protocol_state->accepting_handshake && + peer->protocol_state->accepting_handshake->peer_config == peer_conf) { + free_handshake(peer->protocol_state->accepting_handshake); + peer->protocol_state->accepting_handshake = NULL; + } + } +} + +static protocol_handshake* new_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_peer_config *peer_config, bool initiate) { + protocol_handshake **handshake; + + if (initiate) + handshake = &peer->protocol_state->initiating_handshake; + else + handshake = &peer->protocol_state->accepting_handshake; + + free_handshake(*handshake); + + *handshake = malloc(sizeof(protocol_handshake)); + + (*handshake)->peer_config = peer_config; + + (*handshake)->state = HANDSHAKE_STATE_INIT; + + fastd_random_bytes(ctx, (*handshake)->secret_key.s, 32, false); + ecc_25519_secret_sanitize(&(*handshake)->secret_key, &(*handshake)->secret_key); + + ecc_25519_work work; + ecc_25519_scalarmult_base(&work, &(*handshake)->secret_key); + ecc_25519_store(&(*handshake)->public_key, &work); + + return *handshake; +} + +static void protocol_handshake_init(fastd_context *ctx, fastd_peer *peer) { + init_peer_state(ctx, peer); + + fastd_buffer buffer = fastd_handshake_new_init(ctx, peer, 3*(4+PUBLICKEYBYTES) /* sender key, receipient key, handshake key */); + + protocol_handshake *handshake = new_handshake(ctx, peer, peer->config, true); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p); + + if (handshake->peer_config) + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, handshake->peer_config->protocol_config->public_key.p); + else + pr_debug(ctx, "sending handshake to unknown peer %P", peer); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake->public_key.p); + + fastd_task_put_send_handshake(ctx, peer, buffer); +} + +static inline bool has_field(const fastd_handshake *handshake, uint8_t type, size_t length) { + return (handshake->records[type].length == length); +} + +static void respond_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { + pr_debug(ctx, "responding handshake with %P...", peer); + + uint8_t hashinput[5*PUBLICKEYBYTES]; + uint8_t hashbuf[HASHBYTES]; + uint8_t hmacbuf[HMACBYTES]; + + memcpy(hashinput, peer->protocol_state->accepting_handshake->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_key.p, PUBLICKEYBYTES); + memcpy(hashinput+2*PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+3*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); + + crypto_hash_sha256(hashbuf, hashinput, 4*PUBLICKEYBYTES); + + ecc_secret_key_256 d = {{0}}, e = {{0}}, eb, s; + + memcpy(d.s, hashbuf, HASHBYTES/2); + memcpy(e.s, hashbuf+HASHBYTES/2, HASHBYTES/2); + + d.s[15] |= 0x80; + e.s[15] |= 0x80; + + ecc_25519_secret_mult(&eb, &e, &ctx->conf->protocol_config->secret_key); + ecc_25519_secret_add(&s, &eb, &peer->protocol_state->accepting_handshake->secret_key); + + ecc_25519_work work, workX; + ecc_25519_load(&work, &peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key); + ecc_25519_load(&workX, &peer->protocol_state->accepting_handshake->peer_key); + + 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; + + ecc_25519_store(&peer->protocol_state->accepting_handshake->sigma, &work); + + memcpy(hashinput+4*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->sigma.p, PUBLICKEYBYTES); + crypto_hash_sha256(peer->protocol_state->accepting_handshake->shared_handshake_key, hashinput, 5*PUBLICKEYBYTES); + + memcpy(hashinput, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->public_key.p, PUBLICKEYBYTES); + + crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->shared_handshake_key); + + fastd_buffer buffer = fastd_handshake_new_reply(ctx, peer, handshake, 4*(4+PUBLICKEYBYTES) + 4+HMACBYTES); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf); + + fastd_task_put_send_handshake(ctx, peer, buffer); + + peer->protocol_state->accepting_handshake->state = HANDSHAKE_STATE_RESPONSE; +} + +static void establish(fastd_context *ctx, fastd_peer *peer, const fastd_peer_config *peer_config, bool initiator, + const ecc_public_key_256 *A, const ecc_public_key_256 *B, const ecc_public_key_256 *X, + const ecc_public_key_256 *Y, const ecc_public_key_256 *sigma) { + uint8_t hashinput[5*PUBLICKEYBYTES]; + uint8_t hash[HASHBYTES]; + + pr_verbose(ctx, "New session with %P established.", peer); + + if (is_session_valid(ctx, &peer->protocol_state->session) && !is_session_valid(ctx, &peer->protocol_state->old_session)) { + ctx->conf->method->session_free(ctx, peer->protocol_state->old_session.method_state); + peer->protocol_state->old_session = peer->protocol_state->session; + } + else { + ctx->conf->method->session_free(ctx, peer->protocol_state->session.method_state); + } + + memcpy(hashinput, X->p, PUBLICKEYBYTES); + memcpy(hashinput+PUBLICKEYBYTES, Y->p, PUBLICKEYBYTES); + memcpy(hashinput+2*PUBLICKEYBYTES, A->p, PUBLICKEYBYTES); + memcpy(hashinput+3*PUBLICKEYBYTES, B->p, PUBLICKEYBYTES); + memcpy(hashinput+4*PUBLICKEYBYTES, sigma->p, PUBLICKEYBYTES); + crypto_hash_sha256(hash, hashinput, 5*PUBLICKEYBYTES); + + peer->protocol_state->session.handshakes_cleaned = false; + peer->protocol_state->session.refreshing = false; + peer->protocol_state->session.method_state = ctx->conf->method->session_init(ctx, hash, HASHBYTES, initiator); + + free_handshake(peer->protocol_state->initiating_handshake); + peer->protocol_state->initiating_handshake = NULL; + + free_handshake(peer->protocol_state->accepting_handshake); + peer->protocol_state->accepting_handshake = NULL; + + fastd_peer_seen(ctx, peer); + + if (peer_config != peer->config) { + fastd_peer *perm_peer; + for (perm_peer = ctx->peers; perm_peer; perm_peer = perm_peer->next) { + if (perm_peer->config == peer_config) { + peer = fastd_peer_set_established_merge(ctx, perm_peer, peer); + break; + } + } + } + else { + fastd_peer_set_established(ctx, peer); + } + + fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000); + + if (!initiator) + protocol_send(ctx, peer, fastd_buffer_alloc(0, ctx->conf->method->min_encrypt_head_space(ctx), 0)); +} + +static void finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { + pr_debug(ctx, "finishing handshake with %P...", peer); + + uint8_t hashinput[5*PUBLICKEYBYTES]; + uint8_t hashbuf[HASHBYTES]; + uint8_t hmacbuf[HMACBYTES]; + + memcpy(hashinput, peer->protocol_state->initiating_handshake->peer_key.p, PUBLICKEYBYTES); + memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+2*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+3*PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); + + crypto_hash_sha256(hashbuf, hashinput, 4*PUBLICKEYBYTES); + + ecc_secret_key_256 d = {{0}}, e = {{0}}, da, s; + + memcpy(d.s, hashbuf, HASHBYTES/2); + memcpy(e.s, hashbuf+HASHBYTES/2, HASHBYTES/2); + + d.s[15] |= 0x80; + e.s[15] |= 0x80; + + ecc_25519_secret_mult(&da, &d, &ctx->conf->protocol_config->secret_key); + ecc_25519_secret_add(&s, &da, &peer->protocol_state->initiating_handshake->secret_key); + + ecc_25519_work work, workY; + ecc_25519_load(&work, &peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key); + ecc_25519_load(&workY, &peer->protocol_state->initiating_handshake->peer_key); + + 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; + + ecc_25519_store(&peer->protocol_state->initiating_handshake->sigma, &work); + + memcpy(hashinput+4*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->sigma.p, PUBLICKEYBYTES); + crypto_hash_sha256(peer->protocol_state->initiating_handshake->shared_handshake_key, hashinput, 5*PUBLICKEYBYTES); + + memcpy(hashinput, peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_key.p, PUBLICKEYBYTES); + + if(crypto_auth_hmacsha256_verify(handshake->records[RECORD_T].data, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->shared_handshake_key) != 0) { + pr_warn(ctx, "received invalid protocol handshake response from %P", peer); + return; + } + + memcpy(hashinput, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->public_key.p, PUBLICKEYBYTES); + crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->shared_handshake_key); + + fastd_buffer buffer = fastd_handshake_new_reply(ctx, peer, handshake, 4*(4+PUBLICKEYBYTES) + 4+HMACBYTES); + + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->public_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_key.p); + fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf); + + fastd_task_put_send_handshake(ctx, peer, buffer); + + establish(ctx, peer, peer->protocol_state->initiating_handshake->peer_config, true, + &peer->protocol_state->initiating_handshake->public_key, + &peer->protocol_state->initiating_handshake->peer_key, + &ctx->conf->protocol_config->public_key, + &peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key, + &peer->protocol_state->initiating_handshake->sigma); +} + +static void handle_finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { + pr_debug(ctx, "handling handshake finish with %P...", peer); + + uint8_t hashinput[2*PUBLICKEYBYTES]; + + memcpy(hashinput, peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); + memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_key.p, PUBLICKEYBYTES); + + if(crypto_auth_hmacsha256_verify(handshake->records[RECORD_T].data, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->shared_handshake_key) != 0) { + pr_warn(ctx, "received invalid protocol handshake finish from %P", peer); + return; + } + + establish(ctx, peer, peer->protocol_state->accepting_handshake->peer_config, false, + &peer->protocol_state->accepting_handshake->peer_key, + &peer->protocol_state->accepting_handshake->public_key, + &peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key, + &ctx->conf->protocol_config->public_key, + &peer->protocol_state->accepting_handshake->sigma); +} + +static inline const fastd_peer_config* match_sender_key(fastd_context *ctx, const fastd_peer *peer, const unsigned char key[32]) { + if (peer->config) { + if (memcmp(peer->config->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) + return peer->config; + } + + if (fastd_peer_is_temporary(peer) || fastd_peer_is_floating(peer)) { + fastd_peer_config *config; + for (config = ctx->conf->peers; config; config = config->next) { + if (!fastd_peer_config_is_floating(config)) + continue; + + if (memcmp(config->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) + return config; + } + } + + return NULL; +} + +static void kill_handshakes(fastd_context *ctx, fastd_peer *peer) { + pr_debug(ctx, "there is a handshake conflict, retrying in a moment..."); + + free_handshake(peer->protocol_state->initiating_handshake); + peer->protocol_state->initiating_handshake = NULL; + + free_handshake(peer->protocol_state->accepting_handshake); + peer->protocol_state->accepting_handshake = NULL; +} + +static void protocol_handshake_handle(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { + init_peer_state(ctx, peer); + + if (!has_field(handshake, RECORD_SENDER_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake without sender key from %P", peer); + return; + } + + const fastd_peer_config *peer_config = match_sender_key(ctx, peer, handshake->records[RECORD_SENDER_KEY].data); + + if (handshake->type > 1 && !has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake reply without receipient key from %P", peer); + return; + } + else if(has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { + if (memcmp(ctx->conf->protocol_config->public_key.p, handshake->records[RECORD_RECEIPIENT_KEY].data, PUBLICKEYBYTES) != 0) { + pr_debug(ctx, "received protocol handshake with wrong receipient key from %P", peer); + return; + } + } + + if (!has_field(handshake, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake without sender handshake key from %P", peer); + return; + } + + if (handshake->type > 1 && !has_field(handshake, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES)) { + pr_debug(ctx, "received handshake reply without receipient handshake key from %P", peer); + return; + } + + if (handshake->type > 1 && !has_field(handshake, RECORD_T, HMACBYTES)) { + pr_debug(ctx, "received handshake reply without HMAC from %P", peer); + return; + } + + switch(handshake->type) { + case 1: + new_handshake(ctx, peer, peer_config, false); + memcpy(peer->protocol_state->accepting_handshake->peer_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES); + respond_handshake(ctx, peer, handshake); + break; + + case 2: + if (!peer->protocol_state->initiating_handshake || peer->protocol_state->initiating_handshake->state != HANDSHAKE_STATE_INIT) { + pr_debug(ctx, "received unexpected handshake response from %P", peer); + return; + } + + if (peer->protocol_state->initiating_handshake->peer_config != peer_config) { + if (peer->protocol_state->initiating_handshake->peer_config) { + pr_debug(ctx, "received handshake response with wrong sender key from %P", peer); + return; + } + else { + peer->protocol_state->initiating_handshake->peer_config = peer_config; + } + } + + if (memcmp(peer->protocol_state->initiating_handshake->public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) != 0) { + pr_debug(ctx, "received handshake response with unexpected receipient handshake key from %P", peer); + return; + } + + pr_debug(ctx, "received handshake response from %P", peer); + + if (peer->protocol_state->accepting_handshake) { + kill_handshakes(ctx, peer); + return; + } + + memcpy(peer->protocol_state->initiating_handshake->peer_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES); + + finish_handshake(ctx, peer, handshake); + break; + + case 3: + if (!peer->protocol_state->accepting_handshake || peer->protocol_state->accepting_handshake->state != HANDSHAKE_STATE_RESPONSE) { + pr_debug(ctx, "received unexpected protocol handshake finish from %P", peer); + return; + } + + if (peer->protocol_state->accepting_handshake->peer_config != peer_config) { + pr_debug(ctx, "received protocol handshake finish with wrong sender key from %P", peer); + return; + } + + if (memcmp(peer->protocol_state->accepting_handshake->public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) != 0) { + pr_debug(ctx, "received handshake response with unexpected receipient handshake key from %P", peer); + return; + } + + if (memcmp(peer->protocol_state->accepting_handshake->peer_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES) != 0) { + pr_debug(ctx, "received handshake response with unexpected sender handshake key from %P", peer); + return; + } + + pr_debug(ctx, "received handshake finish from %P", peer); + + if (peer->protocol_state->initiating_handshake && peer->protocol_state->initiating_handshake->state != HANDSHAKE_STATE_INIT) { + kill_handshakes(ctx, peer); + return; + } + + handle_finish_handshake(ctx, peer, handshake); + break; + + default: + pr_debug(ctx, "received handshake reply with unknown type %u", handshake->type); + } +} + +static void protocol_handle_recv(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer) { + if (!fastd_peer_is_established(peer)) { + pr_debug(ctx, "received unexpected packet from %P", peer); + + if (fastd_peer_is_temporary(peer)) { + pr_debug(ctx, "sending handshake to temporary peer %P", peer); + fastd_task_schedule_handshake(ctx, peer, 0); + } + + goto fail; + } + + if (!peer->protocol_state || !is_session_valid(ctx, &peer->protocol_state->session)) + goto fail; + + fastd_buffer recv_buffer; + bool ok = false; + + if (is_session_valid(ctx, &peer->protocol_state->old_session)) { + if (ctx->conf->method->decrypt(ctx, peer->protocol_state->old_session.method_state, &recv_buffer, buffer)) + ok = true; + } + + if (!ok) { + if (ctx->conf->method->decrypt(ctx, peer->protocol_state->session.method_state, &recv_buffer, buffer)) { + ok = true; + + if (!peer->protocol_state->session.handshakes_cleaned) { + pr_debug(ctx, "cleaning left handshakes with %P", peer); + fastd_task_delete_peer_handshakes(ctx, peer); + peer->protocol_state->session.handshakes_cleaned = true; + + if (ctx->conf->method->session_is_initiator(ctx, peer->protocol_state->session.method_state)) + protocol_send(ctx, peer, fastd_buffer_alloc(0, ctx->conf->method->min_encrypt_head_space(ctx), 0)); + } + + if (peer->protocol_state->old_session.method_state) { + pr_debug(ctx, "invalidating old session with %P", peer); + ctx->conf->method->session_free(ctx, peer->protocol_state->old_session.method_state); + peer->protocol_state->old_session.method_state = NULL; + } + + check_session_refresh(ctx, peer); + } + } + + if (!ok) { + pr_debug(ctx, "verification failed for packet received from %P", peer); + goto fail; + } + + fastd_peer_seen(ctx, peer); + + if (recv_buffer.len) + fastd_task_put_handle_recv(ctx, peer, recv_buffer); + else + fastd_buffer_free(recv_buffer); + + return; + + fail: + fastd_buffer_free(buffer); +} + +static void protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer) { + if (!peer->protocol_state || !is_session_valid(ctx, &peer->protocol_state->session)) + goto fail; + + check_session_refresh(ctx, peer); + + protocol_session *session; + if (ctx->conf->method->session_is_initiator(ctx, peer->protocol_state->session.method_state) && is_session_valid(ctx, &peer->protocol_state->old_session)) { + pr_debug(ctx, "sending packet for old session to %P", peer); + session = &peer->protocol_state->old_session; + } + else { + session = &peer->protocol_state->session; + } + + fastd_buffer send_buffer; + if (!ctx->conf->method->encrypt(ctx, session->method_state, &send_buffer, buffer)) + goto fail; + + fastd_task_put_send(ctx, peer, send_buffer); + + fastd_task_delete_peer_keepalives(ctx, peer); + fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000); + return; + + fail: + fastd_buffer_free(buffer); +} + +static void protocol_free_peer_state(fastd_context *ctx, fastd_peer *peer) { + if (peer->protocol_state) { + free_handshake(peer->protocol_state->initiating_handshake); + free_handshake(peer->protocol_state->accepting_handshake); + + ctx->conf->method->session_free(ctx, peer->protocol_state->old_session.method_state); + ctx->conf->method->session_free(ctx, peer->protocol_state->session.method_state); + + free(peer->protocol_state); + } +} + + +static void hexdump(const char *desc, unsigned char d[32]) { + printf("%s", desc); + + int i; + for (i = 0; i < 32; i++) + printf("%02x", d[i]); + + printf("\n"); +} + +static void protocol_generate_key(fastd_context *ctx) { + ecc_secret_key_256 secret_key; + ecc_public_key_256 public_key; + + pr_info(ctx, "Reading 32 bytes from /dev/random..."); + + fastd_random_bytes(ctx, secret_key.s, 32, true); + ecc_25519_secret_sanitize(&secret_key, &secret_key); + + ecc_25519_work work; + ecc_25519_scalarmult_base(&work, &secret_key); + ecc_25519_store(&public_key, &work); + + hexdump("Secret: ", secret_key.s); + hexdump("Public: ", public_key.p); +} + + +const fastd_protocol fastd_protocol_ec25519_fhmqvc = { + .name = "ec25519-fhmqvc", + + .init = protocol_init, + .peer_configure = protocol_peer_configure, + .peer_config_purged = protocol_peer_config_purged, + + .handshake_init = protocol_handshake_init, + .handshake_handle = protocol_handshake_handle, + + .handle_recv = protocol_handle_recv, + .send = protocol_send, + + .free_peer_state = protocol_free_peer_state, + + .generate_key = protocol_generate_key, +}; diff --git a/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c b/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c deleted file mode 100644 index 8dbf10e..0000000 --- a/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c +++ /dev/null @@ -1,865 +0,0 @@ -/* - Copyright (c) 2012, 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. -*/ - - -#define _GNU_SOURCE - -#include "fastd.h" -#include "handshake.h" -#include "peer.h" -#include "task.h" - -#include - -#include -#include -#include -#include - - -#define NONCEBYTES 7 -#define PUBLICKEYBYTES 32 -#define SECRETKEYBYTES 32 -#define HMACBYTES crypto_auth_hmacsha256_BYTES -#define HASHBYTES crypto_hash_sha256_BYTES - - -#if HASHBYTES != crypto_auth_hmacsha256_KEYBYTES -#error bug: HASHBYTES != crypto_auth_hmacsha256_KEYBYTES -#endif - -#if HASHBYTES != crypto_secretbox_xsalsa20poly1305_KEYBYTES -#error bug: HASHBYTES != crypto_secretbox_xsalsa20poly1305_KEYBYTES -#endif - -#if HASHBYTES != SECRETKEYBYTES -#error bug: HASHBYTES != SECRETKEYBYTES -#endif - -#if crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES < NONCEBYTES -#error bug: crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES < NONCEBYTES -#endif - - -struct _fastd_protocol_config { - ecc_secret_key_256 secret_key; - ecc_public_key_256 public_key; -}; - -typedef enum _handshake_state { - HANDSHAKE_STATE_INIT, - HANDSHAKE_STATE_RESPONSE, - HANDSHAKE_STATE_ESTABLISHED -} handshake_state; - -struct _fastd_protocol_peer_config { - ecc_public_key_256 public_key; -}; - -typedef struct _protocol_handshake { - const fastd_peer_config *peer_config; - - handshake_state state; - ecc_secret_key_256 secret_key; - ecc_public_key_256 public_key; - ecc_public_key_256 peer_key; - ecc_public_key_256 sigma; - uint8_t shared_handshake_key[HASHBYTES]; -} protocol_handshake; - -typedef struct _protocol_session { - bool handshakes_cleaned; - - struct timespec valid_till; - struct timespec refresh_after; - bool refreshing; - uint8_t key[HASHBYTES]; - - uint8_t send_nonce[NONCEBYTES]; - uint8_t receive_nonce[NONCEBYTES]; -} protocol_session; - -struct _fastd_protocol_peer_state { - protocol_session old_session; - protocol_session session; - - protocol_handshake *initiating_handshake; - protocol_handshake *accepting_handshake; -}; - - -#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 protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer); - - -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_nonce_zero(const uint8_t nonce[NONCEBYTES]) { - int i; - for (i = 0; i < NONCEBYTES; i++) { - if (nonce[i] != 0) - return false; - } - - return true; -} - -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_session_valid(fastd_context *ctx, const protocol_session *session) { - return timespec_after(&session->valid_till, &ctx->now); -} - -static inline bool is_session_zero(fastd_context *ctx, const protocol_session *session) { - return (session->valid_till.tv_sec == 0); -} - -static inline bool is_session_initiator(const protocol_session *session) { - return (session->send_nonce[0] & 1); -} - -static inline void check_session_refresh(fastd_context *ctx, fastd_peer *peer) { - protocol_session *session = &peer->protocol_state->session; - - if (is_session_initiator(session) && !session->refreshing && timespec_after(&ctx->now, &session->refresh_after)) { - pr_debug(ctx, "refreshing session with %P", peer); - session->refreshing = true; - fastd_task_schedule_handshake(ctx, peer, 0); - } -} - -static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES]) { - if ((nonce[0] & 1) != (old_nonce[0] & 1)) - return false; - - int i; - for (i = NONCEBYTES-1; i >= 0; i--) { - if (nonce[i] > old_nonce[i]) - return true; - if (nonce[i] < old_nonce[i]) - return false; - } - - return false; -} - -static fastd_protocol_config* protocol_init(fastd_context *ctx) { - fastd_protocol_config *protocol_config = malloc(sizeof(fastd_protocol_config)); - - if (!ctx->conf->secret) - exit_error(ctx, "no secret key configured"); - - if (!read_key(protocol_config->secret_key.s, ctx->conf->secret)) - exit_error(ctx, "invalid secret key"); - - ecc_25519_work work; - ecc_25519_scalarmult_base(&work, &protocol_config->secret_key); - ecc_25519_store(&protocol_config->public_key, &work); - - return protocol_config; -} - -static void protocol_peer_configure(fastd_context *ctx, fastd_peer_config *peer_conf) { - ecc_public_key_256 key; - - if (!peer_conf->key) { - pr_warn(ctx, "no key configured for `%s', disabling peer", peer_conf->name); - peer_conf->enabled = false; - return; - } - - if (!read_key(key.p, peer_conf->key)) { - pr_warn(ctx, "invalid key configured for `%s', disabling peer", peer_conf->name); - peer_conf->enabled = false; - return; - } - - peer_conf->protocol_config = malloc(sizeof(fastd_protocol_peer_config)); - peer_conf->protocol_config->public_key = key; -} - -static void init_peer_state(fastd_context *ctx, fastd_peer *peer) { - if (peer->protocol_state) - return; - - peer->protocol_state = malloc(sizeof(fastd_protocol_peer_state)); - memset(peer->protocol_state, 0, sizeof(fastd_protocol_peer_state)); -} - -static inline void free_handshake(protocol_handshake *handshake) { - if (handshake) { - memset(handshake, 0, sizeof(protocol_handshake)); - free(handshake); - } -} - -static void protocol_peer_config_purged(fastd_context *ctx, fastd_peer_config *peer_conf) { - fastd_peer *peer; - for (peer = ctx->peers; peer; peer = peer->next) { - if (!peer->protocol_state) - continue; - - if (peer->protocol_state->initiating_handshake && - peer->protocol_state->initiating_handshake->peer_config == peer_conf) { - free_handshake(peer->protocol_state->initiating_handshake); - peer->protocol_state->initiating_handshake = NULL; - } - - if (peer->protocol_state->accepting_handshake && - peer->protocol_state->accepting_handshake->peer_config == peer_conf) { - free_handshake(peer->protocol_state->accepting_handshake); - peer->protocol_state->accepting_handshake = NULL; - } - } -} - -static size_t protocol_max_packet_size(fastd_context *ctx) { - return (fastd_max_packet_size(ctx) + NONCEBYTES + crypto_secretbox_xsalsa20poly1305_ZEROBYTES - crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); -} - -static size_t protocol_min_encrypt_head_space(fastd_context *ctx) { - return crypto_secretbox_xsalsa20poly1305_ZEROBYTES; -} - -static size_t protocol_min_decrypt_head_space(fastd_context *ctx) { - return (crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES - NONCEBYTES); -} - -static protocol_handshake* new_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_peer_config *peer_config, bool initiate) { - protocol_handshake **handshake; - - if (initiate) - handshake = &peer->protocol_state->initiating_handshake; - else - handshake = &peer->protocol_state->accepting_handshake; - - free_handshake(*handshake); - - *handshake = malloc(sizeof(protocol_handshake)); - - (*handshake)->peer_config = peer_config; - - (*handshake)->state = HANDSHAKE_STATE_INIT; - - fastd_random_bytes(ctx, (*handshake)->secret_key.s, 32, false); - ecc_25519_secret_sanitize(&(*handshake)->secret_key, &(*handshake)->secret_key); - - ecc_25519_work work; - ecc_25519_scalarmult_base(&work, &(*handshake)->secret_key); - ecc_25519_store(&(*handshake)->public_key, &work); - - return *handshake; -} - -static void protocol_handshake_init(fastd_context *ctx, fastd_peer *peer) { - init_peer_state(ctx, peer); - - fastd_buffer buffer = fastd_handshake_new_init(ctx, peer, 3*(4+PUBLICKEYBYTES) /* sender key, receipient key, handshake key */); - - protocol_handshake *handshake = new_handshake(ctx, peer, peer->config, true); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p); - - if (handshake->peer_config) - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, handshake->peer_config->protocol_config->public_key.p); - else - pr_debug(ctx, "sending handshake to unknown peer %P", peer); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake->public_key.p); - - fastd_task_put_send_handshake(ctx, peer, buffer); -} - -static inline bool has_field(const fastd_handshake *handshake, uint8_t type, size_t length) { - return (handshake->records[type].length == length); -} - -static void respond_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { - pr_debug(ctx, "responding handshake with %P...", peer); - - uint8_t hashinput[5*PUBLICKEYBYTES]; - uint8_t hashbuf[HASHBYTES]; - uint8_t hmacbuf[HMACBYTES]; - - memcpy(hashinput, peer->protocol_state->accepting_handshake->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_key.p, PUBLICKEYBYTES); - memcpy(hashinput+2*PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+3*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); - - crypto_hash_sha256(hashbuf, hashinput, 4*PUBLICKEYBYTES); - - ecc_secret_key_256 d = {{0}}, e = {{0}}, eb, s; - - memcpy(d.s, hashbuf, HASHBYTES/2); - memcpy(e.s, hashbuf+HASHBYTES/2, HASHBYTES/2); - - d.s[15] |= 0x80; - e.s[15] |= 0x80; - - ecc_25519_secret_mult(&eb, &e, &ctx->conf->protocol_config->secret_key); - ecc_25519_secret_add(&s, &eb, &peer->protocol_state->accepting_handshake->secret_key); - - ecc_25519_work work, workX; - ecc_25519_load(&work, &peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key); - ecc_25519_load(&workX, &peer->protocol_state->accepting_handshake->peer_key); - - 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; - - ecc_25519_store(&peer->protocol_state->accepting_handshake->sigma, &work); - - memcpy(hashinput+4*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->sigma.p, PUBLICKEYBYTES); - crypto_hash_sha256(peer->protocol_state->accepting_handshake->shared_handshake_key, hashinput, 5*PUBLICKEYBYTES); - - memcpy(hashinput, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->public_key.p, PUBLICKEYBYTES); - - crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->shared_handshake_key); - - fastd_buffer buffer = fastd_handshake_new_reply(ctx, peer, handshake, 4*(4+PUBLICKEYBYTES) + 4+HMACBYTES); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf); - - fastd_task_put_send_handshake(ctx, peer, buffer); - - peer->protocol_state->accepting_handshake->state = HANDSHAKE_STATE_RESPONSE; -} - -static void establish(fastd_context *ctx, fastd_peer *peer, const fastd_peer_config *peer_config, bool initiator, - const ecc_public_key_256 *A, const ecc_public_key_256 *B, const ecc_public_key_256 *X, - const ecc_public_key_256 *Y, const ecc_public_key_256 *sigma) { - int i; - uint8_t hashinput[5*PUBLICKEYBYTES]; - - pr_verbose(ctx, "New session with %P established.", peer); - - if (is_session_valid(ctx, &peer->protocol_state->session) && !is_session_valid(ctx, &peer->protocol_state->old_session)) - peer->protocol_state->old_session = peer->protocol_state->session; - - memcpy(hashinput, X->p, PUBLICKEYBYTES); - memcpy(hashinput+PUBLICKEYBYTES, Y->p, PUBLICKEYBYTES); - memcpy(hashinput+2*PUBLICKEYBYTES, A->p, PUBLICKEYBYTES); - memcpy(hashinput+3*PUBLICKEYBYTES, B->p, PUBLICKEYBYTES); - memcpy(hashinput+4*PUBLICKEYBYTES, sigma->p, PUBLICKEYBYTES); - crypto_hash_sha256(peer->protocol_state->session.key, hashinput, 5*PUBLICKEYBYTES); - - peer->protocol_state->session.handshakes_cleaned = false; - - peer->protocol_state->session.valid_till = ctx->now; - peer->protocol_state->session.valid_till.tv_sec += ctx->conf->key_valid; - - peer->protocol_state->session.refresh_after = ctx->now; - peer->protocol_state->session.refresh_after.tv_sec += ctx->conf->key_refresh; - peer->protocol_state->session.refreshing = false; - - peer->protocol_state->session.send_nonce[0] = initiator ? 3 : 2; - peer->protocol_state->session.receive_nonce[0] = initiator ? 0 : 1; - for (i = 1; i < NONCEBYTES; i++) { - peer->protocol_state->session.send_nonce[i] = 0; - peer->protocol_state->session.receive_nonce[i] = 0; - } - - free_handshake(peer->protocol_state->initiating_handshake); - peer->protocol_state->initiating_handshake = NULL; - - free_handshake(peer->protocol_state->accepting_handshake); - peer->protocol_state->accepting_handshake = NULL; - - fastd_peer_seen(ctx, peer); - - if (peer_config != peer->config) { - fastd_peer *perm_peer; - for (perm_peer = ctx->peers; perm_peer; perm_peer = perm_peer->next) { - if (perm_peer->config == peer_config) { - peer = fastd_peer_set_established_merge(ctx, perm_peer, peer); - break; - } - } - } - else { - fastd_peer_set_established(ctx, peer); - } - - fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000); - - if (!initiator) - protocol_send(ctx, peer, fastd_buffer_alloc(0, protocol_min_encrypt_head_space(ctx), 0)); -} - -static void finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { - pr_debug(ctx, "finishing handshake with %P...", peer); - - uint8_t hashinput[5*PUBLICKEYBYTES]; - uint8_t hashbuf[HASHBYTES]; - uint8_t hmacbuf[HMACBYTES]; - - memcpy(hashinput, peer->protocol_state->initiating_handshake->peer_key.p, PUBLICKEYBYTES); - memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+2*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+3*PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); - - crypto_hash_sha256(hashbuf, hashinput, 4*PUBLICKEYBYTES); - - ecc_secret_key_256 d = {{0}}, e = {{0}}, da, s; - - memcpy(d.s, hashbuf, HASHBYTES/2); - memcpy(e.s, hashbuf+HASHBYTES/2, HASHBYTES/2); - - d.s[15] |= 0x80; - e.s[15] |= 0x80; - - ecc_25519_secret_mult(&da, &d, &ctx->conf->protocol_config->secret_key); - ecc_25519_secret_add(&s, &da, &peer->protocol_state->initiating_handshake->secret_key); - - ecc_25519_work work, workY; - ecc_25519_load(&work, &peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key); - ecc_25519_load(&workY, &peer->protocol_state->initiating_handshake->peer_key); - - 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; - - ecc_25519_store(&peer->protocol_state->initiating_handshake->sigma, &work); - - memcpy(hashinput+4*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->sigma.p, PUBLICKEYBYTES); - crypto_hash_sha256(peer->protocol_state->initiating_handshake->shared_handshake_key, hashinput, 5*PUBLICKEYBYTES); - - memcpy(hashinput, peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_key.p, PUBLICKEYBYTES); - - if(crypto_auth_hmacsha256_verify(handshake->records[RECORD_T].data, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->shared_handshake_key) != 0) { - pr_warn(ctx, "received invalid protocol handshake response from %P", peer); - return; - } - - memcpy(hashinput, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->public_key.p, PUBLICKEYBYTES); - crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->shared_handshake_key); - - fastd_buffer buffer = fastd_handshake_new_reply(ctx, peer, handshake, 4*(4+PUBLICKEYBYTES) + 4+HMACBYTES); - - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->public_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer->protocol_state->initiating_handshake->peer_key.p); - fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf); - - fastd_task_put_send_handshake(ctx, peer, buffer); - - establish(ctx, peer, peer->protocol_state->initiating_handshake->peer_config, true, - &peer->protocol_state->initiating_handshake->public_key, - &peer->protocol_state->initiating_handshake->peer_key, - &ctx->conf->protocol_config->public_key, - &peer->protocol_state->initiating_handshake->peer_config->protocol_config->public_key, - &peer->protocol_state->initiating_handshake->sigma); -} - -static void handle_finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { - pr_debug(ctx, "handling handshake finish with %P...", peer); - - uint8_t hashinput[2*PUBLICKEYBYTES]; - - memcpy(hashinput, peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key.p, PUBLICKEYBYTES); - memcpy(hashinput+PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->peer_key.p, PUBLICKEYBYTES); - - if(crypto_auth_hmacsha256_verify(handshake->records[RECORD_T].data, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->shared_handshake_key) != 0) { - pr_warn(ctx, "received invalid protocol handshake finish from %P", peer); - return; - } - - establish(ctx, peer, peer->protocol_state->accepting_handshake->peer_config, false, - &peer->protocol_state->accepting_handshake->peer_key, - &peer->protocol_state->accepting_handshake->public_key, - &peer->protocol_state->accepting_handshake->peer_config->protocol_config->public_key, - &ctx->conf->protocol_config->public_key, - &peer->protocol_state->accepting_handshake->sigma); -} - -static inline const fastd_peer_config* match_sender_key(fastd_context *ctx, const fastd_peer *peer, const unsigned char key[32]) { - if (peer->config) { - if (memcmp(peer->config->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) - return peer->config; - } - - if (fastd_peer_is_temporary(peer) || fastd_peer_is_floating(peer)) { - fastd_peer_config *config; - for (config = ctx->conf->peers; config; config = config->next) { - if (!fastd_peer_config_is_floating(config)) - continue; - - if (memcmp(config->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) - return config; - } - } - - return NULL; -} - -static void kill_handshakes(fastd_context *ctx, fastd_peer *peer) { - pr_debug(ctx, "there is a handshake conflict, retrying in a moment..."); - - free_handshake(peer->protocol_state->initiating_handshake); - peer->protocol_state->initiating_handshake = NULL; - - free_handshake(peer->protocol_state->accepting_handshake); - peer->protocol_state->accepting_handshake = NULL; -} - -static void protocol_handshake_handle(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { - init_peer_state(ctx, peer); - - if (!has_field(handshake, RECORD_SENDER_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake without sender key from %P", peer); - return; - } - - const fastd_peer_config *peer_config = match_sender_key(ctx, peer, handshake->records[RECORD_SENDER_KEY].data); - - if (handshake->type > 1 && !has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake reply without receipient key from %P", peer); - return; - } - else if(has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) { - if (memcmp(ctx->conf->protocol_config->public_key.p, handshake->records[RECORD_RECEIPIENT_KEY].data, PUBLICKEYBYTES) != 0) { - pr_debug(ctx, "received protocol handshake with wrong receipient key from %P", peer); - return; - } - } - - if (!has_field(handshake, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake without sender handshake key from %P", peer); - return; - } - - if (handshake->type > 1 && !has_field(handshake, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES)) { - pr_debug(ctx, "received handshake reply without receipient handshake key from %P", peer); - return; - } - - if (handshake->type > 1 && !has_field(handshake, RECORD_T, HMACBYTES)) { - pr_debug(ctx, "received handshake reply without HMAC from %P", peer); - return; - } - - switch(handshake->type) { - case 1: - new_handshake(ctx, peer, peer_config, false); - memcpy(peer->protocol_state->accepting_handshake->peer_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES); - respond_handshake(ctx, peer, handshake); - break; - - case 2: - if (!peer->protocol_state->initiating_handshake || peer->protocol_state->initiating_handshake->state != HANDSHAKE_STATE_INIT) { - pr_debug(ctx, "received unexpected handshake response from %P", peer); - return; - } - - if (peer->protocol_state->initiating_handshake->peer_config != peer_config) { - if (peer->protocol_state->initiating_handshake->peer_config) { - pr_debug(ctx, "received handshake response with wrong sender key from %P", peer); - return; - } - else { - peer->protocol_state->initiating_handshake->peer_config = peer_config; - } - } - - if (memcmp(peer->protocol_state->initiating_handshake->public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) != 0) { - pr_debug(ctx, "received handshake response with unexpected receipient handshake key from %P", peer); - return; - } - - pr_debug(ctx, "received handshake response from %P", peer); - - if (peer->protocol_state->accepting_handshake) { - kill_handshakes(ctx, peer); - return; - } - - memcpy(peer->protocol_state->initiating_handshake->peer_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES); - - finish_handshake(ctx, peer, handshake); - break; - - case 3: - if (!peer->protocol_state->accepting_handshake || peer->protocol_state->accepting_handshake->state != HANDSHAKE_STATE_RESPONSE) { - pr_debug(ctx, "received unexpected protocol handshake finish from %P", peer); - return; - } - - if (peer->protocol_state->accepting_handshake->peer_config != peer_config) { - pr_debug(ctx, "received protocol handshake finish with wrong sender key from %P", peer); - return; - } - - if (memcmp(peer->protocol_state->accepting_handshake->public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) != 0) { - pr_debug(ctx, "received handshake response with unexpected receipient handshake key from %P", peer); - return; - } - - if (memcmp(peer->protocol_state->accepting_handshake->peer_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES) != 0) { - pr_debug(ctx, "received handshake response with unexpected sender handshake key from %P", peer); - return; - } - - pr_debug(ctx, "received handshake finish from %P", peer); - - if (peer->protocol_state->initiating_handshake && peer->protocol_state->initiating_handshake->state != HANDSHAKE_STATE_INIT) { - kill_handshakes(ctx, peer); - return; - } - - handle_finish_handshake(ctx, peer, handshake); - break; - - default: - pr_debug(ctx, "received handshake reply with unknown type %u", handshake->type); - } -} - -static void protocol_handle_recv(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer) { - if (!fastd_peer_is_established(peer)) { - pr_debug(ctx, "received unexpected packet from %P", peer); - - if (fastd_peer_is_temporary(peer)) { - pr_debug(ctx, "sending handshake to temporary peer %P", peer); - fastd_task_schedule_handshake(ctx, peer, 0); - } - - goto end; - } - - if (buffer.len < NONCEBYTES) - goto end; - - if (!peer->protocol_state || !is_session_valid(ctx, &peer->protocol_state->session)) { - goto end; - } - - uint8_t nonce[crypto_secretbox_xsalsa20poly1305_NONCEBYTES]; - memcpy(nonce, buffer.data, NONCEBYTES); - memset(nonce+NONCEBYTES, 0, crypto_secretbox_xsalsa20poly1305_NONCEBYTES-NONCEBYTES); - - fastd_buffer_pull_head(&buffer, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); - memset(buffer.data, 0, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); - - fastd_buffer recv_buffer = fastd_buffer_alloc(buffer.len, 0, 0); - - protocol_session *session = NULL; - - if (is_session_valid(ctx, &peer->protocol_state->old_session)) { - if (is_nonce_valid(nonce, peer->protocol_state->old_session.receive_nonce)) { - if (crypto_secretbox_xsalsa20poly1305_open(recv_buffer.data, buffer.data, buffer.len, nonce, peer->protocol_state->old_session.key) == 0) { - pr_debug(ctx, "received packet for old session from %P", peer); - session = &peer->protocol_state->old_session; - } - } - } - - if (!session) { - session = &peer->protocol_state->session; - - if (!is_nonce_valid(nonce, session->receive_nonce)) { - pr_debug(ctx, "received packet with invalid nonce from %P", peer); - fastd_buffer_free(recv_buffer); - goto end; - } - - if (crypto_secretbox_xsalsa20poly1305_open(recv_buffer.data, buffer.data, buffer.len, nonce, session->key) == 0) { - if (!session->handshakes_cleaned) { - pr_debug(ctx, "cleaning left handshakes with %P", peer); - fastd_task_delete_peer_handshakes(ctx, peer); - session->handshakes_cleaned = true; - - if (is_session_initiator(session)) - protocol_send(ctx, peer, fastd_buffer_alloc(0, protocol_min_encrypt_head_space(ctx), 0)); - } - - if (!is_session_zero(ctx, &peer->protocol_state->old_session)) { - pr_debug(ctx, "invalidating old session with %P", peer); - memset(&peer->protocol_state->old_session, 0, sizeof(protocol_session)); - } - - check_session_refresh(ctx, peer); - } - else { - pr_debug(ctx, "verification failed for packet received from %P", peer); - fastd_buffer_free(recv_buffer); - goto end; - } - } - - fastd_peer_seen(ctx, peer); - - fastd_buffer_push_head(&recv_buffer, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); - - if (recv_buffer.len) - fastd_task_put_handle_recv(ctx, peer, recv_buffer); - else - fastd_buffer_free(recv_buffer); - - memcpy(session->receive_nonce, nonce, NONCEBYTES); - - end: - fastd_buffer_free(buffer); -} - -static void protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer) { - if (!peer->protocol_state || !is_session_valid(ctx, &peer->protocol_state->session)) { - fastd_buffer_free(buffer); - return; - } - - check_session_refresh(ctx, peer); - - protocol_session *session; - if (is_session_initiator(&peer->protocol_state->session) && is_session_valid(ctx, &peer->protocol_state->old_session)) { - pr_debug(ctx, "sending packet for old session to %P", peer); - session = &peer->protocol_state->old_session; - } - else { - session = &peer->protocol_state->session; - } - - fastd_buffer_pull_head(&buffer, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); - memset(buffer.data, 0, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); - - fastd_buffer send_buffer = fastd_buffer_alloc(buffer.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(send_buffer.data, buffer.data, buffer.len, nonce, session->key); - - fastd_buffer_free(buffer); - - fastd_buffer_push_head(&send_buffer, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); - memcpy(send_buffer.data, session->send_nonce, NONCEBYTES); - - fastd_task_put_send(ctx, peer, send_buffer); - - increment_nonce(session->send_nonce); - - fastd_task_delete_peer_keepalives(ctx, peer); - fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000); -} - -static void protocol_free_peer_state(fastd_context *ctx, fastd_peer *peer) { - if (peer->protocol_state) { - free_handshake(peer->protocol_state->initiating_handshake); - free_handshake(peer->protocol_state->accepting_handshake); - - free(peer->protocol_state); - } -} - - -static void hexdump(const char *desc, unsigned char d[32]) { - printf("%s", desc); - - int i; - for (i = 0; i < 32; i++) - printf("%02x", d[i]); - - printf("\n"); -} - -static void protocol_generate_key(fastd_context *ctx) { - ecc_secret_key_256 secret_key; - ecc_public_key_256 public_key; - - pr_info(ctx, "Reading 32 bytes from /dev/random..."); - - fastd_random_bytes(ctx, secret_key.s, 32, true); - ecc_25519_secret_sanitize(&secret_key, &secret_key); - - ecc_25519_work work; - ecc_25519_scalarmult_base(&work, &secret_key); - ecc_25519_store(&public_key, &work); - - hexdump("Secret: ", secret_key.s); - hexdump("Public: ", public_key.p); -} - - -const fastd_protocol fastd_protocol_ec25519_fhmqvc_xsalsa20_poly1305 = { - .name = "ec25519-fhmqvc-xsalsa20-poly1305", - - .init = protocol_init, - .peer_configure = protocol_peer_configure, - .peer_config_purged = protocol_peer_config_purged, - - .max_packet_size = protocol_max_packet_size, - .min_encrypt_head_space = protocol_min_encrypt_head_space, - .min_decrypt_head_space = protocol_min_decrypt_head_space, - - .handshake_init = protocol_handshake_init, - .handshake_handle = protocol_handshake_handle, - - .handle_recv = protocol_handle_recv, - .send = protocol_send, - - .free_peer_state = protocol_free_peer_state, - - .generate_key = protocol_generate_key, -}; diff --git a/src/protocol_null.c b/src/protocol_null.c deleted file mode 100644 index b3c0b17..0000000 --- a/src/protocol_null.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - Copyright (c) 2012, 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. -*/ - - -#define _GNU_SOURCE - -#include "fastd.h" -#include "task.h" -#include "peer.h" -#include "handshake.h" - -#include - - -static fastd_protocol_config* protocol_init(fastd_context *ctx) { - if (ctx->conf->n_floating > 1) - exit_error(ctx, "with protocol `null' use can't define more than one floating peer"); - - return NULL; -} - -static void protocol_peer_configure(fastd_context *ctx, fastd_peer_config *peer_conf) { -} - -static void protocol_peer_config_purged(fastd_context *ctx, fastd_peer_config *peer_conf) { -} - -static size_t protocol_max_packet_size(fastd_context *ctx) { - return fastd_max_packet_size(ctx); -} - -static size_t protocol_min_head_space(fastd_context *ctx) { - return 0; -} - -static void protocol_handshake_init(fastd_context *ctx, fastd_peer *peer) { - fastd_buffer buffer = fastd_handshake_new_init(ctx, peer, 0); - fastd_task_put_send_handshake(ctx, peer, buffer); -} - -static void establish(fastd_context *ctx, fastd_peer *peer) { - fastd_peer_seen(ctx, peer); - - if (fastd_peer_is_temporary(peer)) { - fastd_peer *perm_peer; - for (perm_peer = ctx->peers; perm_peer; perm_peer = perm_peer->next) { - if (fastd_peer_is_floating(perm_peer)) - break; - } - - if (!perm_peer) { - return; - } - - fastd_peer_set_established_merge(ctx, perm_peer, peer); - } - else { - fastd_peer_set_established(ctx, peer); - } - - fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000); -} - -static void protocol_handshake_handle(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { - fastd_buffer buffer; - - switch(handshake->type) { - case 1: - buffer = fastd_handshake_new_reply(ctx, peer, handshake, 0); - fastd_task_put_send_handshake(ctx, peer, buffer); - break; - - case 2: - establish(ctx, peer); - buffer = fastd_handshake_new_reply(ctx, peer, handshake, 0); - fastd_task_put_send_handshake(ctx, peer, buffer); - break; - - case 3: - establish(ctx, peer); - break; - - default: - pr_debug(ctx, "received handshake reply with unknown type %u", handshake->type); - } - -} - -static void protocol_handle_recv(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer) { - if (fastd_peer_is_established(peer) && buffer.len) { - /* this could be optimized a bit */ - fastd_task_delete_peer_handshakes(ctx, peer); - - fastd_peer_seen(ctx, peer); - fastd_task_put_handle_recv(ctx, peer, buffer); - - fastd_task_delete_peer_keepalives(ctx, peer); - fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000); - } - else { - fastd_buffer_free(buffer); - } -} - -static void protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buffer) { - fastd_task_put_send(ctx, peer, buffer); -} - -static void protocol_free_peer_state(fastd_context *ctx, fastd_peer *peer) { -} - -static void protocol_generate_key(fastd_context *ctx) { - exit_error(ctx, "trying to generate key for `null' protocol"); -} - -const fastd_protocol fastd_protocol_null = { - .name = "null", - - .init = protocol_init, - .peer_configure = protocol_peer_configure, - .peer_config_purged = protocol_peer_config_purged, - - .max_packet_size = protocol_max_packet_size, - .min_encrypt_head_space = protocol_min_head_space, - .min_decrypt_head_space = protocol_min_head_space, - - .handshake_init = protocol_handshake_init, - .handshake_handle = protocol_handshake_handle, - - .handle_recv = protocol_handle_recv, - .send = protocol_send, - - .free_peer_state = protocol_free_peer_state, - - .generate_key = protocol_generate_key, -}; diff --git a/src/types.h b/src/types.h index 504b6dd..3808b32 100644 --- a/src/types.h +++ b/src/types.h @@ -66,14 +66,16 @@ typedef struct _fastd_config fastd_config; typedef struct _fastd_context fastd_context; typedef struct _fastd_protocol fastd_protocol; +typedef struct _fastd_method fastd_method; typedef struct _fastd_handshake fastd_handshake; typedef struct _fastd_string_stack fastd_string_stack; -/* May be defined by the protocol however it likes */ +/* May be defined by the protocol/method however it likes */ typedef struct _fastd_protocol_config fastd_protocol_config; typedef struct _fastd_protocol_peer_config fastd_protocol_peer_config; typedef struct _fastd_protocol_peer_state fastd_protocol_peer_state; +typedef struct _fastd_method_session_state fastd_method_session_state; #endif /* _FASTD_TYPES_H_ */ -- cgit v1.2.3