summaryrefslogtreecommitdiffstats
path: root/src/protocol_ec25519_fhmqvc.c
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2012-04-19 17:42:56 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2012-04-19 17:42:56 +0200
commit8c91443808ce376947ff387eaffca6e8cfbe9251 (patch)
tree227a4367a2c013bbee40dc99c8abafc52da5ee2e /src/protocol_ec25519_fhmqvc.c
parentc5b12202c834fe484634131ee9a91465b9e6e7bc (diff)
downloadfastd-8c91443808ce376947ff387eaffca6e8cfbe9251.tar
fastd-8c91443808ce376947ff387eaffca6e8cfbe9251.zip
Don't regenerate session handshake keypair for every handshake so a global state can be used; remove the concept of temporary peers
These changes will fix the possibility of a TCP-SYN-Flood-like DoS attack, at the cost of another protocol change: as we can't count request IDs when we don't know have temporary peers, request IDs are removed completely.
Diffstat (limited to 'src/protocol_ec25519_fhmqvc.c')
-rw-r--r--src/protocol_ec25519_fhmqvc.c434
1 files changed, 198 insertions, 236 deletions
diff --git a/src/protocol_ec25519_fhmqvc.c b/src/protocol_ec25519_fhmqvc.c
index 93fd9c4..e4a0e93 100644
--- a/src/protocol_ec25519_fhmqvc.c
+++ b/src/protocol_ec25519_fhmqvc.c
@@ -58,26 +58,22 @@ struct _fastd_protocol_config {
ecc_public_key_256 public_key;
};
-typedef enum _handshake_state {
- HANDSHAKE_STATE_INIT,
- HANDSHAKE_STATE_RESPONSE,
- HANDSHAKE_STATE_ESTABLISHED
-} handshake_state;
+typedef struct _handshake_key {
+ struct timespec preferred_till;
+ struct timespec valid_till;
-struct _fastd_protocol_peer_config {
+ ecc_secret_key_256 secret_key;
ecc_public_key_256 public_key;
-};
+} handshake_key;
-typedef struct _protocol_handshake {
- const fastd_peer_config *peer_config;
+struct _fastd_protocol_state {
+ handshake_key prev_handshake_key;
+ handshake_key handshake_key;
+};
- handshake_state state;
- ecc_secret_key_256 secret_key;
+struct _fastd_protocol_peer_config {
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;
@@ -89,9 +85,6 @@ typedef struct _protocol_session {
struct _fastd_protocol_peer_state {
protocol_session old_session;
protocol_session session;
-
- protocol_handshake *initiating_handshake;
- protocol_handshake *accepting_handshake;
};
@@ -116,6 +109,14 @@ static inline bool read_key(uint8_t key[32], const char *hexkey) {
return true;
}
+static inline bool is_handshake_key_valid(fastd_context *ctx, const handshake_key *handshake_key) {
+ return timespec_after(&handshake_key->valid_till, &ctx->now);
+}
+
+static inline bool is_handshake_key_preferred(fastd_context *ctx, const handshake_key *handshake_key) {
+ return timespec_after(&handshake_key->preferred_till, &ctx->now);
+}
+
static inline bool is_session_valid(fastd_context *ctx, const protocol_session *session) {
return ctx->conf->method->session_is_valid(ctx, session->method_state);
}
@@ -123,7 +124,7 @@ static inline bool is_session_valid(fastd_context *ctx, const protocol_session *
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)) {
+ if (!session->refreshing && ctx->conf->method->session_is_initiator(ctx, session->method_state) && 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);
@@ -173,89 +174,67 @@ static void init_peer_state(fastd_context *ctx, fastd_peer *peer) {
memset(peer->protocol_state, 0, sizeof(fastd_protocol_peer_state));
}
-static inline void free_handshake(protocol_handshake *handshake) {
+static inline void free_handshake_key(handshake_key *handshake) {
if (handshake) {
- memset(handshake, 0, sizeof(protocol_handshake));
+ memset(handshake, 0, sizeof(handshake_key));
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 void maintenance(fastd_context *ctx) {
+ if (!ctx->protocol_state) {
+ ctx->protocol_state = malloc(sizeof(fastd_protocol_state));
+ memset(ctx->protocol_state, 0, sizeof(fastd_protocol_state));
}
-}
-static protocol_handshake* new_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_peer_config *peer_config, bool initiate) {
- protocol_handshake **handshake;
+ if (!is_handshake_key_preferred(ctx, &ctx->protocol_state->handshake_key)) {
+ pr_debug(ctx, "generating new handshake key");
- if (initiate)
- handshake = &peer->protocol_state->initiating_handshake;
- else
- handshake = &peer->protocol_state->accepting_handshake;
-
- free_handshake(*handshake);
-
- *handshake = malloc(sizeof(protocol_handshake));
+ ctx->protocol_state->prev_handshake_key = ctx->protocol_state->handshake_key;
- (*handshake)->peer_config = peer_config;
+ fastd_random_bytes(ctx, ctx->protocol_state->handshake_key.secret_key.s, 32, false);
+ ecc_25519_secret_sanitize(&ctx->protocol_state->handshake_key.secret_key, &ctx->protocol_state->handshake_key.secret_key);
- (*handshake)->state = HANDSHAKE_STATE_INIT;
+ ecc_25519_work work;
+ ecc_25519_scalarmult_base(&work, &ctx->protocol_state->handshake_key.secret_key);
+ ecc_25519_store(&ctx->protocol_state->handshake_key.public_key, &work);
- fastd_random_bytes(ctx, (*handshake)->secret_key.s, 32, false);
- ecc_25519_secret_sanitize(&(*handshake)->secret_key, &(*handshake)->secret_key);
+ ctx->protocol_state->handshake_key.preferred_till = ctx->now;
+ ctx->protocol_state->handshake_key.preferred_till.tv_sec += 15;
- ecc_25519_work work;
- ecc_25519_scalarmult_base(&work, &(*handshake)->secret_key);
- ecc_25519_store(&(*handshake)->public_key, &work);
-
- return *handshake;
+ ctx->protocol_state->handshake_key.valid_till = ctx->now;
+ ctx->protocol_state->handshake_key.valid_till.tv_sec += 30;
+ }
}
-static void protocol_handshake_init(fastd_context *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 */);
+static void protocol_handshake_init(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf) {
+ maintenance(ctx);
- protocol_handshake *handshake = new_handshake(ctx, peer, peer->config, true);
+ fastd_buffer buffer = fastd_handshake_new_init(ctx, 3*(4+PUBLICKEYBYTES) /* sender key, receipient key, handshake key */);
fastd_handshake_add(ctx, &buffer, RECORD_SENDER_KEY, PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p);
- if (handshake->peer_config)
- fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, handshake->peer_config->protocol_config->public_key.p);
+ if (peer_conf)
+ fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES, peer_conf->protocol_config->public_key.p);
else
- pr_debug(ctx, "sending handshake to unknown peer %P", peer);
+ pr_debug(ctx, "sending handshake to unknown peer %I", address);
- fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake->public_key.p);
+ fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, ctx->protocol_state->handshake_key.public_key.p);
- fastd_send_handshake(ctx, peer, buffer);
+ fastd_send_handshake(ctx, address, buffer);
}
-static void respond_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) {
- pr_debug(ctx, "responding handshake with %P...", peer);
+static void respond_handshake(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key, const fastd_handshake *handshake) {
+ pr_debug(ctx, "responding handshake with %I...", address);
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, handshake_key->public_key.p, PUBLICKEYBYTES);
+ memcpy(hashinput+PUBLICKEYBYTES, peer_handshake_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);
+ memcpy(hashinput+3*PUBLICKEYBYTES, peer_conf->protocol_config->public_key.p, PUBLICKEYBYTES);
crypto_hash_sha256(hashbuf, hashinput, 4*PUBLICKEYBYTES);
@@ -268,11 +247,11 @@ static void respond_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_
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_secret_add(&s, &eb, &handshake_key->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_load(&work, &peer_conf->protocol_config->public_key);
+ ecc_25519_load(&workX, peer_handshake_key);
ecc_25519_scalarmult(&work, &d, &work);
ecc_25519_add(&work, &workX, &work);
@@ -281,36 +260,46 @@ static void respond_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_
if (ecc_25519_is_identity(&work))
return;
- ecc_25519_store(&peer->protocol_state->accepting_handshake->sigma, &work);
+ ecc_public_key_256 sigma;
+ ecc_25519_store(&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);
+ uint8_t shared_handshake_key[HASHBYTES];
+ memcpy(hashinput+4*PUBLICKEYBYTES, sigma.p, PUBLICKEYBYTES);
+ crypto_hash_sha256(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);
+ memcpy(hashinput+PUBLICKEYBYTES, handshake_key->public_key.p, PUBLICKEYBYTES);
- crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, peer->protocol_state->accepting_handshake->shared_handshake_key);
+ crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, shared_handshake_key);
- fastd_buffer buffer = fastd_handshake_new_reply(ctx, peer, handshake, 4*(4+PUBLICKEYBYTES) + 4+HMACBYTES);
+ fastd_buffer buffer = fastd_handshake_new_reply(ctx, 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_RECEIPIENT_KEY, PUBLICKEYBYTES, peer_conf->protocol_config->public_key.p);
+ fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake_key->public_key.p);
+ fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p);
fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf);
- fastd_send_handshake(ctx, peer, buffer);
-
- peer->protocol_state->accepting_handshake->state = HANDSHAKE_STATE_RESPONSE;
+ fastd_send_handshake(ctx, address, buffer);
}
-static void establish(fastd_context *ctx, fastd_peer *peer, const fastd_peer_config *peer_config, bool initiator,
+static void establish(fastd_context *ctx, const fastd_peer_config *peer_conf, const fastd_peer_address *address, 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);
+ fastd_peer *peer;
+ for (peer = ctx->peers; peer; peer = peer->next) {
+ if (peer->config == peer_conf)
+ break;
+ }
+ if (!peer)
+ exit_bug(ctx, "no peer for config found");
+
+ pr_verbose(ctx, "%I authorized as %P", address, peer);
+
+ init_peer_state(ctx, 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);
@@ -331,43 +320,34 @@ static void establish(fastd_context *ctx, fastd_peer *peer, const fastd_peer_con
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);
+ if (!fastd_peer_claim_address(ctx, peer, address)) {
+ pr_warn(ctx, "can't set address %I which is used by a fixed peer", ctx->resolve_returns->addr);
+ fastd_peer_reset(ctx, peer);
+ return;
}
+ fastd_peer_set_established(ctx, peer);
+
+ pr_verbose(ctx, "new session with %P established.", 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);
+static void finish_handshake(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key, const fastd_handshake *handshake) {
+ pr_debug(ctx, "finishing handshake with %I...", address);
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, peer_handshake_key->p, PUBLICKEYBYTES);
+ memcpy(hashinput+PUBLICKEYBYTES, handshake_key->public_key.p, PUBLICKEYBYTES);
+ memcpy(hashinput+2*PUBLICKEYBYTES, peer_conf->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);
@@ -381,11 +361,11 @@ static void finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_h
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_secret_add(&s, &da, &handshake_key->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_load(&work, &peer_conf->protocol_config->public_key);
+ ecc_25519_load(&workY, peer_handshake_key);
ecc_25519_scalarmult(&work, &e, &work);
ecc_25519_add(&work, &workY, &work);
@@ -394,72 +374,103 @@ static void finish_handshake(fastd_context *ctx, fastd_peer *peer, const fastd_h
if (ecc_25519_is_identity(&work))
return;
- ecc_25519_store(&peer->protocol_state->initiating_handshake->sigma, &work);
+ ecc_public_key_256 sigma;
+ ecc_25519_store(&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);
+ uint8_t shared_handshake_key[HASHBYTES];
+ memcpy(hashinput+4*PUBLICKEYBYTES, sigma.p, PUBLICKEYBYTES);
+ crypto_hash_sha256(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);
+ memcpy(hashinput, peer_conf->protocol_config->public_key.p, PUBLICKEYBYTES);
+ memcpy(hashinput+PUBLICKEYBYTES, peer_handshake_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);
+ if(crypto_auth_hmacsha256_verify(handshake->records[RECORD_T].data, hashinput, 2*PUBLICKEYBYTES, shared_handshake_key) != 0) {
+ pr_warn(ctx, "received invalid protocol handshake response from %I", address);
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);
+ memcpy(hashinput+PUBLICKEYBYTES, handshake_key->public_key.p, PUBLICKEYBYTES);
+ crypto_auth_hmacsha256(hmacbuf, hashinput, 2*PUBLICKEYBYTES, shared_handshake_key);
- fastd_buffer buffer = fastd_handshake_new_reply(ctx, peer, handshake, 4*(4+PUBLICKEYBYTES) + 4+HMACBYTES);
+ fastd_buffer buffer = fastd_handshake_new_reply(ctx, 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_RECEIPIENT_KEY, PUBLICKEYBYTES, peer_conf->protocol_config->public_key.p);
+ fastd_handshake_add(ctx, &buffer, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES, handshake_key->public_key.p);
+ fastd_handshake_add(ctx, &buffer, RECORD_RECEIPIENT_HANDSHAKE_KEY, PUBLICKEYBYTES, peer_handshake_key->p);
fastd_handshake_add(ctx, &buffer, RECORD_T, HMACBYTES, hmacbuf);
- fastd_send_handshake(ctx, peer, buffer);
+ fastd_send_handshake(ctx, address, 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);
+ establish(ctx, peer_conf, address, true, &handshake_key->public_key, peer_handshake_key, &ctx->conf->protocol_config->public_key,
+ &peer_conf->protocol_config->public_key, &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);
+static void handle_finish_handshake(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const handshake_key *handshake_key, const ecc_public_key_256 *peer_handshake_key, const fastd_handshake *handshake) {
+ pr_debug(ctx, "handling handshake finish with %I...", address);
+
+ uint8_t hashinput[5*PUBLICKEYBYTES];
+ uint8_t hashbuf[HASHBYTES];
+
+ memcpy(hashinput, handshake_key->public_key.p, PUBLICKEYBYTES);
+ memcpy(hashinput+PUBLICKEYBYTES, peer_handshake_key->p, PUBLICKEYBYTES);
+ memcpy(hashinput+2*PUBLICKEYBYTES, ctx->conf->protocol_config->public_key.p, PUBLICKEYBYTES);
+ memcpy(hashinput+3*PUBLICKEYBYTES, peer_conf->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;
- uint8_t hashinput[2*PUBLICKEYBYTES];
+ ecc_25519_secret_mult(&eb, &e, &ctx->conf->protocol_config->secret_key);
+ ecc_25519_secret_add(&s, &eb, &handshake_key->secret_key);
- 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);
+ ecc_25519_work work, workX;
+ ecc_25519_load(&work, &peer_conf->protocol_config->public_key);
+ ecc_25519_load(&workX, peer_handshake_key);
- 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);
+ 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_public_key_256 sigma;
+ ecc_25519_store(&sigma, &work);
+
+ uint8_t shared_handshake_key[HASHBYTES];
+ memcpy(hashinput+4*PUBLICKEYBYTES, sigma.p, PUBLICKEYBYTES);
+ crypto_hash_sha256(shared_handshake_key, hashinput, 5*PUBLICKEYBYTES);
+
+ memcpy(hashinput, peer_conf->protocol_config->public_key.p, PUBLICKEYBYTES);
+ memcpy(hashinput+PUBLICKEYBYTES, peer_handshake_key->p, PUBLICKEYBYTES);
+
+ if(crypto_auth_hmacsha256_verify(handshake->records[RECORD_T].data, hashinput, 2*PUBLICKEYBYTES, shared_handshake_key) != 0) {
+ pr_warn(ctx, "received invalid protocol handshake finish from %I", address);
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);
+ establish(ctx, peer_conf, address, false, peer_handshake_key, &handshake_key->public_key, &peer_conf->protocol_config->public_key,
+ &ctx->conf->protocol_config->public_key, &sigma);
}
-static inline const fastd_peer_config* match_sender_key(fastd_context *ctx, 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;
+static const fastd_peer_config* match_sender_key(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const unsigned char key[32]) {
+ if (peer_conf) {
+ if (memcmp(peer_conf->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0)
+ return peer_conf;
}
- if (fastd_peer_is_temporary(peer) || fastd_peer_is_floating(peer) || fastd_peer_is_dynamic(peer)) {
+ if (!peer_conf || fastd_peer_config_is_floating(peer_conf) || fastd_peer_config_is_dynamic(peer_conf)) {
fastd_peer_config *config;
for (config = ctx->conf->peers; config; config = config->next) {
- if (!fastd_peer_config_is_floating(config) && !fastd_peer_config_matches_dynamic(config, &peer->address))
+ if (!fastd_peer_config_is_floating(config) && !fastd_peer_config_matches_dynamic(config, address))
continue;
if (memcmp(config->protocol_config->public_key.p, key, PUBLICKEYBYTES) == 0) {
@@ -477,129 +488,89 @@ static inline const fastd_peer_config* match_sender_key(fastd_context *ctx, fast
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 inline bool has_field(const fastd_handshake *handshake, uint8_t type, size_t length) {
return (handshake->records[type].length == length);
}
-static void protocol_handshake_handle(fastd_context *ctx, fastd_peer *peer, const fastd_handshake *handshake) {
- init_peer_state(ctx, peer);
+static void protocol_handshake_handle(fastd_context *ctx, const fastd_peer_address *address, const fastd_peer_config *peer_conf, const fastd_handshake *handshake) {
+ handshake_key *handshake_key;
+
+ maintenance(ctx);
if (!has_field(handshake, RECORD_SENDER_KEY, PUBLICKEYBYTES)) {
- pr_debug(ctx, "received handshake without sender key from %P", peer);
+ pr_debug(ctx, "received handshake without sender key from %I", address);
return;
}
- const fastd_peer_config *peer_config = match_sender_key(ctx, peer, handshake->records[RECORD_SENDER_KEY].data);
- if (!peer_config) {
- pr_debug(ctx, "ignoring handshake from %P (unknown key or unresolved host)", peer);
+ peer_conf = match_sender_key(ctx, address, peer_conf, handshake->records[RECORD_SENDER_KEY].data);
+ if (!peer_conf) {
+ pr_debug(ctx, "ignoring handshake from %I (unknown key or unresolved host)", address);
return;
}
if (handshake->type > 1 && !has_field(handshake, RECORD_RECEIPIENT_KEY, PUBLICKEYBYTES)) {
- pr_debug(ctx, "received handshake reply without receipient key from %P", peer);
+ pr_debug(ctx, "received handshake reply without receipient key from %I", address);
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);
+ pr_debug(ctx, "received protocol handshake with wrong receipient key from %I", address);
return;
}
}
if (!has_field(handshake, RECORD_SENDER_HANDSHAKE_KEY, PUBLICKEYBYTES)) {
- pr_debug(ctx, "received handshake without sender handshake key from %P", peer);
+ pr_debug(ctx, "received handshake without sender handshake key from %I", address);
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);
+ pr_debug(ctx, "received handshake reply without receipient handshake key from %I", address);
return;
}
if (handshake->type > 1 && !has_field(handshake, RECORD_T, HMACBYTES)) {
- pr_debug(ctx, "received handshake reply without HMAC from %P", peer);
+ pr_debug(ctx, "received handshake reply without HMAC from %I", address);
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);
+ respond_handshake(ctx, address, peer_conf, &ctx->protocol_state->handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, 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 (is_handshake_key_valid(ctx, &ctx->protocol_state->handshake_key) && memcmp(ctx->protocol_state->handshake_key.public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) {
+ handshake_key = &ctx->protocol_state->handshake_key;
}
-
- 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;
+ else if (is_handshake_key_valid(ctx, &ctx->protocol_state->prev_handshake_key) && memcmp(ctx->protocol_state->prev_handshake_key.public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) {
+ handshake_key = &ctx->protocol_state->prev_handshake_key;
}
-
- pr_debug(ctx, "received handshake response from %P", peer);
-
- if (peer->protocol_state->accepting_handshake) {
- kill_handshakes(ctx, peer);
+ else {
+ pr_debug(ctx, "received handshake response with unexpected receipient handshake key from %I", address);
return;
}
- memcpy(peer->protocol_state->initiating_handshake->peer_key.p, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, PUBLICKEYBYTES);
+ pr_debug(ctx, "received handshake response from %I", address);
- finish_handshake(ctx, peer, handshake);
+ finish_handshake(ctx, address, peer_conf, handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, 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 (is_handshake_key_valid(ctx, &ctx->protocol_state->handshake_key) && memcmp(ctx->protocol_state->handshake_key.public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) {
+ handshake_key = &ctx->protocol_state->handshake_key;
}
-
- 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;
+ else if (is_handshake_key_valid(ctx, &ctx->protocol_state->prev_handshake_key) && memcmp(ctx->protocol_state->prev_handshake_key.public_key.p, handshake->records[RECORD_RECEIPIENT_HANDSHAKE_KEY].data, PUBLICKEYBYTES) == 0) {
+ handshake_key = &ctx->protocol_state->prev_handshake_key;
}
-
- 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);
+ else {
+ pr_debug(ctx, "received handshake response with unexpected receipient handshake key from %I", address);
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 %I", address);
- 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);
+ handle_finish_handshake(ctx, address, peer_conf, handshake_key, handshake->records[RECORD_SENDER_HANDSHAKE_KEY].data, handshake);
break;
default:
@@ -609,12 +580,8 @@ static void protocol_handshake_handle(fastd_context *ctx, fastd_peer *peer, cons
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);
- }
+ pr_debug(ctx, "received unexpected packet from %P, scheduling handshake", peer);
+ fastd_task_schedule_handshake(ctx, peer, 0);
goto fail;
}
@@ -690,7 +657,7 @@ static void protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buf
if (!ctx->conf->method->encrypt(ctx, session->method_state, &send_buffer, buffer))
goto fail;
- fastd_send(ctx, peer, send_buffer);
+ fastd_send(ctx, &peer->address, send_buffer);
fastd_task_delete_peer_keepalives(ctx, peer);
fastd_task_schedule_keepalive(ctx, peer, ctx->conf->keepalive_interval*1000);
@@ -702,9 +669,6 @@ static void protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buf
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);
@@ -712,7 +676,6 @@ static void protocol_free_peer_state(fastd_context *ctx, fastd_peer *peer) {
}
}
-
static void hexdump(const char *desc, unsigned char d[32]) {
printf("%s", desc);
@@ -746,7 +709,6 @@ const fastd_protocol fastd_protocol_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,