From 8c91443808ce376947ff387eaffca6e8cfbe9251 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 19 Apr 2012 17:42:56 +0200 Subject: 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. --- src/protocol_ec25519_fhmqvc.c | 434 +++++++++++++++++++----------------------- 1 file changed, 198 insertions(+), 236 deletions(-) (limited to 'src/protocol_ec25519_fhmqvc.c') 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, -- cgit v1.2.3