summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.c3
-rw-r--r--src/fastd.c10
-rw-r--r--src/fastd.h8
-rw-r--r--src/peer.c37
-rw-r--r--src/peer.h3
-rw-r--r--src/protocol_ec25519_fhmqvc_xsalsa20_poly1305.c87
6 files changed, 109 insertions, 39 deletions
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) {