From 3378fbc4c59bbc6110a51eba5d9c3a3bf8a0dba8 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 14 Apr 2019 11:22:00 +0200 Subject: implement handle_validation_response logic --- src/include/gnunet_crypto_lib.h | 12 +- src/transport/gnunet-service-tng.c | 289 ++++++++++++++++++++++++++++--------- 2 files changed, 228 insertions(+), 73 deletions(-) (limited to 'src') diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h index 257fee48d..6822de2f1 100644 --- a/src/include/gnunet_crypto_lib.h +++ b/src/include/gnunet_crypto_lib.h @@ -1955,7 +1955,7 @@ GNUNET_CRYPTO_rsa_public_key_encode (const struct GNUNET_CRYPTO_RsaPublicKey *ke */ struct GNUNET_CRYPTO_RsaPublicKey * GNUNET_CRYPTO_rsa_public_key_decode (const char *buf, - size_t len); + size_t len); /** @@ -1977,7 +1977,7 @@ GNUNET_CRYPTO_rsa_public_key_dup (const struct GNUNET_CRYPTO_RsaPublicKey *key); */ int GNUNET_CRYPTO_rsa_signature_cmp (struct GNUNET_CRYPTO_RsaSignature *s1, - struct GNUNET_CRYPTO_RsaSignature *s2); + struct GNUNET_CRYPTO_RsaSignature *s2); /** * Compare the values of two private keys. @@ -1988,7 +1988,7 @@ GNUNET_CRYPTO_rsa_signature_cmp (struct GNUNET_CRYPTO_RsaSignature *s1, */ int GNUNET_CRYPTO_rsa_private_key_cmp (struct GNUNET_CRYPTO_RsaPrivateKey *p1, - struct GNUNET_CRYPTO_RsaPrivateKey *p2); + struct GNUNET_CRYPTO_RsaPrivateKey *p2); /** @@ -2000,7 +2000,7 @@ GNUNET_CRYPTO_rsa_private_key_cmp (struct GNUNET_CRYPTO_RsaPrivateKey *p1, */ int GNUNET_CRYPTO_rsa_public_key_cmp (struct GNUNET_CRYPTO_RsaPublicKey *p1, - struct GNUNET_CRYPTO_RsaPublicKey *p2); + struct GNUNET_CRYPTO_RsaPublicKey *p2); /** @@ -2065,7 +2065,7 @@ GNUNET_CRYPTO_rsa_signature_free (struct GNUNET_CRYPTO_RsaSignature *sig); */ size_t GNUNET_CRYPTO_rsa_signature_encode (const struct GNUNET_CRYPTO_RsaSignature *sig, - char **buffer); + char **buffer); /** @@ -2078,7 +2078,7 @@ GNUNET_CRYPTO_rsa_signature_encode (const struct GNUNET_CRYPTO_RsaSignature *sig */ struct GNUNET_CRYPTO_RsaSignature * GNUNET_CRYPTO_rsa_signature_decode (const char *buf, - size_t len); + size_t len); /** diff --git a/src/transport/gnunet-service-tng.c b/src/transport/gnunet-service-tng.c index e685e28a6..ecff6b6e1 100644 --- a/src/transport/gnunet-service-tng.c +++ b/src/transport/gnunet-service-tng.c @@ -138,6 +138,10 @@ * When do we forget an invalid address for sure? */ #define MAX_ADDRESS_VALID_UNTIL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MONTHS, 1) +/** + * How long do we consider an address valid if we just checked? + */ +#define ADDRESS_VALIDATION_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) /** * What is the maximum frequency at which we do address validation? @@ -147,6 +151,14 @@ */ #define MIN_DELAY_ADDRESS_VALIDATION GNUNET_TIME_UNIT_MILLISECONDS +/** + * How many network RTTs before an address validation expires should we begin + * trying to revalidate? (Note that the RTT used here is the one that we + * experienced during the last validation, not necessarily the latest RTT + * observed). + */ +#define VALIDATION_RTT_BUFFER_FACTOR 3 + /** * How many messages can we have pending for a given communicator * process before we start to throttle that communicator? @@ -1597,8 +1609,8 @@ struct ValidationState * Next time we will send the @e challenge to the peer, if this time is past * @e valid_until, this validation state is released at this time. If the * address is valid, @e next_challenge is set to @e validated_until MINUS @e - * validation_delay * 3, such that we will try to re-validate before the - * validity actually expires. + * validation_delay * #VALIDATION_RTT_BUFFER_FACTOR, such that we will try + * to re-validate before the validity actually expires. */ struct GNUNET_TIME_Absolute next_challenge; @@ -3051,10 +3063,13 @@ store_pi (void *cls); /** * Function called when peerstore is done storing our address. + * + * @param cls a `struct AddressListEntry` + * @param success #GNUNET_YES if peerstore was successful */ static void -peerstore_store_cb (void *cls, - int success) +peerstore_store_own_cb (void *cls, + int success) { struct AddressListEntry *ale = cls; @@ -3081,29 +3096,20 @@ static void store_pi (void *cls) { struct AddressListEntry *ale = cls; - void *addr; - size_t addr_len; struct GNUNET_TIME_Absolute expiration; ale->st = NULL; expiration = GNUNET_TIME_relative_to_absolute (ale->expiration); - GNUNET_HELLO_sign_address (ale->address, - ale->nt, - expiration, - GST_my_private_key, - &addr, - &addr_len); ale->sc = GNUNET_PEERSTORE_store (peerstore, "transport", &GST_my_identity, GNUNET_HELLO_PEERSTORE_KEY, - addr, - addr_len, + ale->address, + strlen (ale->address) + 1, expiration, GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, - &peerstore_store_cb, + &peerstore_store_own_cb, ale); - GNUNET_free (addr); if (NULL == ale->sc) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -3846,6 +3852,120 @@ handle_validation_challenge (void *cls, } +/** + * Closure for #check_known_challenge. + */ +struct CheckKnownChallengeContext +{ + /** + * Set to the challenge we are looking for. + */ + const struct GNUNET_ShortHashCode *challenge; + + /** + * Set to a matching validation state, if one was found. + */ + struct ValidationState *vs; +}; + + +/** + * Test if the validation state in @a value matches the + * challenge from @a cls. + * + * @param cls a `struct CheckKnownChallengeContext` + * @param pid unused (must match though) + * @param value a `struct ValidationState` + * @return #GNUNET_OK if not matching, #GNUNET_NO if match found + */ +static int +check_known_challenge (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct CheckKnownChallengeContext *ckac = cls; + struct ValidationState *vs = value; + + (void) pid; + if (0 != GNUNET_memcmp (&vs->challenge, + ckac->challenge)) + return GNUNET_OK; + ckac->vs = vs; + return GNUNET_NO; +} + + +/** + * Function called when peerstore is done storing a + * validated address. + * + * @param cls a `struct ValidationState` + * @param success #GNUNET_YES on success + */ +static void +peerstore_store_validation_cb (void *cls, + int success) +{ + struct ValidationState *vs = cls; + + vs->sc = NULL; + if (GNUNET_YES == success) + return; + GNUNET_STATISTICS_update (GST_stats, + "# Peerstore failed to store foreign address", + 1, + GNUNET_NO); +} + + +/** + * Task run periodically to validate some address based on #validation_heap. + * + * @param cls NULL + */ +static void +validation_start_cb (void *cls); + + +/** + * Set the time for next_challenge of @a vs to @a new_time. + * Updates the heap and if necessary reschedules the job. + * + * @param vs validation state to update + * @param new_time new time for revalidation + */ +static void +update_next_challenge_time (struct ValidationState *vs, + struct GNUNET_TIME_Absolute new_time) +{ + struct GNUNET_TIME_Relative delta; + + if (new_time.abs_value_us == vs->next_challenge.abs_value_us) + return; /* be lazy */ + vs->next_challenge = new_time; + if (NULL == vs->hn) + vs->hn = GNUNET_CONTAINER_heap_insert (validation_heap, + vs, + new_time.abs_value_us); + else + GNUNET_CONTAINER_heap_update_cost (vs->hn, + new_time.abs_value_us); + if ( (vs != GNUNET_CONTAINER_heap_peek (validation_heap)) && + (NULL != validation_task) ) + return; + if (NULL != validation_task) + GNUNET_SCHEDULER_cancel (validation_task); + /* randomize a bit */ + delta.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + MIN_DELAY_ADDRESS_VALIDATION.rel_value_us); + new_time = GNUNET_TIME_absolute_add (new_time, + delta); + validation_task = GNUNET_SCHEDULER_add_at (new_time, + &validation_start_cb, + NULL); +} + + /** * Communicator gave us a transport address validation response. Process the request. * @@ -3857,9 +3977,92 @@ handle_validation_response (void *cls, const struct TransportValidationResponse *tvr) { struct CommunicatorMessageContext *cmc = cls; + struct ValidationState *vs; + struct CheckKnownChallengeContext ckac = { + .challenge = &tvr->challenge, + .vs = NULL + }; + struct GNUNET_TIME_Absolute origin_time; - // FIXME: check for matching pending challenge and mark address - // as valid if applicable (passing to PEERSTORE as well!) + /* check this is one of our challenges */ + (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + &cmc->im.sender, + &check_known_challenge, + &ckac); + if (NULL == (vs = ckac.vs)) + { + /* This can happen simply if we 'forgot' the challenge by now, + i.e. because we received the validation response twice */ + GNUNET_STATISTICS_update (GST_stats, + "# Validations dropped, challenge unknown", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + + /* sanity check on origin time */ + origin_time = GNUNET_TIME_absolute_ntoh (tvr->origin_time); + if ( (origin_time.abs_value_us < vs->first_challenge_use.abs_value_us) || + (origin_time.abs_value_us > vs->last_challenge_use.abs_value_us) ) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + + { + /* check signature */ + struct TransportValidationPS tvp = { + .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), + .purpose.size = htonl (sizeof (tvp)), + .validity_duration = tvr->validity_duration, + .challenge = tvr->challenge + }; + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE, + &tvp.purpose, + &tvr->signature, + &cmc->im.sender.public_key)) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + } + + /* validity is capped by our willingness to keep track of the + validation entry and the maximum the other peer allows */ + vs->valid_until + = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_min (GNUNET_TIME_relative_ntoh (tvr->validity_duration), + MAX_ADDRESS_VALID_UNTIL)); + vs->validated_until + = GNUNET_TIME_absolute_min (vs->valid_until, + GNUNET_TIME_relative_to_absolute (ADDRESS_VALIDATION_LIFETIME)); + vs->validation_rtt = GNUNET_TIME_absolute_get_duration (origin_time); + vs->challenge_backoff = GNUNET_TIME_UNIT_ZERO; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &vs->challenge, + sizeof (vs->challenge)); + vs->first_challenge_use = GNUNET_TIME_absolute_subtract (vs->validated_until, + GNUNET_TIME_relative_multiply (vs->validation_rtt, + VALIDATION_RTT_BUFFER_FACTOR)); + vs->last_challenge_use = GNUNET_TIME_UNIT_ZERO_ABS; /* challenge was not yet used */ + update_next_challenge_time (vs, + vs->first_challenge_use); + vs->sc = GNUNET_PEERSTORE_store (peerstore, + "transport", + &cmc->im.sender, + GNUNET_HELLO_PEERSTORE_KEY, + vs->address, + strlen (vs->address) + 1, + vs->valid_until, + GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, + &peerstore_store_validation_cb, + vs); + // FIXME: now that we know that the address is *valid*, + // do we need to trigger _using_ it for something? finish_cmc_handling (cmc); } @@ -4707,54 +4910,6 @@ suggest_to_connect (const struct GNUNET_PeerIdentity *pid, } -/** - * Task run periodically to validate some address based on #validation_heap. - * - * @param cls NULL - */ -static void -validation_start_cb (void *cls); - - -/** - * Set the time for next_challenge of @a vs to @a new_time. - * Updates the heap and if necessary reschedules the job. - * - * @param vs validation state to update - * @param new_time new time for revalidation - */ -static void -update_next_challenge_time (struct ValidationState *vs, - struct GNUNET_TIME_Absolute new_time) -{ - struct GNUNET_TIME_Relative delta; - - if (new_time.abs_value_us == vs->next_challenge.abs_value_us) - return; /* be lazy */ - vs->next_challenge = new_time; - if (NULL == vs->hn) - vs->hn = GNUNET_CONTAINER_heap_insert (validation_heap, - vs, - new_time.abs_value_us); - else - GNUNET_CONTAINER_heap_update_cost (vs->hn, - new_time.abs_value_us); - if ( (vs != GNUNET_CONTAINER_heap_peek (validation_heap)) && - (NULL != validation_task) ) - return; - if (NULL != validation_task) - GNUNET_SCHEDULER_cancel (validation_task); - /* randomize a bit */ - delta.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - MIN_DELAY_ADDRESS_VALIDATION.rel_value_us); - new_time = GNUNET_TIME_absolute_add (new_time, - delta); - validation_task = GNUNET_SCHEDULER_add_at (new_time, - &validation_start_cb, - NULL); -} - - /** * The queue @a q (which matches the peer and address in @a vs) is * ready for queueing. We should now queue the validation request. @@ -5468,7 +5623,7 @@ do_shutdown (void *cls) if (NULL != peerstore) { GNUNET_PEERSTORE_disconnect (peerstore, - GNUNET_NO); + GNUNET_NO); peerstore = NULL; } if (NULL != GST_stats) -- cgit v1.2.3