From ee8b2de4b795fbc3b292e14c9f088244a7e08690 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 31 Mar 2012 19:38:18 +0200 Subject: ecfxp: refresh session key periodically --- src/config.c | 3 +- src/fastd.c | 10 +-- src/fastd.h | 8 +++ src/peer.c | 37 ++++++----- src/peer.h | 3 +- src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c | 87 +++++++++++++++++++++---- 6 files changed, 109 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/config.c b/src/config.c index 61cd42f..5acdc21 100644 --- a/src/config.c +++ b/src/config.c @@ -65,7 +65,8 @@ static void default_config(fastd_config *conf) { conf->protocol = &fastd_protocol_null; conf->secret = NULL; - conf->key_valid = 3600; + conf->key_valid = 3600; /* 60 minutes */ + conf->key_refresh = 3300; /* 55 minutes */ conf->peers = NULL; diff --git a/src/fastd.c b/src/fastd.c index a651946..a0acbb1 100644 --- a/src/fastd.c +++ b/src/fastd.c @@ -207,13 +207,13 @@ static void handle_tasks(fastd_context *ctx) { break; case TASK_HANDSHAKE: - if (task->peer->state != STATE_WAIT && task->peer->state != STATE_TEMP) - break; - pr_debug(ctx, "Sending handshake to %P...", task->peer); ctx->conf->protocol->handshake_init(ctx, task->peer); - fastd_task_schedule_handshake(ctx, task->peer, 20000); + if (fastd_peer_is_established(task->peer)) + fastd_task_schedule_handshake(ctx, task->peer, fastd_rand(ctx, 10000, 20000)); + else + fastd_task_schedule_handshake(ctx, task->peer, 20000); break; default: @@ -412,6 +412,8 @@ int main(int argc, char *argv[]) { fastd_context ctx; memset(&ctx, 0, sizeof(ctx)); + fastd_random_bytes(&ctx, &ctx.randseed, sizeof(ctx.randseed), false); + fastd_config conf; fastd_configure(&ctx, &conf, argc, argv); ctx.conf = &conf; diff --git a/src/fastd.h b/src/fastd.h index dc425ba..e9d30a0 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -93,6 +93,7 @@ struct _fastd_config { fastd_protocol *protocol; char *secret; unsigned key_valid; + unsigned key_refresh; fastd_peer_config *peers; @@ -123,6 +124,8 @@ struct _fastd_context { size_t eth_addr_size; size_t n_eth_addr; fastd_peer_eth_addr *eth_addr; + + unsigned int randseed; }; void fastd_printf(const fastd_context *ctx, const char *format, ...); @@ -133,6 +136,11 @@ void fastd_configure(fastd_context *ctx, fastd_config *conf, int argc, char *con void fastd_random_bytes(fastd_context *ctx, void *buffer, size_t len, bool secure); +static inline int fastd_rand(fastd_context *ctx, int min, int max) { + unsigned int r = (unsigned int)rand_r(&ctx->randseed); + return (r%(max-min) + min); +} + #define pr_log(ctx, level, prefix, args...) do { \ if ((ctx)->conf == NULL || (level) <= (ctx)->conf->loglevel) { \ fputs(prefix, stderr); fastd_printf(ctx, args); fputs("\n", stderr); \ diff --git a/src/peer.c b/src/peer.c index 653a62f..7fe8c0e 100644 --- a/src/peer.c +++ b/src/peer.c @@ -153,6 +153,8 @@ fastd_peer* fastd_peer_set_established_merge(fastd_context *ctx, fastd_peer *per ctx->conf->protocol->free_peer_state(ctx, perm_peer); + fastd_task_delete_peer_handshakes(ctx, perm_peer); + perm_peer->address = temp_peer->address; perm_peer->state = STATE_ESTABLISHED; perm_peer->seen = temp_peer->seen; @@ -174,24 +176,6 @@ fastd_peer* fastd_peer_set_established_merge(fastd_context *ctx, fastd_peer *per return perm_peer; } -const fastd_eth_addr* fastd_get_source_address(const fastd_context *ctx, fastd_buffer buffer) { - switch (ctx->conf->mode) { - case MODE_TAP: - return (fastd_eth_addr*)&((struct ethhdr*)buffer.data)->h_source; - default: - exit_bug(ctx, "invalid mode"); - } -} - -const fastd_eth_addr* fastd_get_dest_address(const fastd_context *ctx, fastd_buffer buffer) { - switch (ctx->conf->mode) { - case MODE_TAP: - return (fastd_eth_addr*)&((struct ethhdr*)buffer.data)->h_dest; - default: - exit_bug(ctx, "invalid mode"); - } -} - void fastd_peer_set_established(fastd_context *ctx, fastd_peer *peer) { fastd_task_delete_peer_handshakes(ctx, peer); @@ -209,6 +193,23 @@ void fastd_peer_set_established(fastd_context *ctx, fastd_peer *peer) { } } +const fastd_eth_addr* fastd_get_source_address(const fastd_context *ctx, fastd_buffer buffer) { + switch (ctx->conf->mode) { + case MODE_TAP: + return (fastd_eth_addr*)&((struct ethhdr*)buffer.data)->h_source; + default: + exit_bug(ctx, "invalid mode"); + } +} + +const fastd_eth_addr* fastd_get_dest_address(const fastd_context *ctx, fastd_buffer buffer) { + switch (ctx->conf->mode) { + case MODE_TAP: + return (fastd_eth_addr*)&((struct ethhdr*)buffer.data)->h_dest; + default: + exit_bug(ctx, "invalid mode"); + } +} static inline int fastd_eth_addr_cmp(const fastd_eth_addr *addr1, const fastd_eth_addr *addr2) { return memcmp(addr1->data, addr2->data, ETH_ALEN); diff --git a/src/peer.h b/src/peer.h index 9df018e..1ffa18b 100644 --- a/src/peer.h +++ b/src/peer.h @@ -76,6 +76,7 @@ void fastd_peer_reset(fastd_context *ctx, fastd_peer *peer); fastd_peer* fastd_peer_add(fastd_context *ctx, fastd_peer_config *conf); fastd_peer* fastd_peer_add_temp(fastd_context *ctx, const fastd_peer_address *address); fastd_peer* fastd_peer_set_established_merge(fastd_context *ctx, fastd_peer *perm_peer, fastd_peer *temp_peer); +void fastd_peer_set_established(fastd_context *ctx, fastd_peer *peer); const fastd_eth_addr* fastd_get_source_address(const fastd_context *ctx, fastd_buffer buffer); const fastd_eth_addr* fastd_get_dest_address(const fastd_context *ctx, fastd_buffer buffer); @@ -96,8 +97,6 @@ static inline bool fastd_peer_is_established(const fastd_peer *peer) { return (peer->state == STATE_ESTABLISHED); } -void fastd_peer_set_established(fastd_context *ctx, fastd_peer *peer); - static inline void fastd_peer_seen(fastd_context *ctx, fastd_peer *peer) { peer->seen = ctx->now; } diff --git a/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c b/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c index 11edfa2..a7a635c 100644 --- a/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c +++ b/src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c @@ -91,6 +91,8 @@ typedef struct _protocol_handshake { typedef struct _protocol_session { struct timespec valid_till; + struct timespec refresh_after; + bool refreshing; uint8_t key[HASHBYTES]; uint8_t send_nonce[NONCEBYTES]; @@ -151,6 +153,16 @@ static inline bool is_session_valid(fastd_context *ctx, protocol_session *sessio return timespec_after(&session->valid_till, &ctx->now); } +static inline void check_session_refresh(fastd_context *ctx, fastd_peer *peer) { + protocol_session *session = &peer->protocol_state->session; + + if (!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, fastd_rand(ctx, 0, 10000)); + } +} + 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; @@ -341,6 +353,8 @@ static void establish(fastd_context *ctx, fastd_peer *peer, const fastd_peer_con int i; uint8_t hashinput[5*PUBLICKEYBYTES]; + pr_info(ctx, "Session with %P established", peer); + if (is_session_valid(ctx, &peer->protocol_state->session)) peer->protocol_state->old_session = peer->protocol_state->session; @@ -354,6 +368,10 @@ static void establish(fastd_context *ctx, fastd_peer *peer, const fastd_peer_con 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++) { @@ -377,8 +395,12 @@ static void establish(fastd_context *ctx, fastd_peer *peer, const fastd_peer_con } } +static inline bool is_session_initiator(const protocol_session *session) { + return (session->send_nonce[0] & 1); +} + static void finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { - pr_info(ctx, "Finishing protocol handshake with %P...", peer); + pr_info(ctx, "Finishing handshake with %P...", peer); uint8_t hashinput[5*PUBLICKEYBYTES]; uint8_t hashbuf[HASHBYTES]; @@ -449,6 +471,8 @@ static void finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_h } static void handle_finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) { + pr_info(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); @@ -593,10 +617,7 @@ static void protocol_handle_recv(fastd_context *ctx, fastd_peer *peer, fastd_buf goto end; } - if (!is_nonce_valid(buffer.data, peer->protocol_state->session.receive_nonce)) { - pr_debug(ctx, "received packet with invalid nonce from %P", peer); - goto end; - } + check_session_refresh(ctx, peer); uint8_t nonce[crypto_secretbox_xsalsa20poly1305_NONCEBYTES]; memcpy(nonce, buffer.data, NONCEBYTES); @@ -607,10 +628,37 @@ static void protocol_handle_recv(fastd_context *ctx, fastd_peer *peer, fastd_buf fastd_buffer recv_buffer = fastd_buffer_alloc(buffer.len, 0, 0); - if (crypto_secretbox_xsalsa20poly1305_open(recv_buffer.data, buffer.data, buffer.len, nonce, peer->protocol_state->session.key) != 0) { - pr_debug(ctx, "verification failed for packet received from %P", peer); - fastd_buffer_free(recv_buffer); - goto end; + 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); + + goto end; + } + + if (crypto_secretbox_xsalsa20poly1305_open(recv_buffer.data, buffer.data, buffer.len, nonce, session->key) == 0) { + if (is_session_valid(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)); + } + } + else { + pr_debug(ctx, "verification failed for packet received from %P", peer); + fastd_buffer_free(recv_buffer); + goto end; + } } fastd_peer_seen(ctx, peer); @@ -618,7 +666,7 @@ static void protocol_handle_recv(fastd_context *ctx, fastd_peer *peer, fastd_buf fastd_buffer_push_head(&recv_buffer, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); fastd_task_put_handle_recv(ctx, peer, recv_buffer); - memcpy(peer->protocol_state->session.receive_nonce, nonce, NONCEBYTES); + memcpy(session->receive_nonce, nonce, NONCEBYTES); end: fastd_buffer_free(buffer); @@ -630,25 +678,36 @@ static void protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buf 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, peer->protocol_state->session.send_nonce, 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, peer->protocol_state->session.key); + 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, peer->protocol_state->session.send_nonce, NONCEBYTES); + memcpy(send_buffer.data, session->send_nonce, NONCEBYTES); fastd_task_put_send(ctx, peer, send_buffer); - increment_nonce(peer->protocol_state->session.send_nonce); + increment_nonce(session->send_nonce); } static void protocol_free_peer_state(fastd_context *ctx, fastd_peer *peer) { -- cgit v1.2.3